Replace PanelSpanButtons with SampleRateOverlay overlay

- Add SampleRateOverlay.pas: TCustomControl overlay (top-left of spectrum)
  with hide-panel button and 6 span buttons (48k–1536k), same hit-test
  pattern as VfoOverlay
- Consolidate toolbar: move PanelTopVfoGroup and PanelSMeterRight into
  PanelToolbar (height 72) instead of floating on the form
- Remove PanelSpanButtons (alTop spacer) that caused the ~1cm black strip
  at the top of the spectrum
- Add separator line inside PanelToolbar anchored to its bottom edge,
  with 16px left/right margins
- Fix left panel blocks starting Y offset (was 38, now 0)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-22 15:31:42 +03:00
co-authored by Claude Sonnet 4.6
parent 24ff61cb05
commit ea6e57b8b8
3 changed files with 320 additions and 212 deletions
+75 -212
View File
@@ -22,7 +22,8 @@ unit MainForm;
interface
uses
Classes, SysUtils, StrUtils, FreqDisplay, DeviceForm, VfoOverlay, FlatButton, FlatSlider, FlatDropDown, AppTheme, Forms, Controls, Graphics, Dialogs,
Classes, SysUtils, StrUtils, FreqDisplay, DeviceForm, VfoOverlay, SampleRateOverlay,
FlatButton, FlatSlider, FlatDropDown, AppTheme, Forms, Controls, Graphics, Dialogs,
StdCtrls, ExtCtrls, ComCtrls, Buttons, Menus, Math, Types,
LCLIntf, LCLType, GraphType,
HPSDRProtocol, HPSDRNetwork,
@@ -179,8 +180,9 @@ type
FDeviceCount: Integer;
FDeviceDialog: TDeviceDialog;
FPendingIP: string; // IP устройства для подключения
FVfoOverlay: TVfoOverlay; // накладка SmartSDR-стиль на спектре
FPanelHidden: Boolean; // True = левая панель скрыта
FVfoOverlay: TVfoOverlay; // накладка SmartSDR-стиль на спектре
FSampleRateOverlay: TSampleRateOverlay; // оверлей span/hide слева вверху спектра
FPanelHidden: Boolean; // True = левая панель скрыта
FPendingBoardType: Integer; // BoardType устройства для подключения
// ---- DSP + Audio ----
@@ -341,12 +343,11 @@ type
FSpectrumTimer: TTimer;
FAfterShowTimer:TTimer; // однократный таймер для пост-инициализации
// ---- Toolbar ----
// ---- Top panel (toolbar + VFO group + S-meter) ----
PanelToolbar: TPanel;
BtnDiscover: TFlatButton;
BtnStartStop: TFlatButton; // START / STOP (connect+run в одном)
BtnSettings: TFlatButton; // открыть диалог настроек
PanelSep: TPanel; // 1px разделитель под тулбаром
BtnStartStop: TFlatButton;
BtnSettings: TFlatButton;
// ---- Left panel ----
PanelLeft: TPanel;
@@ -418,16 +419,7 @@ type
BtnTUN: TFlatButton;
// ---- Right panel ----
PanelRight: TPanel;
PanelSpanButtons: TPanel;
LblSpan: TLabel;
BtnSpan48k: TFlatButton;
BtnSpan96k: TFlatButton;
BtnSpan192k: TFlatButton;
BtnSpan384k: TFlatButton;
BtnSpan768k: TFlatButton;
BtnSpan1536k: TFlatButton;
BtnHidePanel: TFlatButton; // скрыть/показать левую панель
PanelRight: TPanel;
PbSpectrum: TPaintBox;
PbRuler: TPaintBox; // полоса частотных меток между спектром и водопадом
PanelSplitter: TPanel; // перетаскиваемый разделитель спектр/водопад
@@ -445,7 +437,9 @@ type
procedure SetStatusText(Index: Integer; const Text: string);
procedure SetRadioOfflineStatus(const ConnText: string; ClearDevice: Boolean = False);
procedure ApplyStatusTheme(const T: TAppTheme);
procedure PositionSpanOverlayButtons;
procedure PositionSampleRateOverlay;
procedure OnSampleRateSelect(SampleRate: Integer);
procedure OnSampleRateHidePanel;
procedure UpdateVfoDisplay;
function FormatFreq(Hz: Double): string;
procedure LayoutTopVfoBlock;
@@ -551,7 +545,6 @@ type
procedure BtnNBClick(Sender: TObject);
procedure BtnSNBClick(Sender: TObject);
procedure BtnANFClick(Sender: TObject);
procedure BtnSpanClick(Sender: TObject);
// Веб-интерфейс: callbacks от TWebServer
procedure WebOnFreq(Hz: Double);
procedure WebOnMode(Mode: Integer);
@@ -660,7 +653,6 @@ type
procedure ActivateXvtrBand(Idx: Integer);
procedure DeactivateXvtr;
// -------------------------------------------------------------------------
procedure BtnHidePanelClick(Sender: TObject);
procedure BtnSettingsClick(Sender: TObject);
procedure PositionVfoOverlay;
procedure OnModeFilterSelect(Mode: Integer; FilterBW: Integer);
@@ -1127,14 +1119,9 @@ begin
// --- Sample Rate / Span ---
// SampleRate — глобальный, не per-band. Не восстанавливаем из диапазона.
// Обновляем только FSpanHz для отображения (берём текущий FSampleRate)
FSpanHz := FSampleRate;
StyleSpanButton(BtnSpan48k, FSampleRate = 48000);
StyleSpanButton(BtnSpan96k, FSampleRate = 96000);
StyleSpanButton(BtnSpan192k, FSampleRate = 192000);
StyleSpanButton(BtnSpan384k, FSampleRate = 384000);
StyleSpanButton(BtnSpan768k, FSampleRate = 768000);
StyleSpanButton(BtnSpan1536k, FSampleRate = 1536000);
if Assigned(FSampleRateOverlay) then
FSampleRateOverlay.SetCurrentRate(FSampleRate);
// --- VFO A: центрируем на новой частоте, сбрасываем shift ---
FCenterFreq := B.VfoA; // DDC = центр диапазона
@@ -1375,12 +1362,6 @@ begin
FCurrentXvtr := -1;
BuildUI;
StyleSpanButton(BtnSpan48k, FSampleRate = 48000);
StyleSpanButton(BtnSpan96k, FSampleRate = 96000);
StyleSpanButton(BtnSpan192k, FSampleRate = 192000);
StyleSpanButton(BtnSpan384k, FSampleRate = 384000);
StyleSpanButton(BtnSpan768k, FSampleRate = 768000);
StyleSpanButton(BtnSpan1536k, FSampleRate = 1536000);
ResizeSMeter;
ApplyDarkTheme;
UpdateVfoDisplay;
@@ -1501,6 +1482,7 @@ const
var
i, X, Y, W: Integer;
B: TFlatButton;
SepLine: TPanel;
function MakeBtn(Parent: TWinControl; const Cap: string;
ALeft, ATop, AW, AH: Integer; Handler: TNotifyEvent): TFlatButton;
@@ -1535,7 +1517,7 @@ begin
PanelToolbar := TPanel.Create(Self);
PanelToolbar.Parent := Self;
PanelToolbar.Align := alTop;
PanelToolbar.Height := 36;
PanelToolbar.Height := 72;
PanelToolbar.BevelOuter := bvNone;
PanelToolbar.Color := CLR_PANEL;
@@ -1557,7 +1539,13 @@ begin
BtnStartStop.ClrBorder := TColor($00304830);
BtnStartStop.ClrText := TColor($0090C090);
// ---- Separator line ----
// Линия-разделитель внутри PanelToolbar, прижата к его нижнему краю
SepLine := TPanel.Create(Self);
SepLine.Parent := PanelToolbar;
SepLine.BevelOuter := bvNone;
SepLine.Color := TColor($00606060);
SepLine.SetBounds(16, PanelToolbar.Height - 10, PanelToolbar.Width - 32, 2);
SepLine.Anchors := [akLeft, akRight, akBottom];
// ---- Status bar ----
StatusPanel := TMainStatusBar.Create(Self);
@@ -1570,11 +1558,11 @@ begin
PanelLeft.Width := LEFT_W;
PanelLeft.BevelOuter := bvNone;
Y := 38; // gap below separator: separator at y=66, content starts at y=74
Y := 0;
// Compact top VFO group
PanelTopVfoGroup := TPanel.Create(Self);
PanelTopVfoGroup.Parent := Self;
PanelTopVfoGroup.Parent := PanelToolbar;
PanelTopVfoGroup.SetBounds(0, 0, TOP_VFO_W * 2 + 64, 60);
PanelTopVfoGroup.BevelOuter := bvNone;
PanelTopVfoGroup.Color := TColor($00181818);
@@ -1940,59 +1928,15 @@ begin
PanelRight.BevelOuter := bvNone;
PanelRight.OnResize := RightPanelResize;
PanelSpanButtons := TPanel.Create(Self);
PanelSpanButtons.Parent := PanelRight;
PanelSpanButtons.Align := alTop;
PanelSpanButtons.Height := 38;
PanelSpanButtons.BevelOuter := bvNone;
FSampleRateOverlay := TSampleRateOverlay.Create(Self);
FSampleRateOverlay.Parent := PanelRight;
FSampleRateOverlay.OnSpanSelect := OnSampleRateSelect;
FSampleRateOverlay.OnHidePanel := OnSampleRateHidePanel;
FSampleRateOverlay.SetCurrentRate(FSampleRate);
LblSpan := TLabel.Create(Self);
LblSpan.Parent := PanelSpanButtons; LblSpan.Left := 4; LblSpan.Top := 8;
LblSpan.Caption := 'SPAN:'; LblSpan.Font.Color := CLR_TEXTDIM;
LblSpan.Font.Name := 'Courier New'; LblSpan.Font.Size := 7;
BtnSpan48k := MakeBtn(PanelSpanButtons, '48k', 50, 3, 46, BTN_SM, BtnSpanClick);
BtnSpan96k := MakeBtn(PanelSpanButtons, '96k', 99, 3, 46, BTN_SM, BtnSpanClick);
BtnSpan192k := MakeBtn(PanelSpanButtons, '192k', 148, 3, 52, BTN_SM, BtnSpanClick);
BtnSpan384k := MakeBtn(PanelSpanButtons, '384k', 203, 3, 52, BTN_SM, BtnSpanClick);
BtnSpan768k := MakeBtn(PanelSpanButtons, '768k', 258, 3, 52, BTN_SM, BtnSpanClick);
BtnSpan1536k := MakeBtn(PanelSpanButtons, '1536k', 313, 3, 58, BTN_SM, BtnSpanClick);
BtnSpan48k.Tag := 48000;
BtnSpan96k.Tag := 96000;
BtnSpan192k.Tag := 192000;
BtnSpan384k.Tag := 384000;
BtnSpan768k.Tag := 768000;
BtnSpan1536k.Tag := 1536000;
StyleSpanButton(BtnSpan48k, False);
StyleSpanButton(BtnSpan96k, False);
StyleSpanButton(BtnSpan192k, True);
StyleSpanButton(BtnSpan384k, False);
StyleSpanButton(BtnSpan768k, False);
StyleSpanButton(BtnSpan1536k, False);
BtnHidePanel := MakeBtn(PanelRight, '◀', 0, 0, 28, 28, BtnHidePanelClick);
BtnHidePanel.Font.Size := 10;
BtnHidePanel.ClrNorm := TColor($00543F18);
BtnHidePanel.ClrHot := TColor($00624B1D);
BtnHidePanel.ClrActive := TColor($00624B1D);
BtnHidePanel.ClrBorder := TColor($00685624);
BtnHidePanel.ClrText := TColor($00D8E6EE);
BtnHidePanel.ClrTextAct := TColor($00F0FAFF);
BtnHidePanel.BringToFront;
// Разделитель: на уровне формы, под всеми верхними элементами
PanelSep := TPanel.Create(Self);
PanelSep.Parent := Self;
PanelSep.Align := alNone;
PanelSep.BevelOuter := bvNone;
PanelSep.ParentColor := False;
PanelSep.Color := TColor($00606060);
PanelSep.SetBounds(16, PanelToolbar.Height + 30, ClientWidth - 32, 2);
// Большой S-метр — на главной форме поверх двух панелей
// S-метр: Parent=Self, перекрывает Toolbar+SpanButtons вертикально
// S-метр — внутри PanelToolbar, справа
PanelSMeterRight := TPanel.Create(Self);
PanelSMeterRight.Parent := Self;
PanelSMeterRight.Parent := PanelToolbar;
PanelSMeterRight.BevelOuter := bvNone;
PanelSMeterRight.Color := CLR_PANEL;
PbSMeterRight := TPaintBox.Create(Self);
@@ -2033,16 +1977,6 @@ begin
PbWaterfall.OnMouseMove := PbWaterfallMouseMove;
PbWaterfall.OnMouseUp := PbWaterfallMouseUp;
// SPAN кнопки как оверлей на спектре (в стиле web)
BtnSpan48k.Parent := PanelRight;
BtnSpan96k.Parent := PanelRight;
BtnSpan192k.Parent := PanelRight;
BtnSpan384k.Parent := PanelRight;
BtnSpan768k.Parent := PanelRight;
BtnSpan1536k.Parent := PanelRight;
LblSpan.Visible := False;
PositionSpanOverlayButtons;
// Initial layout
ResizeSpectrumPanels;
end;
@@ -2089,7 +2023,7 @@ begin
P := PanelRight.ScreenToClient(PanelSplitter.ClientToScreen(Point(X, Y)));
DeltaY := P.Y - FSplitterDragY0;
TopOff := PanelSpanButtons.Height;
TopOff := 0;
AvailH := PanelRight.ClientHeight - TopOff - RULER_H - SPLITTER_H;
if AvailH <= 0 then Exit;
@@ -2127,7 +2061,7 @@ begin
P := PanelRight.ScreenToClient(PanelSplitter.ClientToScreen(Point(X, Y)));
DeltaY := P.Y - FSplitterDragY0;
TopOff := PanelSpanButtons.Height;
TopOff := 0;
AvailH := PanelRight.ClientHeight - TopOff - RULER_H - SPLITTER_H;
if AvailH <= 0 then Exit;
@@ -2173,7 +2107,7 @@ var
TopVfoAW, TopVfoBW, NeedFreqAW, NeedFreqBW, FreqTextW, GroupW: Integer;
XferX, XferY: Integer;
begin
if (PanelToolbar = nil) or (PanelSpanButtons = nil) or (PanelTopVfoGroup = nil) or
if (PanelToolbar = nil) or (PanelTopVfoGroup = nil) or
(PanelTopVfoA = nil) or (PanelTopVfoB = nil) then Exit;
Canvas.Font.Name := FreqDispB.FontName;
@@ -2217,16 +2151,14 @@ begin
DynGap := XFER_SIDE_GAP + ExtraPerSide;
GroupW := TopVfoAW + DynGap + XFER_PANEL_W + DynGap + TopVfoBW;
BlockH := PanelToolbar.Height + 30 - (MARGIN * 2); // 30 = span row content, not full PanelSpanButtons
BlockH := PanelToolbar.Height - (MARGIN * 2) - 8;
if BlockH < 56 then BlockH := 56;
// Центрируем группу в доступном пространстве
GroupLeft := SafeLeft + (AvailW - GroupW) div 2;
GroupTop := TOP_OFFSET;
// Ось выравнивания: центр S-метра на форме (SMTop=3, SMBot=63) → y=33
// Относительно PanelTopVfoGroup (GroupTop=2): CenterY = 33 - 2 = 31
CenterY := (PanelToolbar.Height + 30) div 2 - GroupTop;
CenterY := PanelToolbar.Height div 2 - GroupTop;
PanelTopVfoGroup.SetBounds(GroupLeft, GroupTop, GroupW, BlockH);
PanelTopVfoGroup.Visible := True;
@@ -2264,15 +2196,11 @@ begin
FreqDispB.SetBounds(FreqDispB.Left, FreqTop,
FreqTextW + VFO_FREQ_LEFT_PAD + VFO_FREQ_RIGHT_PAD, TOP_VFO_FREQ_H);
if PanelSep <> nil then
PanelSep.SetBounds(16, PanelToolbar.Height + 30, ClientWidth - 32, 2);
PanelTopVfoGroup.BringToFront;
end;
procedure TMainForm.ResizeSMeter;
// PanelSMeterRight на главной форме (Parent=Self).
// По Y: от самого верха формы до низа PanelSpanButtons (Toolbar+Span = 66px).
// По Y: от самого верха формы до низа тулбара.
var
FW: Integer;
RightW: Integer; // ширина PanelRight
@@ -2281,8 +2209,7 @@ var
SMTop: Integer;
SMBot: Integer;
begin
if (PanelSMeterRight = nil) or (PanelToolbar = nil) or
(PanelSpanButtons = nil) or (PanelLeft = nil) then Exit;
if (PanelSMeterRight = nil) or (PanelToolbar = nil) or (PanelLeft = nil) then Exit;
LayoutTopVfoBlock;
@@ -2294,15 +2221,13 @@ begin
// Ширина S-метра = 32% от PanelRight
SMW := Round(RightW * SMETER_WIDTH_RATIO);
// X: прижат к правому краю формы
// X: прижат к правому краю PanelToolbar
SMLeft := FW - SMW - SMETER_MARGIN;
// Y: от верха формы+margin до низа PanelSpanButtons-margin
SMTop := SMETER_MARGIN;
SMBot := PanelToolbar.Height + 30 - SMETER_MARGIN; // 30 = span row content, ends before separator
SMBot := PanelToolbar.Height - SMETER_MARGIN - 9;
PanelSMeterRight.SetBounds(SMLeft, SMTop, SMW, SMBot - SMTop);
PanelSMeterRight.BringToFront;
end;
procedure TMainForm.ResizeSpectrumPanels;
@@ -2320,7 +2245,7 @@ begin
if Assigned(FSpecView) then SyncSpecViewFreq;
RW := PanelRight.ClientWidth;
RH := PanelRight.ClientHeight;
TopOff := PanelSpanButtons.Height;
TopOff := 0;
// S-метр позиционируется отдельно в ResizeSMeter
ResizeSMeter;
@@ -2334,7 +2259,7 @@ begin
FSpectrumWidth := RW;
FSpectrumHeight := 0;
FWaterfallHeight := 0;
PositionSpanOverlayButtons;
PositionSampleRateOverlay;
Exit;
end;
@@ -2351,7 +2276,7 @@ begin
PbRuler.Visible := True;
PanelSplitter.Visible := False;
PbWaterfall.Visible := False;
PositionSpanOverlayButtons;
PositionSampleRateOverlay;
FSpectrumWidth := RW;
FSpectrumHeight := SH;
FWaterfallHeight := 0;
@@ -2380,7 +2305,7 @@ begin
PbWaterfall.Visible := True;
PbRuler.SetBounds(0, TopOff + WH, RW, RULER_H);
PbRuler.Visible := True;
PositionSpanOverlayButtons;
PositionSampleRateOverlay;
FSpectrumWidth := RW;
FSpectrumHeight := 0;
FWaterfallHeight := WH;
@@ -2424,7 +2349,7 @@ begin
// Водопад — под сплиттером
PbWaterfall.SetBounds(0, TopOff + SH + RULER_H + SPLITTER_H, RW, WH);
PositionSpanOverlayButtons;
PositionSampleRateOverlay;
// Обновляем переменные размеров
FSpectrumWidth := RW;
@@ -2471,7 +2396,7 @@ begin
if PanelFMCTCSS <> nil then DP(PanelFMCTCSS);
if PanelFMStep <> nil then DP(PanelFMStep);
DP(PanelRX); DP(PanelTX);
DP(PanelRight); DP(PanelSpanButtons);
DP(PanelRight);
PbSpectrum.Color := T.BG;
PbWaterfall.Color := T.BG;
@@ -2481,7 +2406,7 @@ begin
// Метки
DL(LblAGCTop, False);
DL(LblDrv); DL(LblSpan);
DL(LblDrv);
if LblFMSQ <> nil then DL(LblFMSQ, False);
// Кнопки тулбара
@@ -2518,17 +2443,10 @@ begin
StyleButton(BtnMOX, BtnMOX.Active);
StyleButton(BtnTUN, BtnTUN.Active);
// Span / VFO-copy / HidePanel
StyleSpanButton(BtnSpan48k, BtnSpan48k.Active);
StyleSpanButton(BtnSpan96k, BtnSpan96k.Active);
StyleSpanButton(BtnSpan192k, BtnSpan192k.Active);
StyleSpanButton(BtnSpan384k, BtnSpan384k.Active);
StyleSpanButton(BtnSpan768k, BtnSpan768k.Active);
StyleSpanButton(BtnSpan1536k, BtnSpan1536k.Active);
// VFO-copy
StyleSpanButton(BtnVfoACopyB, False);
StyleSpanButton(BtnVfoSwap, False);
StyleSpanButton(BtnVfoBCopyA, False);
if BtnHidePanel <> nil then StyleSpanButton(BtnHidePanel, False);
// Ползунки
DS(TrkAGC); DS(TrkVolume); DS(TrkDrive);
@@ -2618,59 +2536,17 @@ begin
B.Font.Size := 7;
end;
procedure TMainForm.PositionSpanOverlayButtons;
procedure TMainForm.PositionSampleRateOverlay;
const
BTN_TOP = 6;
GAP = 3;
W48 = 40;
W96 = 40;
W192 = 44;
W384 = 44;
W768 = 44;
W1536 = 50;
HBTN = 18;
var
X, NeedW, BaseX, BaseY: Integer;
MARGIN = 4;
begin
if (PbSpectrum = nil) or (BtnSpan48k = nil) then Exit;
NeedW := W48 + W96 + W192 + W384 + W768 + W1536 + GAP * 5 + 8;
if PbSpectrum.Width < NeedW then
begin
BtnSpan48k.Visible := False;
BtnSpan96k.Visible := False;
BtnSpan192k.Visible := False;
BtnSpan384k.Visible := False;
BtnSpan768k.Visible := False;
BtnSpan1536k.Visible := False;
Exit;
end;
BaseX := PbSpectrum.Left;
BaseY := PbSpectrum.Top;
X := BaseX + 56;
BtnSpan48k.SetBounds(X, BaseY + BTN_TOP, W48, HBTN); Inc(X, W48 + GAP);
BtnSpan96k.SetBounds(X, BaseY + BTN_TOP, W96, HBTN); Inc(X, W96 + GAP);
BtnSpan192k.SetBounds(X, BaseY + BTN_TOP, W192, HBTN); Inc(X, W192 + GAP);
BtnSpan384k.SetBounds(X, BaseY + BTN_TOP, W384, HBTN); Inc(X, W384 + GAP);
BtnSpan768k.SetBounds(X, BaseY + BTN_TOP, W768, HBTN); Inc(X, W768 + GAP);
BtnSpan1536k.SetBounds(X, BaseY + BTN_TOP, W1536, HBTN);
if BtnHidePanel <> nil then
BtnHidePanel.SetBounds(BaseX, BaseY + BTN_TOP, 28, HBTN);
BtnSpan48k.Visible := True;
BtnSpan96k.Visible := True;
BtnSpan192k.Visible := True;
BtnSpan384k.Visible := True;
BtnSpan768k.Visible := True;
BtnSpan1536k.Visible := True;
BtnSpan48k.BringToFront;
BtnSpan96k.BringToFront;
BtnSpan192k.BringToFront;
BtnSpan384k.BringToFront;
BtnSpan768k.BringToFront;
BtnSpan1536k.BringToFront;
if BtnHidePanel <> nil then BtnHidePanel.BringToFront;
if (FSampleRateOverlay = nil) or (PbSpectrum = nil) then Exit;
FSampleRateOverlay.SetBounds(
PbSpectrum.Left + MARGIN,
PbSpectrum.Top + MARGIN,
FSampleRateOverlay.Width,
FSampleRateOverlay.Height);
FSampleRateOverlay.BringToFront;
end;
// ===========================================================================
@@ -3954,6 +3830,8 @@ begin
begin
FSampleRate := G_Settings.SampleRate;
FSpanHz := FSampleRate;
if Assigned(FSampleRateOverlay) then
FSampleRateOverlay.SetCurrentRate(FSampleRate);
end;
BtnNR.Tag := EnsureRange(G_Settings.NRMode, 0, 4); UpdateNRButton;
BtnNB.Tag := EnsureRange(G_Settings.NBMode, 0, 2); UpdateNBButton;
@@ -5390,13 +5268,12 @@ begin
FSettings.SaveWebSettings(W);
end;
procedure TMainForm.BtnHidePanelClick(Sender: TObject);
procedure TMainForm.OnSampleRateHidePanel;
begin
FPanelHidden := not FPanelHidden;
if FPanelHidden then
begin
PanelLeft.Width := 0;
BtnHidePanel.Caption := '▶';
FVfoOverlay.Parent := PanelRight;
FVfoOverlay.Width := 260;
FVfoOverlay.Height := 136; // OVL_H_NORM — расширяется сам при открытии AGC-пикера
@@ -5409,9 +5286,10 @@ begin
else
begin
PanelLeft.Width := 232;
BtnHidePanel.Caption := '◀';
FVfoOverlay.Visible := False;
end;
if Assigned(FSampleRateOverlay) then
FSampleRateOverlay.SetPanelHidden(FPanelHidden);
ResizeSpectrumPanels;
ResizeSMeter;
end;
@@ -5492,25 +5370,20 @@ begin
FBandCache[FCurrentBand].AGCMode := FAGCMode;
end;
procedure TMainForm.BtnSpanClick(Sender: TObject);
procedure TMainForm.OnSampleRateSelect(SampleRate: Integer);
var
NewRate: Integer;
DDCRate: Word;
begin
NewRate := (Sender as TFlatButton).Tag;
NewRate := SampleRate;
if NewRate = FSampleRate then Exit;
FSampleRate := NewRate;
FSpanHz := NewRate;
DDCRate := NewRate div 1000;
// Обновляем кнопки сразу — UI отзывчив
StyleSpanButton(BtnSpan48k, FSampleRate = 48000);
StyleSpanButton(BtnSpan96k, FSampleRate = 96000);
StyleSpanButton(BtnSpan192k, FSampleRate = 192000);
StyleSpanButton(BtnSpan384k, FSampleRate = 384000);
StyleSpanButton(BtnSpan768k, FSampleRate = 768000);
StyleSpanButton(BtnSpan1536k, FSampleRate = 1536000);
if Assigned(FSampleRateOverlay) then
FSampleRateOverlay.SetCurrentRate(NewRate);
Application.ProcessMessages;
FBandCache[FCurrentBand].SpanHz := FSpanHz; // для обратной совместимости
@@ -5762,27 +5635,17 @@ begin
end;
procedure TMainForm.SyncWebSpan;
const
ValidSpans: array[0..5] of Integer = (48000, 96000, 192000, 384000, 768000, 1536000);
var
ValidSpans: array[0..5] of Integer;
Found: Boolean;
i: Integer;
Btn: TFlatButton;
begin
ValidSpans[0]:=48000; ValidSpans[1]:=96000; ValidSpans[2]:=192000;
ValidSpans[3]:=384000; ValidSpans[4]:=768000; ValidSpans[5]:=1536000;
Found := False;
for i := 0 to 5 do
if ValidSpans[i] = FWebSyncInt then begin Found := True; Break; end;
if not Found then Exit;
if FWebSyncInt = FSampleRate then Exit;
Btn := nil;
if FWebSyncInt = 48000 then Btn := BtnSpan48k
else if FWebSyncInt = 96000 then Btn := BtnSpan96k
else if FWebSyncInt = 192000 then Btn := BtnSpan192k
else if FWebSyncInt = 384000 then Btn := BtnSpan384k
else if FWebSyncInt = 768000 then Btn := BtnSpan768k
else if FWebSyncInt = 1536000 then Btn := BtnSpan1536k;
if Assigned(Btn) then BtnSpanClick(Btn);
for i := 0 to High(ValidSpans) do
if ValidSpans[i] = FWebSyncInt then
begin
OnSampleRateSelect(FWebSyncInt);
Exit;
end;
end;
procedure TMainForm.WebOnVolume(V: Integer);