Add NR/NB/SNB/ANF DSP row to VfoOverlay

Third button row mirrors MainForm NR (cycle 0-4), NB (cycle 0-2),
SNB and ANF toggles. OnDSPChange event syncs state back to MainForm
buttons and DSP engine. Overlay height tightened to match top/bottom
padding.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-22 12:40:04 +03:00
co-authored by Claude Sonnet 4.6
parent 5a74017e8a
commit 6b386a4a76
2 changed files with 131 additions and 16 deletions
+15 -2
View File
@@ -666,6 +666,7 @@ type
procedure BtnSettingsClick(Sender: TObject);
procedure PositionVfoOverlay;
procedure OnModeFilterSelect(Mode: Integer; FilterBW: Integer);
procedure OnVfoOverlayDSPChange(NRMode, NBMode: Integer; SNBOn, ANFOn: Boolean);
procedure PbSpectrumDblClick(Sender: TObject);
procedure TrkDriveChange(Sender: TObject);
procedure TrkVolumeChange(Sender: TObject);
@@ -1329,7 +1330,8 @@ begin
FDeviceDialog := TDeviceDialog.Create(Self);
FDeviceDialog.OnDiscover := BtnDiscoverFromDialog;
FVfoOverlay := TVfoOverlay.Create(Self);
FVfoOverlay.OnSelect := OnModeFilterSelect;
FVfoOverlay.OnSelect := OnModeFilterSelect;
FVfoOverlay.OnDSPChange := OnVfoOverlayDSPChange;
FVfoOverlay.Visible := False;
FPanelHidden := False;
FShowSpectrum := True;
@@ -5479,8 +5481,9 @@ begin
BtnHidePanel.Caption := '▶';
FVfoOverlay.Parent := PanelRight;
FVfoOverlay.Width := 260;
FVfoOverlay.Height := 130;
FVfoOverlay.Height := 136;
FVfoOverlay.SetState(FMode, FFilterBW, FVfoA, FLastSMeter);
FVfoOverlay.SetDSPState(BtnNR.Tag, BtnNB.Tag, BtnSNB.Tag <> 0, BtnANF.Tag <> 0);
FVfoOverlay.Visible := True;
FVfoOverlay.BringToFront;
PositionVfoOverlay;
@@ -5544,10 +5547,20 @@ begin
if Assigned(FVfoOverlay) and FVfoOverlay.Visible then
begin
FVfoOverlay.SetState(FMode, FFilterBW, FVfoA, FLastSMeter);
FVfoOverlay.SetDSPState(BtnNR.Tag, BtnNB.Tag, BtnSNB.Tag <> 0, BtnANF.Tag <> 0);
PositionVfoOverlay; // перепозиционируем при смене LSB↔USB
end;
end;
procedure TMainForm.OnVfoOverlayDSPChange(NRMode, NBMode: Integer; SNBOn, ANFOn: Boolean);
begin
BtnNR.Tag := NRMode; UpdateNRButton;
BtnNB.Tag := NBMode; UpdateNBButton;
BtnSNB.Tag := Ord(SNBOn); UpdateSNBButton;
BtnANF.Tag := Ord(ANFOn); UpdateANFButton;
ApplyNoiseFilterButtonsToDSP;
end;
procedure TMainForm.BtnSpanClick(Sender: TObject);
var
NewRate: Integer;
+116 -14
View File
@@ -9,6 +9,7 @@ uses
type
TModeFilterEvent = procedure(Mode: Integer; FilterBW: Integer) of object;
TDSPChangeEvent = procedure(NRMode, NBMode: Integer; SNBOn, ANFOn: Boolean) of object;
TVfoOverlay = class(TCustomControl)
private
@@ -17,10 +18,16 @@ type
FVfoHz: Double;
FSMeterDB: Double;
FOnSelect: TModeFilterEvent;
FModeFilter: array[0..7] of Integer; // запомненный фильтр для каждого режима
FOnDSPChange: TDSPChangeEvent;
FModeFilter: array[0..7] of Integer;
FHitRects: array[0..15] of TRect;
FHitActions: array[0..15] of Integer; // <0 = режим (-1=idx0..), >0 = BW фильтра
FNRMode: Integer; // 0=off, 1..4=NR/NR2/NR3/NR4
FNBMode: Integer; // 0=off, 1=NB, 2=NB2
FSNBOn: Boolean;
FANFOn: Boolean;
FHitRects: array[0..19] of TRect;
FHitActions: array[0..19] of Integer; // <0 = режим/DSP, >0 = BW фильтра
FHitCount: Integer;
FHotIdx: Integer;
@@ -28,6 +35,7 @@ type
procedure DrawSMeterBar(C: TCanvas; X, Y, BW, BH: Integer);
procedure DrawModeRow(C: TCanvas; X, Y, RowW, RowH: Integer);
procedure DrawFilterRow(C: TCanvas; X, Y, RowW, RowH: Integer);
procedure DrawDSPRow(C: TCanvas; X, Y, RowW, RowH: Integer);
procedure RegisterHit(HR: TRect; HV: Integer);
function CalcSLabel: string;
function FormatFreq(Hz: Double): string;
@@ -45,14 +53,21 @@ type
public
constructor Create(AOwner: TComponent); override;
procedure SetState(AMode, ABW: Integer; AVfoHz, ASMeterDB: Double);
procedure SetDSPState(ANRMode, ANBMode: Integer; ASNBOn, AANFOn: Boolean);
procedure UpdateSMeter(DB: Double);
procedure UpdateVfo(AVfoHz: Double);
property OnSelect: TModeFilterEvent read FOnSelect write FOnSelect;
property OnSelect: TModeFilterEvent read FOnSelect write FOnSelect;
property OnDSPChange: TDSPChangeEvent read FOnDSPChange write FOnDSPChange;
end;
implementation
const
HIT_NR = -9;
HIT_NB = -10;
HIT_SNB = -11;
HIT_ANF = -12;
CLR_PANEL_BG = TColor($00141414);
CLR_BORDER = TColor($00505050);
CLR_TEXT = TColor($00E8E8E8);
@@ -170,8 +185,12 @@ begin
FModeFilter[5] := 11000; // FM (NFM по умолчанию)
FModeFilter[6] := 8000; // AM
FModeFilter[7] := 8000; // SAM
Width := 260; // шире — 8 кнопок режимов
Height := 130;
FNRMode := 0;
FNBMode := 0;
FSNBOn := False;
FANFOn := False;
Width := 260;
Height := 136;
Cursor := crHandPoint;
end;
@@ -335,6 +354,62 @@ begin
end;
end;
procedure TVfoOverlay.DrawDSPRow(C: TCanvas; X, Y, RowW, RowH: Integer);
const
DSP_COUNT = 4;
DSP_HITS: array[0..3] of Integer = (HIT_NR, HIT_NB, HIT_SNB, HIT_ANF);
var
I, BX, BRight: Integer;
HR: TRect;
Lbl: string;
IsAct, IsHot: Boolean;
begin
BX := X;
for I := 0 to DSP_COUNT - 1 do
begin
BRight := X + ((I + 1) * RowW) div DSP_COUNT;
HR := Rect(BX, Y, BRight, Y + RowH);
case I of
0: begin
case FNRMode of
1: Lbl := 'NR';
2: Lbl := 'NR2';
3: Lbl := 'NR3';
4: Lbl := 'NR4';
else Lbl := 'NR';
end;
IsAct := FNRMode <> 0;
end;
1: begin
if FNBMode = 2 then Lbl := 'NB2' else Lbl := 'NB';
IsAct := FNBMode <> 0;
end;
2: begin Lbl := 'SNB'; IsAct := FSNBOn; end;
3: begin Lbl := 'ANF'; IsAct := FANFOn; end;
else Lbl := ''; IsAct := False;
end;
IsHot := (FHotIdx >= 0) and (FHitActions[FHotIdx] = DSP_HITS[I]);
if IsAct then C.Brush.Color := CLR_BTN_ACT
else if IsHot then C.Brush.Color := CLR_BTN_HOT
else C.Brush.Color := CLR_BTN_NORM;
C.Brush.Style := bsSolid; C.Pen.Style := psClear;
C.FillRect(HR);
C.Pen.Style := psSolid; C.Pen.Color := CLR_BORDER;
C.Brush.Style := bsClear; C.Rectangle(HR);
if IsAct then C.Font.Color := CLR_ACCENT
else C.Font.Color := CLR_TEXT;
C.Font.Size := 7; C.Font.Name := 'Courier New';
C.Brush.Style := bsClear;
C.TextOut(BX + (BRight - BX - C.TextWidth(Lbl)) div 2,
Y + (RowH - C.TextHeight('A')) div 2, Lbl);
RegisterHit(HR, DSP_HITS[I]);
BX := BRight + 1;
end;
end;
procedure TVfoOverlay.DrawSelf(C: TCanvas; W, H: Integer);
const
PAD = 4;
@@ -386,6 +461,10 @@ begin
// Кнопки фильтров
DrawFilterRow(C, PAD, CurY, W - PAD * 2, ROW_H);
Inc(CurY, ROW_H + GAP);
// Кнопки DSP (NR, NB, SNB, ANF)
DrawDSPRow(C, PAD, CurY, W - PAD * 2, ROW_H);
end;
procedure TVfoOverlay.Paint;
@@ -407,15 +486,30 @@ begin
if HV < 0 then
begin
NewMode := (-HV) - 1;
if NewMode <> FMode then
if NewMode < OVL_MODE_COUNT then
begin
// Сохраняем текущий фильтр для старого режима
FModeFilter[FMode] := FFilterBW;
FMode := NewMode;
// Восстанавливаем запомненный фильтр для нового режима
FFilterBW := FModeFilter[FMode];
// Кнопка режима
if NewMode <> FMode then
begin
FModeFilter[FMode] := FFilterBW;
FMode := NewMode;
FFilterBW := FModeFilter[FMode];
Invalidate;
if Assigned(FOnSelect) then FOnSelect(FMode, FFilterBW);
end;
end
else
begin
// Кнопка DSP
case HV of
HIT_NR: FNRMode := (FNRMode + 1) mod 5;
HIT_NB: FNBMode := (FNBMode + 1) mod 3;
HIT_SNB: FSNBOn := not FSNBOn;
HIT_ANF: FANFOn := not FANFOn;
end;
Invalidate;
if Assigned(FOnSelect) then FOnSelect(FMode, FFilterBW);
if Assigned(FOnDSPChange) then
FOnDSPChange(FNRMode, FNBMode, FSNBOn, FANFOn);
end;
end
else if HV > 0 then
@@ -424,7 +518,6 @@ begin
if NewBW <> FFilterBW then
begin
FFilterBW := NewBW;
// Запоминаем выбранный фильтр для текущего режима
FModeFilter[FMode] := FFilterBW;
Invalidate;
if Assigned(FOnSelect) then FOnSelect(FMode, FFilterBW);
@@ -463,6 +556,15 @@ begin
Invalidate;
end;
procedure TVfoOverlay.SetDSPState(ANRMode, ANBMode: Integer; ASNBOn, AANFOn: Boolean);
begin
FNRMode := ANRMode;
FNBMode := ANBMode;
FSNBOn := ASNBOn;
FANFOn := AANFOn;
Invalidate;
end;
procedure TVfoOverlay.UpdateSMeter(DB: Double);
begin
// Не клипаем — передаём как есть, как в основном S-метре