refactor(slices): этап 3.0A — панадаптер выделен в TPanafallPanel

PanafallPanel.pas: view (CPU/GL), контролы стека (спектр/линейка/сплиттер/
водопад/пан-зум/кнопки зума), геометрия Layout, drag сплиттера и мышь
пан/зум-полосы. MainForm работает через алиасы (FSpecView/PbSpectrum/… =
FPan.*), размеры областей — FPan.SpectrumWidth/SpectrumHeight/WaterfallHeight.
Семантика мыши/флаги/тик пока в MainForm (фаза B). GL-аудит per-instance
пройден (глобальных GL-ресурсов нет). План: doc/SLICES_PLAN.md.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-10 22:41:32 +03:00
co-authored by Claude Fable 5
parent cdbdfec792
commit c125e5978e
3 changed files with 905 additions and 608 deletions
+71 -393
View File
@@ -33,7 +33,7 @@ uses
WebServer, WebAdapter,
CATAdapter,
SpectrumView, SpectrumViewOpengl,
PanZoomBar,
PanZoomBar, PanafallPanel,
WidebandView,
StatusBar,
PlatformUtils,
@@ -156,12 +156,14 @@ type
// живут в TRadioController.
// ---- Spectrum / Waterfall view ----
// Панадаптер (спектр+линейка+сплиттер+водопад+пан/зум) — composite-объект
// (Этап 3.0 мультислайсов). FSpecView и Pb*-поля ниже — алиасы его членов
// (исторические имена; постепенная миграция на FPan.*). Размеры областей —
// FPan.SpectrumWidth/SpectrumHeight/WaterfallHeight.
FPan: TPanafallPanel;
FSpecView: TSpectrumView;
FWidebandView: TWidebandView;
FUseOpenGLSpectrum: Boolean;
FSpectrumWidth: Integer; // актуальная ширина для FDSPEngine и resize-детектора
FSpectrumHeight: Integer;
FWaterfallHeight:Integer;
FDisplayFPS: Integer;
FWaterfallDirty: Boolean;
FWidebandDirty: Boolean;
@@ -170,11 +172,6 @@ type
// Снимок последнего кадра спектра/водопада для web переехал в TRadioController
// (FSpectrumBuf/FWaterfallBuf) — нужен headless-демону.
FAgcLineCounter: Integer;
// ---- Splitter ----
FSplitterDrag: Boolean;
FSplitterDragY0: Integer;
FSplitterSH0: Integer;
FSplitterRatio: Double;
// --- Settings ---
FWebServer: TWebServer;
FWebAdapter: TWebAdapter; // мост web-сервер ↔ контроллер (вынесенные WebOnXxx)
@@ -301,6 +298,7 @@ type
// ---- Right panel ----
PanelRight: TPanel;
// Контролы панадаптера — алиасы членов FPan (владелец FPan; см. выше).
PbSpectrum: TControl;
PbWideband: TControl;
PbWidebandRuler: TPaintBox;
@@ -454,18 +452,11 @@ type
procedure DoSpectrumClick(PixelX: Integer; PanelWidth: Integer);
procedure DoSpectrumDrag(PixelX: Integer; PanelWidth: Integer);
procedure DoWidebandClick(PixelX: Integer; PanelWidth: Integer);
// ---- Пан/зум ----
procedure PbPanZoomMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
procedure PbPanZoomMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
procedure PbPanZoomMouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
procedure PbPanZoomDblClick(Sender: TObject);
// ---- Пан/зум (мышь полосы/сплиттера — внутри TPanafallPanel) ----
procedure OnPanZoomWindow(AZoom, APan: Double);
procedure BtnZoomInClick(Sender: TObject);
procedure BtnZoomDefClick(Sender: TObject);
procedure BtnZoomOutClick(Sender: TObject);
procedure PositionPanZoomBar(BottomY, RW, H: Integer; AVisible: Boolean);
procedure UpdatePanZoomBar;
procedure BtnVfoSwapClick(Sender: TObject);
procedure BtnVfoACopyBClick(Sender: TObject);
@@ -577,13 +568,6 @@ type
procedure SpectrumTimerTick(Sender: TObject);
procedure RightPanelResize(Sender: TObject);
procedure AfterShowTick(Sender: TObject);
// Splitter handlers
procedure SplitterMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
procedure SplitterMouseMove(Sender: TObject; Shift: TShiftState;
X, Y: Integer);
procedure SplitterMouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
public
// Методы для SettingsForm — немедленное применение настроек
@@ -1008,16 +992,15 @@ begin
FController.FSpectrumFill := True;
FDisplayFPS := 60;
FFreqMhzDigits := 3;
FSplitterRatio := 0.40;
FSplitterDrag := False;
// OpenGLContextEx (QOpenGLWidget-бэкенд) работает и в нативном Wayland,
// старая X11-заглушка больше не нужна
FUseOpenGLSpectrum := HasOpenGLSpectrumSwitch;
if FUseOpenGLSpectrum then
FSpecView := TSpectrumViewOpenGL.Create
else
FSpecView := TSpectrumView.Create;
// Панадаптер: владеет view/зум-баром/контролами спектра; контролы создаст
// FPan.Build в BuildUI. Owner=nil — освобождаем сами в FormDestroy.
FPan := TPanafallPanel.Create(nil, FUseOpenGLSpectrum);
FSpecView := FPan.View; // алиас (историческое имя)
FPanZoomBar := FPan.ZoomBar; // алиас
FSpecView.VfoOverlay := FVfoOverlay;
FSpecView.VfoA := FController.FVfoA;
FSpecView.VfoB := FController.FVfoB;
@@ -1114,9 +1097,16 @@ begin
if FController.FNetwork.Running then
FController.FNetwork.SetRunAndFreq(False, XvtrTranslate(FController.FCenterFreq), XvtrTranslate(FController.FCenterFreq), 0);
FController.FNetwork.Disconnect;
FreeAndNil(FSpecView);
// Панадаптер освобождает view, зум-бар и свои контролы; алиасы обнуляем.
FSpecView := nil;
FPanZoomBar := nil;
PbSpectrum := nil;
PbRuler := nil;
PanelSplitter := nil;
PbWaterfall := nil;
PbPanZoom := nil;
FreeAndNil(FPan);
FreeAndNil(FWidebandView);
FreeAndNil(FPanZoomBar);
// Сохраняем настройки при закрытии
if FController.FDevConnected then
@@ -1416,7 +1406,7 @@ begin
// ChangeSampleRate пересоздал WDSP-канал — пересчёт AGC-линий под
// актуальную ширину спектра (UI-зависимая величина).
if FController.FWDSPReady then
FController.FDSPEngine.UpdateAGCLines(FSpectrumWidth);
FController.FDSPEngine.UpdateAGCLines(FPan.SpectrumWidth);
UpdatePanZoomBar;
end;
rfZoom:
@@ -2215,190 +2205,42 @@ begin
PbWidebandRuler.Cursor := crDefault;
PbWidebandRuler.Visible := False;
if FUseOpenGLSpectrum then
begin
PbSpectrum := TOpenGLControl.Create(Self);
TOpenGLControl(PbSpectrum).AutoResizeViewport := False;
TOpenGLControl(PbSpectrum).DoubleBuffered := True;
TOpenGLControl(PbSpectrum).MultiSampling := Max(1, FController.FSpecMSAA);
TOpenGLControl(PbSpectrum).OnPaint := FSpecView.PaintSpectrum;
TOpenGLControl(PbSpectrum).OnMouseDown := PbSpectrumMouseDown;
TOpenGLControl(PbSpectrum).OnDblClick := PbSpectrumDblClick;
TOpenGLControl(PbSpectrum).OnMouseMove := PbSpectrumMouseMove;
TOpenGLControl(PbSpectrum).OnMouseUp := PbSpectrumMouseUp;
TOpenGLControl(PbSpectrum).OnMouseLeave := PbSpectrumMouseLeave;
TSpectrumViewOpenGL(FSpecView).AttachControl(TOpenGLControl(PbSpectrum));
end
else
begin
PbSpectrum := TPaintBox.Create(Self);
TPaintBox(PbSpectrum).OnPaint := FSpecView.PaintSpectrum;
TPaintBox(PbSpectrum).OnMouseDown := PbSpectrumMouseDown;
TPaintBox(PbSpectrum).OnDblClick := PbSpectrumDblClick;
TPaintBox(PbSpectrum).OnMouseMove := PbSpectrumMouseMove;
TPaintBox(PbSpectrum).OnMouseUp := PbSpectrumMouseUp;
TPaintBox(PbSpectrum).OnMouseLeave := PbSpectrumMouseLeave;
end;
PbSpectrum.Parent := PanelRight;
PbRuler := TPaintBox.Create(Self);
PbRuler.Parent := PanelRight;
PbRuler.OnPaint := FSpecView.PaintRuler;
PbRuler.Cursor := crDefault;
FSpecView.PbRuler := PbRuler;
// ---- Splitter между спектром+линейкой и водопадом ----
PanelSplitter := TPanel.Create(Self);
PanelSplitter.Parent := PanelRight;
PanelSplitter.BevelOuter := bvNone;
PanelSplitter.Color := TColor($00303030);
PanelSplitter.Cursor := crVSplit;
PanelSplitter.Height := 5;
PanelSplitter.OnMouseDown := SplitterMouseDown;
PanelSplitter.OnMouseMove := SplitterMouseMove;
PanelSplitter.OnMouseUp := SplitterMouseUp;
if FUseOpenGLSpectrum then
begin
PbWaterfall := TOpenGLControl.Create(Self);
TOpenGLControl(PbWaterfall).AutoResizeViewport := False;
TOpenGLControl(PbWaterfall).DoubleBuffered := True;
TOpenGLControl(PbWaterfall).OnPaint := FSpecView.PaintWaterfall;
TOpenGLControl(PbWaterfall).OnMouseDown := PbWaterfallMouseDown;
TOpenGLControl(PbWaterfall).OnMouseMove := PbWaterfallMouseMove;
TOpenGLControl(PbWaterfall).OnMouseUp := PbWaterfallMouseUp;
TSpectrumViewOpenGL(FSpecView).AttachWaterfallControl(TOpenGLControl(PbWaterfall));
end
else
begin
PbWaterfall := TPaintBox.Create(Self);
TPaintBox(PbWaterfall).OnPaint := FSpecView.PaintWaterfall;
TPaintBox(PbWaterfall).OnMouseDown := PbWaterfallMouseDown;
TPaintBox(PbWaterfall).OnMouseMove := PbWaterfallMouseMove;
TPaintBox(PbWaterfall).OnMouseUp := PbWaterfallMouseUp;
end;
PbWaterfall.Parent := PanelRight;
// ---- Панель пана/зума под водопадом ----
// Кнопки создаём с дефолтными цветами; финальная тема — в ApplyDarkTheme.
FPanZoomBar := TPanZoomBar.Create;
// ---- Панадаптер: контролы спектра/линейки/сплиттера/водопада/пан-зума ----
// Создаёт и раскладывает TPanafallPanel; семантика мыши остаётся здесь —
// обработчики подвешиваются ДО Build.
FPan.SpectrumMouseDown := PbSpectrumMouseDown;
FPan.SpectrumMouseMove := PbSpectrumMouseMove;
FPan.SpectrumMouseUp := PbSpectrumMouseUp;
FPan.SpectrumMouseLeave := PbSpectrumMouseLeave;
FPan.SpectrumDblClick := PbSpectrumDblClick;
FPan.WaterfallMouseDown := PbWaterfallMouseDown;
FPan.WaterfallMouseMove := PbWaterfallMouseMove;
FPan.WaterfallMouseUp := PbWaterfallMouseUp;
FPan.ZoomInClick := BtnZoomInClick;
FPan.ZoomDefClick := BtnZoomDefClick;
FPan.ZoomOutClick := BtnZoomOutClick;
FPan.OnSplitterMoved := RightPanelResize;
FPan.Build(PanelRight, Max(1, FController.FSpecMSAA));
// Алиасы контролов (исторические имена; постепенная миграция на FPan.*)
PbSpectrum := FPan.PbSpectrum;
PbRuler := FPan.PbRuler;
PanelSplitter := FPan.Splitter;
PbWaterfall := FPan.PbWaterfall;
PbPanZoom := FPan.PbPanZoom;
BtnZoomIn := FPan.BtnZoomIn;
BtnZoomDef := FPan.BtnZoomDef;
BtnZoomOut := FPan.BtnZoomOut;
FPanZoomBar.OnZoomPan := OnPanZoomWindow;
PbPanZoom := TPaintBox.Create(Self);
PbPanZoom.Parent := PanelRight;
PbPanZoom.OnPaint := FPanZoomBar.Paint;
PbPanZoom.OnMouseDown := PbPanZoomMouseDown;
PbPanZoom.OnMouseMove := PbPanZoomMouseMove;
PbPanZoom.OnMouseUp := PbPanZoomMouseUp;
PbPanZoom.OnDblClick := PbPanZoomDblClick;
FPanZoomBar.PaintBox := PbPanZoom;
BtnZoomOut := MakeFlatBtn(PanelRight, '', 0, 0, 10, 10, BtnZoomOutClick);
BtnZoomDef := MakeFlatBtn(PanelRight, '⌂', 0, 0, 10, 10, BtnZoomDefClick);
BtnZoomIn := MakeFlatBtn(PanelRight, '+', 0, 0, 10, 10, BtnZoomInClick);
// Initial layout
ResizeSpectrumPanels;
UpdatePanZoomBar;
end;
// ===========================================================================
// Splitter — перетаскивание границы спектр/водопад
// Обновление размеров происходит только при отпускании мыши (MouseUp),
// чтобы исключить фризы во время перетаскивания.
// Во время перетаскивания рисуем только призрак-линию на PanelSplitter.
// ===========================================================================
procedure TMainForm.SplitterMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
var
P: TPoint;
begin
if Button <> mbLeft then Exit;
FSplitterDrag := True;
FSplitterSH0 := FSpectrumHeight;
// Y в координатах PanelRight
P := PanelRight.ScreenToClient(PanelSplitter.ClientToScreen(Point(X, Y)));
FSplitterDragY0 := P.Y;
PanelSplitter.Color := TColor($005050A0); // подсветка при захвате
{$IFDEF WINDOWS}
SetCapture(PanelSplitter.Handle);
{$ENDIF}
end;
procedure TMainForm.SplitterMouseMove(Sender: TObject; Shift: TShiftState;
X, Y: Integer);
var
P: TPoint;
DeltaY: Integer;
NewSH: Integer;
TopOff, AvailH: Integer;
RULER_H: Integer;
const
SPLITTER_H = 5;
MIN_SH = 60;
MIN_WH = 40;
begin
RULER_H := MulDiv(18, Screen.PixelsPerInch, 96);
if not FSplitterDrag then Exit;
P := PanelRight.ScreenToClient(PanelSplitter.ClientToScreen(Point(X, Y)));
DeltaY := P.Y - FSplitterDragY0;
TopOff := 0;
// Низ зарезервирован под панель пана/зума (она видна, пока виден водопад).
AvailH := PanelRight.ClientHeight - TopOff - RULER_H - SPLITTER_H
- MulDiv(22, Screen.PixelsPerInch, 96);
if AvailH <= 0 then Exit;
NewSH := FSplitterSH0 + DeltaY;
if NewSH < MIN_SH then NewSH := MIN_SH;
if NewSH > AvailH - MIN_WH then NewSH := AvailH - MIN_WH;
// Только двигаем сплиттер визуально — без пересчёта битмапов
PanelSplitter.Top := TopOff + NewSH + RULER_H;
PbWaterfall.Top := TopOff + NewSH + RULER_H + SPLITTER_H;
PbWaterfall.Height := AvailH - NewSH;
end;
procedure TMainForm.SplitterMouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
var
P: TPoint;
DeltaY: Integer;
NewSH: Integer;
TopOff, AvailH: Integer;
RULER_H: Integer;
const
SPLITTER_H = 5;
MIN_SH = 60;
MIN_WH = 40;
begin
RULER_H := MulDiv(18, Screen.PixelsPerInch, 96);
if not FSplitterDrag then Exit;
FSplitterDrag := False;
{$IFDEF WINDOWS}
ReleaseCapture;
{$ENDIF}
PanelSplitter.Color := TColor($00303030); // обычный цвет
P := PanelRight.ScreenToClient(PanelSplitter.ClientToScreen(Point(X, Y)));
DeltaY := P.Y - FSplitterDragY0;
TopOff := 0;
// Низ зарезервирован под панель пана/зума (она видна, пока виден водопад).
AvailH := PanelRight.ClientHeight - TopOff - RULER_H - SPLITTER_H
- MulDiv(22, Screen.PixelsPerInch, 96);
if AvailH <= 0 then Exit;
NewSH := FSplitterSH0 + DeltaY;
if NewSH < MIN_SH then NewSH := MIN_SH;
if NewSH > AvailH - MIN_WH then NewSH := AvailH - MIN_WH;
// Сохраняем новое соотношение и делаем полный пересчёт с перерисовкой
FSplitterRatio := NewSH / AvailH;
ResizeSpectrumPanels;
end;
// ===========================================================================
// Resize handler (normal method instead of anonymous procedure)
// Сплиттер спектр/водопад живёт в TPanafallPanel; по OnSplitterMoved панель
// зовёт RightPanelResize → полный пересчёт.
// ===========================================================================
procedure TMainForm.RightPanelResize(Sender: TObject);
@@ -2581,27 +2423,22 @@ end;
procedure TMainForm.ResizeSpectrumPanels;
const
SPLITTER_H = 5;
WB_H = 118;
MIN_SH = 60; // минимальная высота спектра
MIN_WH = 40; // минимальная высота водопада
WB_H = 118;
var
RW, RH, SH, WH, TopOff, WideH, WideSpecH: Integer;
AvailH: Integer;
RW, RH, WideH, WideSpecH: Integer;
RULER_H: Integer;
PANZOOM_H: Integer;
ShowPZ: Boolean;
begin
RULER_H := MulDiv(18, Screen.PixelsPerInch, 96);
if PanelRight = nil then Exit;
if Assigned(FSpecView) then SyncSpecViewFreq;
RW := PanelRight.ClientWidth;
RH := PanelRight.ClientHeight;
// Резервируем низ под панель пана/зума (видна, если виден спектр или водопад).
// Низ занят панелью пана/зума (позиционирует FPan.Layout той же формулой) —
// wideband-блок делит оставшуюся высоту.
PANZOOM_H := MulDiv(22, Screen.PixelsPerInch, 96);
ShowPZ := FController.FShowSpectrum or FController.FShowWaterfall;
PositionPanZoomBar(RH - PANZOOM_H, RW, PANZOOM_H, ShowPZ);
if ShowPZ then RH := RH - PANZOOM_H;
if FController.FShowSpectrum or FController.FShowWaterfall then
RH := RH - PANZOOM_H;
UpdateWidebandFrequencyView;
WideH := 0;
WideSpecH := 0;
@@ -2609,7 +2446,6 @@ begin
WideH := Min(MulDiv(WB_H, Screen.PixelsPerInch, 96), Max(40, RH div 2));
if WideH > 0 then
WideSpecH := Max(1, WideH - RULER_H);
TopOff := WideH;
if PbWideband <> nil then
begin
PbWideband.SetBounds(0, 0, RW, WideSpecH);
@@ -2633,124 +2469,9 @@ begin
// S-метр позиционируется отдельно в ResizeSMeter
ResizeSMeter;
// ---- Оба скрыты ----
if (not FController.FShowSpectrum) and (not FController.FShowWaterfall) then
begin
PbSpectrum.Visible := False;
PbRuler.Visible := False;
PanelSplitter.Visible := False;
PbWaterfall.Visible := False;
FSpectrumWidth := RW;
FSpectrumHeight := 0;
FWaterfallHeight := 0;
PositionSampleRateOverlay;
Exit;
end;
// ---- Только спектр (водопад скрыт) ----
if FController.FShowSpectrum and (not FController.FShowWaterfall) then
begin
AvailH := RH - TopOff - RULER_H;
if AvailH < MIN_SH then AvailH := MIN_SH;
SH := AvailH;
WH := 0;
PbSpectrum.SetBounds(0, TopOff, RW, SH);
PbSpectrum.Visible := True;
PbRuler.SetBounds(0, TopOff + SH, RW, RULER_H);
PbRuler.Visible := True;
PanelSplitter.Visible := False;
PbWaterfall.Visible := False;
PositionSampleRateOverlay;
FSpectrumWidth := RW;
FSpectrumHeight := SH;
FWaterfallHeight := 0;
if RW > 0 then
begin
FSpecView.SetSpectrumBitmapSize(RW, SH);
FSpecView.SetWaterfallBitmapSize(1, 1);
FSpecView.SetRulerSize(RW, RULER_H);
FSpecView.DrawSpectrum;
PbSpectrum.Invalidate;
PbRuler.Invalidate;
end;
Exit;
end;
// ---- Только водопад (спектр скрыт): линейка под водопадом ----
if (not FController.FShowSpectrum) and FController.FShowWaterfall then
begin
AvailH := RH - TopOff - RULER_H;
if AvailH < MIN_WH then AvailH := MIN_WH;
SH := 0;
WH := AvailH;
PbSpectrum.Visible := False;
PanelSplitter.Visible := False;
PbWaterfall.SetBounds(0, TopOff, RW, WH);
PbWaterfall.Visible := True;
PbRuler.SetBounds(0, TopOff + WH, RW, RULER_H);
PbRuler.Visible := True;
PositionSampleRateOverlay;
FSpectrumWidth := RW;
FSpectrumHeight := 0;
FWaterfallHeight := WH;
if RW > 0 then
begin
FSpecView.SetSpectrumBitmapSize(1, 1);
FSpecView.SetWaterfallBitmapSize(RW, WH);
FSpecView.SetRulerSize(RW, RULER_H);
FSpecView.DrawWaterfall;
PbWaterfall.Invalidate;
PbRuler.Invalidate;
end;
Exit;
end;
// ---- Оба видимы: стандартный режим со сплиттером ----
PbSpectrum.Visible := True;
PbRuler.Visible := True;
PanelSplitter.Visible := True;
PbWaterfall.Visible := True;
AvailH := RH - TopOff - RULER_H - SPLITTER_H;
if AvailH < (MIN_SH + MIN_WH) then Exit;
// Ограничиваем соотношение, чтобы каждая зона имела минимальный размер
if FSplitterRatio < MIN_SH / AvailH then
FSplitterRatio := MIN_SH / AvailH;
if FSplitterRatio > 1.0 - MIN_WH / AvailH then
FSplitterRatio := 1.0 - MIN_WH / AvailH;
SH := Round(AvailH * FSplitterRatio);
WH := AvailH - SH;
if WH < MIN_WH then WH := MIN_WH;
// Спектр
PbSpectrum.SetBounds(0, TopOff, RW, SH);
// Линейка частот — всегда прижата к низу спектра
PbRuler.SetBounds(0, TopOff + SH, RW, RULER_H);
// Сплиттер — между линейкой и водопадом
PanelSplitter.SetBounds(0, TopOff + SH + RULER_H, RW, SPLITTER_H);
// Водопад — под сплиттером
PbWaterfall.SetBounds(0, TopOff + SH + RULER_H + SPLITTER_H, RW, WH);
// Стек спектр/линейка/сплиттер/водопад/пан-зум раскладывает панадаптер.
FPan.Layout(WideH, FController.FShowSpectrum, FController.FShowWaterfall);
PositionSampleRateOverlay;
// Обновляем переменные размеров
FSpectrumWidth := RW;
FSpectrumHeight := SH;
FWaterfallHeight := WH;
// Пересоздаём bitmap точно под новый размер
if RW > 0 then
begin
if SH > 0 then FSpecView.SetSpectrumBitmapSize(RW, SH);
if WH > 0 then FSpecView.SetWaterfallBitmapSize(RW, WH);
FSpecView.SetRulerSize(RW, RULER_H);
FSpecView.DrawSpectrum;
PbSpectrum.Invalidate;
if PbRuler <> nil then PbRuler.Invalidate;
PbWaterfall.Invalidate;
end;
end;
// ===========================================================================
@@ -3234,17 +2955,17 @@ var
begin
// Синхронизируем размеры если размер панели изменился (после ресайза).
// Важно: проверяем только видимые панели — когда спектр скрыт,
// PbSpectrum.Height может быть ненулевым при FSpectrumHeight=0,
// PbSpectrum.Height может быть ненулевым при FPan.SpectrumHeight=0,
// что вызвало бы сброс буфера водопада на каждом тике.
if (FSpectrumWidth = 0) or
(FController.FShowWideband and (PbWideband.Width <> FSpectrumWidth)) or
(FController.FShowSpectrum and ((PbSpectrum.Width <> FSpectrumWidth) or
(PbSpectrum.Height <> FSpectrumHeight))) or
if (FPan.SpectrumWidth = 0) or
(FController.FShowWideband and (PbWideband.Width <> FPan.SpectrumWidth)) or
(FController.FShowSpectrum and ((PbSpectrum.Width <> FPan.SpectrumWidth) or
(PbSpectrum.Height <> FPan.SpectrumHeight))) or
(FController.FShowWaterfall and not FController.FShowSpectrum and
((PbWaterfall.Width <> FSpectrumWidth) or
(PbWaterfall.Height <> FWaterfallHeight))) then
((PbWaterfall.Width <> FPan.SpectrumWidth) or
(PbWaterfall.Height <> FPan.WaterfallHeight))) then
ResizeSpectrumPanels;
if FSpectrumWidth <= 0 then Exit;
if FPan.SpectrumWidth <= 0 then Exit;
SyncSpecViewFreq;
// Проверка таймаута — трансивер не отвечает
@@ -3290,11 +3011,11 @@ begin
if FController.FWDSPReady then
begin
FController.FLastSMeter := FController.FDSPEngine.GetSMeterDBm;
FController.FDSPEngine.SetSpectrumWidth(FSpectrumWidth);
FController.FDSPEngine.SetSpectrumWidth(FPan.SpectrumWidth);
Inc(FAgcLineCounter);
if FAgcLineCounter >= 6 then
begin
FController.FDSPEngine.UpdateAGCLines(FSpectrumWidth);
FController.FDSPEngine.UpdateAGCLines(FPan.SpectrumWidth);
FSpecView.AGCThresh := FController.FDSPEngine.AGCThresh;
FSpecView.AGCHangLevel := FController.FDSPEngine.AGCHangLevel;
FAgcLineCounter := 0;
@@ -3350,9 +3071,9 @@ begin
if not FController.FNetwork.Connected then
begin
// При первом старте показываем демо-спектр
if FSpecView.SpectrumBitmapWidth <> FSpectrumWidth then
if FSpecView.SpectrumBitmapWidth <> FPan.SpectrumWidth then
begin
FSpecView.SetSpectrumBitmapSize(FSpectrumWidth, FSpectrumHeight);
FSpecView.SetSpectrumBitmapSize(FPan.SpectrumWidth, FPan.SpectrumHeight);
FSpecView.ResetSpectrumBuf;
FSpecView.DrawSpectrum;
PbSpectrum.Invalidate;
@@ -3891,7 +3612,7 @@ begin
// вся оркестрация в контроллере (ConnectAndRun); рендер device/band/run-
// виджетов идёт через события (rfDevice/rfBandRestore/rfXvtr/rfActiveVfo/
// rfRunning). Здесь — только UI-extras (статус-строки/AGCTop/resize).
if not FController.ConnectAndRun(Dev, FSpectrumWidth) then
if not FController.ConnectAndRun(Dev, FPan.SpectrumWidth) then
begin
ShowMessage('Failed to connect to ' + Dev.IPAddress +
IfThen(FController.FNetwork.LastError <> '', ': ' + FController.FNetwork.LastError, ''));
@@ -4437,27 +4158,6 @@ end;
// Панель пана / зума
// ---------------------------------------------------------------------------
procedure TMainForm.PositionPanZoomBar(BottomY, RW, H: Integer; AVisible: Boolean);
var BtnW, Gap, StripW, X: Integer;
begin
if (PbPanZoom = nil) or (BtnZoomIn = nil) then Exit;
PbPanZoom.Visible := AVisible;
BtnZoomIn.Visible := AVisible;
BtnZoomDef.Visible := AVisible;
BtnZoomOut.Visible := AVisible;
if not AVisible then Exit;
Gap := MulDiv(2, Screen.PixelsPerInch, 96);
BtnW := H; // квадратные кнопки в высоту строки
StripW := RW - 3 * BtnW - 4 * Gap;
if StripW < 20 then StripW := 20;
PbPanZoom.SetBounds(0, BottomY, StripW, H);
X := StripW + Gap;
BtnZoomOut.SetBounds(X, BottomY, BtnW, H); Inc(X, BtnW + Gap);
BtnZoomDef.SetBounds(X, BottomY, BtnW, H); Inc(X, BtnW + Gap);
BtnZoomIn.SetBounds(X, BottomY, BtnW, H);
PbPanZoom.Invalidate;
end;
procedure TMainForm.UpdatePanZoomBar;
begin
if FPanZoomBar <> nil then
@@ -4475,28 +4175,6 @@ begin
FController.ApplyZoom(AZoom, APan);
end;
procedure TMainForm.PbPanZoomDblClick(Sender: TObject);
begin
if FPanZoomBar <> nil then FPanZoomBar.HandleDblClick;
end;
procedure TMainForm.PbPanZoomMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
if FPanZoomBar <> nil then FPanZoomBar.HandleMouseDown(Button, X, Y);
end;
procedure TMainForm.PbPanZoomMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
begin
if FPanZoomBar <> nil then FPanZoomBar.HandleMouseMove(X, Y);
end;
procedure TMainForm.PbPanZoomMouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
if FPanZoomBar <> nil then FPanZoomBar.HandleMouseUp;
end;
procedure TMainForm.BtnZoomInClick(Sender: TObject);
begin
FController.ZoomIn;