feat(overlay): redesign VfoOverlay in SmartSDR slice-flag style

Полный редизайн накладки поверх спектра: шапка (слайс-бейдж A/B, ширина
фильтра, кликабельный SPLIT, индикатор TX), метр-ряд (индикаторы NR/NB/ANF/
SNB + сигнал-бар), крупная частота + S, нижний бар с fly-out пикерами.

- Fly-out движок MODE/DSP/AUD (раскрываются под баром, карточка растёт):
  MODE — сетка режимов 4×2 + фильтры; DSP — NR/NB/SNB/ANF + AGC; AUD —
  выбор аудио-устройства вывода (задел под слайсы).
- Слайдер громкости (click+drag) + mute; SPLIT-toggle; live TX-индикатор.
- Все контролы подключены к RadioController (SetVolume/ToggleMute/SetSplit/
  EnumOutputDevices→ApplyAudioDevice); wire в MainForm + HandleMouseUp, чтобы
  клик по накладке не уходил в спектр.
- FormatFreq на Int64 (double накапливал ошибку в десятки Гц на ~10 ГГц QO-100).
- S-метр ниже по высоте + плавный градиент по уровню dBm: тёмно-зелёный→
  зелёный→жёлтый→красный (красный к ~S9+18).
- Кэш+dirty рендер сохранён (CPU-паритет со старой накладкой), GL-путь через
  DrawOverlayBitmap.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-01 14:00:59 +03:00
