mirror of
https://git.vladimir.cc/vladimir/ewsdr.git
synced 2026-08-25 17:27:32 +00:00
feat(vfo): кнопка SQL + ползунок FM-squelch в DSP-меню флага (main + слайсы)
В fly-out DSP появляется ряд SQL (тумблер + порог 0..100) только в режиме FM. Squelch работает per-slice: у слайсов свои WDSP-каналы, поэтому добавлены поля/сеттеры FMSQ в TSliceChan/TCtrlSlice, ApplyFMSQToChan и восстановление при переоткрытии канала. Роутинг события по SliceId (0=RXA, иначе слайс). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+90
-1
@@ -41,6 +41,7 @@ function SliceColor(L: Char): TColor;
|
||||
type
|
||||
TModeFilterEvent = procedure(Mode: Integer; FilterBW: Integer) of object;
|
||||
TDSPChangeEvent = procedure(NRMode, NBMode: Integer; SNBOn, ANFOn: Boolean) of object;
|
||||
TSquelchEvent = procedure(SQLOn: Boolean; SQLLevel: Integer) of object;
|
||||
TAGCChangeEvent = procedure(AGCMode: Integer) of object;
|
||||
TVolumeEvent = procedure(Vol: Integer) of object;
|
||||
TAudioDeviceEvent = procedure(DevIndex: Integer; const DevName: string) of object;
|
||||
@@ -61,6 +62,9 @@ type
|
||||
FANFOn: Boolean;
|
||||
FAGCMode: Integer; // 0=FAST 1=MED 2=SLOW 3=LONG 4=OFF
|
||||
|
||||
FSQLOn: Boolean; // FM-шумоподавитель (squelch) включён
|
||||
FSQLLevel: Integer; // порог squelch 0..100
|
||||
|
||||
FVolume: Integer; // 0..100
|
||||
FMuted: Boolean;
|
||||
FSplit: Boolean;
|
||||
@@ -77,9 +81,12 @@ type
|
||||
FAudScroll: Integer; // прокрутка списка аудио-устройств (верхний индекс)
|
||||
FVolTrack: TRect; // трек громкости (локальные координаты)
|
||||
FVolDrag: Boolean;
|
||||
FSqlTrack: TRect; // трек squelch в DSP-панели (лок. координаты)
|
||||
FSqlDrag: Boolean;
|
||||
|
||||
FOnSelect: TModeFilterEvent;
|
||||
FOnDSPChange: TDSPChangeEvent;
|
||||
FOnSquelchChange: TSquelchEvent;
|
||||
FOnAGCChange: TAGCChangeEvent;
|
||||
FOnVolume: TVolumeEvent;
|
||||
FOnMute: TNotifyEvent;
|
||||
@@ -131,6 +138,8 @@ type
|
||||
function ComputeHeight: Integer;
|
||||
procedure SetPanel(P: TFlyPanel);
|
||||
procedure ApplyVolFromX(LX: Integer);
|
||||
procedure ApplySqlFromX(LX: Integer);
|
||||
function IsFM: Boolean;
|
||||
procedure RequestInvalidate;
|
||||
procedure RequestMeterInvalidate;
|
||||
procedure RefreshMeter(C: TCanvas);
|
||||
@@ -159,6 +168,7 @@ type
|
||||
procedure SetState(AMode, ABW: Integer; AVfoHz, ASMeterDB: Double);
|
||||
procedure SetDSPState(ANRMode, ANBMode: Integer; ASNBOn, AANFOn: Boolean;
|
||||
AAGCMode: Integer);
|
||||
procedure SetSquelchState(AOn: Boolean; ALevel: Integer);
|
||||
procedure SetExtState(AVolume: Integer; AMuted, ASplit, ATx, ATxSel: Boolean);
|
||||
procedure SetAudioDevices(Names: TStrings; const Indices: array of Integer;
|
||||
const CurName: string);
|
||||
@@ -185,6 +195,7 @@ type
|
||||
property BaseHeight: Integer read ComputeHeight;
|
||||
property OnSelect: TModeFilterEvent read FOnSelect write FOnSelect;
|
||||
property OnDSPChange: TDSPChangeEvent read FOnDSPChange write FOnDSPChange;
|
||||
property OnSquelchChange: TSquelchEvent read FOnSquelchChange write FOnSquelchChange;
|
||||
property OnAGCChange: TAGCChangeEvent read FOnAGCChange write FOnAGCChange;
|
||||
property OnVolume: TVolumeEvent read FOnVolume write FOnVolume;
|
||||
property OnMute: TNotifyEvent read FOnMute write FOnMute;
|
||||
@@ -228,6 +239,8 @@ const
|
||||
HIT_AUD_DN = -28;
|
||||
HIT_SLICE_SEL = -29; // клик по бейджу буквы (выбрать слайс активным)
|
||||
HIT_CLOSE = -30; // × закрытия слайса (неглавные)
|
||||
HIT_SQL = -31; // тумблер FM-squelch (в DSP-панели)
|
||||
HIT_SQL_SLIDER = -32; // трек порога FM-squelch
|
||||
HIT_AGC_PICK_BASE = 100000;
|
||||
HIT_DEV_BASE = 200000;
|
||||
|
||||
@@ -402,6 +415,7 @@ begin
|
||||
FModeFilter[3] := 500; FModeFilter[4] := 500; FModeFilter[5] := 11000;
|
||||
FModeFilter[6] := 8000; FModeFilter[7] := 8000;
|
||||
FNRMode := 0; FNBMode := 0; FSNBOn := False; FANFOn := False; FAGCMode := 0;
|
||||
FSQLOn := False; FSQLLevel := 40; FSqlDrag := False;
|
||||
FVolume := 70; FMuted := False; FSplit := False; FTx := False;
|
||||
FTxSel := True; FSlice := 'A';
|
||||
FSliceId := 0; FIsMain := True; // по умолчанию — главный приёмник (слайс A)
|
||||
@@ -532,11 +546,19 @@ end;
|
||||
|
||||
{ ---- Высота с учётом fly-out ---- }
|
||||
|
||||
function TVfoOverlay.IsFM: Boolean;
|
||||
begin
|
||||
Result := FMode = 5; // индекс FM в OVL_MODE_NAMES (= MODE_FM контроллера)
|
||||
end;
|
||||
|
||||
function TVfoOverlay.PanelContentH: Integer;
|
||||
begin
|
||||
case FPanel of
|
||||
fpMode: Result := ROW_H*2 + IGAP + ROW_H + IGAP; // 2 ряда режимов + ряд фильтров
|
||||
fpDsp: Result := ROW_H + IGAP + ROW_H; // toggles + AGC
|
||||
fpDsp: begin
|
||||
Result := ROW_H + IGAP + ROW_H; // toggles + AGC
|
||||
if IsFM then Inc(Result, IGAP + ROW_H); // + ряд SQL (только FM)
|
||||
end;
|
||||
fpAud: Result := Min(Max(1, FDevNames.Count), AUD_MAX_VIS) * AUD_ROW_H;
|
||||
else Result := 0;
|
||||
end;
|
||||
@@ -796,6 +818,34 @@ begin
|
||||
RegisterHit(HR, HIT_AGC_PICK_BASE + I);
|
||||
BX := BRight;
|
||||
end;
|
||||
|
||||
// Ряд 3 (только FM): тумблер SQL + ползунок порога squelch
|
||||
if IsFM then
|
||||
begin
|
||||
Inc(Y, ROW_H + IGAP);
|
||||
// кнопка SQL слева (1/4 ширины)
|
||||
BRight := X + RowW div 4;
|
||||
HR := Rect(X, Y, BRight - 2, Y + ROW_H);
|
||||
DrawButton(C, HR, 'SQL', FSQLOn,
|
||||
(FHotIdx >= 0) and (FHitActions[FHotIdx] = HIT_SQL), 7);
|
||||
RegisterHit(HR, HIT_SQL);
|
||||
// ползунок порога справа
|
||||
BX := BRight + 4;
|
||||
FSqlTrack := Rect(BX, Y + ROW_H div 2 - 3, X + RowW, Y + ROW_H div 2 + 3);
|
||||
C.Brush.Color := CLR_BTN_NORM; C.Brush.Style := bsSolid;
|
||||
C.Pen.Color := CLR_BTN_NORM; C.Pen.Style := psSolid;
|
||||
C.RoundRect(FSqlTrack.Left, FSqlTrack.Top, FSqlTrack.Right, FSqlTrack.Bottom, 3, 3);
|
||||
BX := FSqlTrack.Left + Round(FSQLLevel/100.0 * (FSqlTrack.Right - FSqlTrack.Left));
|
||||
if FSQLOn then
|
||||
begin
|
||||
C.Brush.Color := CLR_ACCENT; C.Pen.Color := CLR_ACCENT;
|
||||
C.RoundRect(FSqlTrack.Left, FSqlTrack.Top, BX, FSqlTrack.Bottom, 3, 3);
|
||||
end;
|
||||
C.Brush.Color := CLR_TEXT; C.Pen.Color := CLR_TEXT;
|
||||
C.Ellipse(BX-4, Y + ROW_H div 2 - 5, BX+4, Y + ROW_H div 2 + 5);
|
||||
RegisterHit(Rect(FSqlTrack.Left - 4, Y, FSqlTrack.Right + 4, Y + ROW_H),
|
||||
HIT_SQL_SLIDER);
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TVfoOverlay.DrawAudPanel(C: TCanvas; X, Y, RowW: Integer);
|
||||
@@ -1081,6 +1131,9 @@ begin
|
||||
HIT_BAR_AUD: begin SetPanel(fpAud); Exit; end;
|
||||
HIT_MUTE: begin FMuted := not FMuted; RequestInvalidate;
|
||||
if Assigned(FOnMute) then FOnMute(Self); Exit; end;
|
||||
HIT_SQL: begin FSQLOn := not FSQLOn; RequestInvalidate;
|
||||
if Assigned(FOnSquelchChange) then
|
||||
FOnSquelchChange(FSQLOn, FSQLLevel); Exit; end;
|
||||
HIT_SPLIT: begin if Assigned(FOnSplit) then FOnSplit(Self); Exit; end;
|
||||
HIT_TX: begin if Assigned(FOnTxSelect) then FOnTxSelect(Self); Exit; end;
|
||||
HIT_SLICE_SEL: begin if Assigned(FOnSliceSelect) then FOnSliceSelect(Self); Exit; end;
|
||||
@@ -1176,6 +1229,21 @@ begin
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TVfoOverlay.ApplySqlFromX(LX: Integer);
|
||||
var
|
||||
V: Integer;
|
||||
begin
|
||||
if FSqlTrack.Right <= FSqlTrack.Left then Exit;
|
||||
V := Round((LX - FSqlTrack.Left) / (FSqlTrack.Right - FSqlTrack.Left) * 100);
|
||||
if V < 0 then V := 0 else if V > 100 then V := 100;
|
||||
if V <> FSQLLevel then
|
||||
begin
|
||||
FSQLLevel := V;
|
||||
RequestInvalidate;
|
||||
if Assigned(FOnSquelchChange) then FOnSquelchChange(FSQLOn, FSQLLevel);
|
||||
end;
|
||||
end;
|
||||
|
||||
{ ---- Внешний ввод (маршрутизируется MainForm поверх спектра) ---- }
|
||||
|
||||
function TVfoOverlay.HandleMouseDown(Button: TMouseButton; X, Y: Integer): Boolean;
|
||||
@@ -1200,6 +1268,12 @@ begin
|
||||
Result := True;
|
||||
Exit;
|
||||
end;
|
||||
if FSqlDrag then
|
||||
begin
|
||||
ApplySqlFromX(LX);
|
||||
Result := True;
|
||||
Exit;
|
||||
end;
|
||||
OldHot := FHotIdx;
|
||||
FHotIdx := -1;
|
||||
if (LX >= 0) and (LY >= 0) and (LX < Width) and (LY < Height) then
|
||||
@@ -1218,6 +1292,7 @@ begin
|
||||
Result := False;
|
||||
if not Visible then Exit;
|
||||
if FVolDrag then begin FVolDrag := False; Result := True; Exit; end;
|
||||
if FSqlDrag then begin FSqlDrag := False; Result := True; Exit; end;
|
||||
if (X >= Left) and (Y >= Top) and (X < Left + Width) and (Y < Top + Height) then
|
||||
Result := True; // клик был по накладке — не пускаем его в спектр
|
||||
end;
|
||||
@@ -1226,6 +1301,7 @@ function TVfoOverlay.HandleMouseLeave: Boolean;
|
||||
begin
|
||||
Result := False;
|
||||
FVolDrag := False;
|
||||
FSqlDrag := False;
|
||||
if FHotIdx >= 0 then
|
||||
begin
|
||||
FHotIdx := -1;
|
||||
@@ -1250,6 +1326,11 @@ begin
|
||||
FVolDrag := True;
|
||||
ApplyVolFromX(X);
|
||||
end
|
||||
else if HV = HIT_SQL_SLIDER then
|
||||
begin
|
||||
FSqlDrag := True;
|
||||
ApplySqlFromX(X);
|
||||
end
|
||||
else
|
||||
DoHit(HV);
|
||||
Break;
|
||||
@@ -1298,6 +1379,14 @@ begin
|
||||
RequestInvalidate;
|
||||
end;
|
||||
|
||||
procedure TVfoOverlay.SetSquelchState(AOn: Boolean; ALevel: Integer);
|
||||
begin
|
||||
if (FSQLOn = AOn) and ((FSQLLevel = ALevel) or FSqlDrag) then Exit;
|
||||
FSQLOn := AOn;
|
||||
if not FSqlDrag then FSQLLevel := ALevel; // во время drag ведём сами
|
||||
RequestInvalidate;
|
||||
end;
|
||||
|
||||
procedure TVfoOverlay.SetExtState(AVolume: Integer; AMuted, ASplit, ATx, ATxSel: Boolean);
|
||||
begin
|
||||
if (AVolume = FVolume) and (AMuted = FMuted) and (ASplit = FSplit)
|
||||
|
||||
Reference in New Issue
Block a user