co-authored by Claude Opus 4.8
parent 5b7eda291e
commit 4a92256e72
2 changed files with 734 additions and 406 deletions
+70 -2
View File
@@ -519,6 +519,12 @@ type
procedure OnModeFilterSelect(Mode: Integer; FilterBW: Integer); procedure OnModeFilterSelect(Mode: Integer; FilterBW: Integer);
procedure OnVfoOverlayDSPChange(NRMode, NBMode: Integer; SNBOn, ANFOn: Boolean); procedure OnVfoOverlayDSPChange(NRMode, NBMode: Integer; SNBOn, ANFOn: Boolean);
procedure OnVfoOverlayAGCChange(AGCMode: Integer); procedure OnVfoOverlayAGCChange(AGCMode: Integer);
procedure OnVfoOverlayVolume(Vol: Integer);
procedure OnVfoOverlayMute(Sender: TObject);
procedure OnVfoOverlayAudioDevice(DevIndex: Integer; const DevName: string);
procedure OnVfoOverlaySplit(Sender: TObject);
procedure PushVfoOverlayExtState;
procedure PushVfoOverlayAudioDevices;
procedure OnVfoOverlayInvalidate(Sender: TObject); procedure OnVfoOverlayInvalidate(Sender: TObject);
procedure PbSpectrumDblClick(Sender: TObject); procedure PbSpectrumDblClick(Sender: TObject);
procedure TrkDriveChange(Sender: TObject); procedure TrkDriveChange(Sender: TObject);
@@ -956,6 +962,10 @@ begin
FVfoOverlay.OnSelect := OnModeFilterSelect; FVfoOverlay.OnSelect := OnModeFilterSelect;
FVfoOverlay.OnDSPChange := OnVfoOverlayDSPChange; FVfoOverlay.OnDSPChange := OnVfoOverlayDSPChange;
FVfoOverlay.OnAGCChange := OnVfoOverlayAGCChange; FVfoOverlay.OnAGCChange := OnVfoOverlayAGCChange;
FVfoOverlay.OnVolume := OnVfoOverlayVolume;
FVfoOverlay.OnMute := OnVfoOverlayMute;
FVfoOverlay.OnAudioDevice := OnVfoOverlayAudioDevice;
FVfoOverlay.OnSplit := OnVfoOverlaySplit;
FVfoOverlay.OnInvalidate := OnVfoOverlayInvalidate; FVfoOverlay.OnInvalidate := OnVfoOverlayInvalidate;
FVfoOverlay.Visible := False; FVfoOverlay.Visible := False;
FPanelHidden := False; FPanelHidden := False;
@@ -3251,6 +3261,7 @@ begin
begin begin
FVfoOverlay.UpdateSMeter(FController.FLastSMeter); FVfoOverlay.UpdateSMeter(FController.FLastSMeter);
FVfoOverlay.UpdateVfo(FController.FVfoA); FVfoOverlay.UpdateVfo(FController.FVfoA);
PushVfoOverlayExtState;
PositionVfoOverlay; PositionVfoOverlay;
end; end;
// Панорама и водопад теперь рисуются независимо по факту новых кадров. // Панорама и водопад теперь рисуются независимо по факту новых кадров.
@@ -4501,6 +4512,12 @@ end;
procedure TMainForm.PbSpectrumMouseUp(Sender: TObject; Button: TMouseButton; procedure TMainForm.PbSpectrumMouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer); Shift: TShiftState; X, Y: Integer);
begin begin
if Assigned(FVfoOverlay) and FVfoOverlay.HandleMouseUp(X, Y) then
begin
FSpecDrag := False;
PbSpectrum.Cursor := crDefault;
Exit; // клик/drag по накладке — не пускаем в спектр
end;
if Button = mbLeft then if Button = mbLeft then
begin begin
if (Abs(X - FSpecDragX0) < 4) then if (Abs(X - FSpecDragX0) < 4) then
@@ -4989,11 +5006,13 @@ begin
begin begin
FLeftPanelW := PanelLeft.Width; // сохраняем реальную ширину (учитывает DPI на Win32) FLeftPanelW := PanelLeft.Width; // сохраняем реальную ширину (учитывает DPI на Win32)
PanelLeft.Width := 0; PanelLeft.Width := 0;
FVfoOverlay.Width := 260; FVfoOverlay.Width := OVL_W;
FVfoOverlay.Height := 136; // OVL_H_NORM — расширяется сам при открытии AGC-пикера FVfoOverlay.Height := FVfoOverlay.BaseHeight; // растёт сам при открытии fly-out
FVfoOverlay.SetState(FController.FMode, FController.FFilterBW, FController.FVfoA, FController.FLastSMeter); FVfoOverlay.SetState(FController.FMode, FController.FFilterBW, FController.FVfoA, FController.FLastSMeter);
FVfoOverlay.SetDSPState(FController.FNRMode, FController.FNBMode, FController.FSNB, FController.FANF, FController.FAGCMode); FVfoOverlay.SetDSPState(FController.FNRMode, FController.FNBMode, FController.FSNB, FController.FANF, FController.FAGCMode);
FVfoOverlay.Visible := True; FVfoOverlay.Visible := True;
PushVfoOverlayExtState;
PushVfoOverlayAudioDevices;
PositionVfoOverlay; PositionVfoOverlay;
end end
else else
@@ -5098,6 +5117,55 @@ begin
FController.SetAGCMode(AGCMode); FController.SetAGCMode(AGCMode);
end; end;
procedure TMainForm.OnVfoOverlayVolume(Vol: Integer);
begin
FController.SetVolume(Vol);
end;
procedure TMainForm.OnVfoOverlayMute(Sender: TObject);
begin
FController.ToggleMute;
end;
procedure TMainForm.OnVfoOverlaySplit(Sender: TObject);
begin
FController.SetSplit(not FController.FSplitTxB);
PushVfoOverlayExtState;
end;
procedure TMainForm.OnVfoOverlayAudioDevice(DevIndex: Integer; const DevName: string);
begin
ApplyAudioDevice(DevIndex, DevName);
PushVfoOverlayAudioDevices; // обновить подсветку текущего устройства
end;
procedure TMainForm.PushVfoOverlayExtState;
begin
if not (Assigned(FVfoOverlay) and FVfoOverlay.Visible) then Exit;
FVfoOverlay.SetExtState(FController.FVolume, FController.FMuted,
FController.FSplitTxB, FController.FTransmitting);
end;
procedure TMainForm.PushVfoOverlayAudioDevices;
var
Names: TStringList;
Indices: array[0..63] of Integer;
Cnt, I: Integer;
Idx: array of Integer;
begin
if not (Assigned(FVfoOverlay) and Assigned(FController.FAudioOut)) then Exit;
Names := TStringList.Create;
try
Cnt := 0;
FController.FAudioOut.EnumOutputDevices(Names, Indices, Cnt);
SetLength(Idx, Cnt);
for I := 0 to Cnt - 1 do Idx[I] := Indices[I];
FVfoOverlay.SetAudioDevices(Names, Idx, FController.FAudioOutDevName);
finally
Names.Free;
end;
end;
procedure TMainForm.OnVfoOverlayInvalidate(Sender: TObject); procedure TMainForm.OnVfoOverlayInvalidate(Sender: TObject);
begin begin
FSpectrumDirty := True; FSpectrumDirty := True;
+641 -381
View File
File diff suppressed because it is too large Load Diff