feat(dmr): show stable status on receiver slices

This commit is contained in:
2026-07-14 21:23:24 +03:00
parent 8b28986fec
commit 7c4b024186
3 changed files with 140 additions and 3 deletions
+79 -2
View File
@@ -79,6 +79,11 @@ type
FTxSel: Boolean; // этот слайс выбран для передачи (янтарный)
FSlice: Char; // буква слайса ('A')
FSMeterThreshold: Double; // мин. изменение S-метра (дБ) для перерисовки
FDMRText: string; // стабильный краткий статус рядом с полосой
FDMRSynced: Boolean;
FDMRCandidateText: string; // debounce: не мигать на кратком sync-loss
FDMRCandidateSynced: Boolean;
FDMRCandidateSince: QWord;
FDevNames: TStringList; // имена устройств вывода
FDevIdx: array of Integer; // соответствующие PortAudio-индексы (-1=default)
@@ -182,6 +187,7 @@ type
const CurName: string);
procedure UpdateSMeter(DB: Double);
procedure UpdateVfo(AVfoHz: Double);
procedure UpdateDMRStatus(const AText: string; Synced: Boolean);
// Тема приложения (цвета кнопок берутся отсюда — как левая панель).
procedure SetTheme(const T: TAppTheme);
// Привязка к слайсу: id, буква флага, признак главного (слайс A).
@@ -450,6 +456,9 @@ begin
FSQLOn := False; FSQLLevel := 40; FSqlDrag := False;
FVolume := 70; FMuted := False; FSplit := False; FTx := False;
FTxSel := True; FSlice := 'A';
FDMRText := ''; FDMRSynced := False;
FDMRCandidateText := ''; FDMRCandidateSynced := False;
FDMRCandidateSince := 0;
FSliceId := 0; FIsMain := True; // по умолчанию — главный приёмник (слайс A)
FSMeterThreshold := 0.4; // главный флаг — чувствительный метр
FPanel := fpNone;
@@ -977,8 +986,8 @@ end;
procedure TVfoOverlay.DrawSelf(C: TCanvas; W, H: Integer);
var
FreqStr, SStr, BWStr: string;
CurY, X, RW, i: Integer;
FreqStr, SStr, BWStr, DMRStr: string;
CurY, X, RW, i, DMRX, DMRRight: Integer;
R: TRect;
BarL, BarW, MeterX: Integer;
begin
@@ -1013,6 +1022,21 @@ begin
C.Font.Name := 'Courier New'; C.Font.Size := 8; C.Font.Style := [];
C.Font.Color := CLR_DIM; C.Brush.Style := bsClear;
C.TextOut(X + 22, CurY + 1, BWStr);
// Краткий статус дополнительного DMR-слайса. Он обновляется только при
// устойчивом изменении sync/slot/CC/TG, поэтому не заставляет GL-текстуру
// перезаливаться на каждом burst.
if (not FIsMain) and (FMode = 11) and (FDMRText <> '') then
begin
DMRStr := FDMRText;
DMRX := X + 22 + C.TextWidth(BWStr) + 8;
DMRRight := W - PAD - 26 - 6 - 14 - 5; // до кнопок × и TX
while (Length(DMRStr) > 1) and
(C.TextWidth(DMRStr) > DMRRight - DMRX) do
SetLength(DMRStr, Length(DMRStr) - 1);
if FDMRSynced then C.Font.Color := CLR_ACCENT
else C.Font.Color := CLR_SVAL;
C.TextOut(DMRX, CurY + 1, DMRStr);
end;
// TX badge: a DMR slice is visibly disabled and has no hit target.
R := Rect(W - PAD - 26, CurY, W - PAD, CurY + HDR_BADGE_H);
if not TxSelectionAllowed then
@@ -1405,12 +1429,29 @@ end;
{ ---- Обновление состояния из контроллера ---- }
procedure TVfoOverlay.SetState(AMode, ABW: Integer; AVfoHz, ASMeterDB: Double);
var
OldMode: Integer;
begin
OldMode := FMode;
FMode := Max(0, Min(OVL_MODE_COUNT - 1, AMode));
FFilterBW := ABW;
FModeFilter[FMode] := ABW;
FVfoHz := AVfoHz;
FSMeterDB := ASMeterDB;
if FMode <> 11 then
begin
FDMRText := '';
FDMRSynced := False;
FDMRCandidateText := '';
FDMRCandidateSince := 0;
end
else if OldMode <> 11 then
begin
FDMRText := 'DMR...';
FDMRSynced := False;
FDMRCandidateText := '';
FDMRCandidateSince := 0;
end;
Height := ComputeHeight;
RequestInvalidate;
end;
@@ -1487,4 +1528,40 @@ begin
end;
end;
procedure TVfoOverlay.UpdateDMRStatus(const AText: string; Synced: Boolean);
var
NowTick, HoldMs: QWord;
begin
if FMode <> 11 then Exit;
if (FDMRText = AText) and (FDMRSynced = Synced) then
begin
FDMRCandidateText := '';
FDMRCandidateSince := 0;
Exit;
end;
NowTick := GetTickCount64;
if (FDMRCandidateText <> AText) or
(FDMRCandidateSynced <> Synced) then
begin
FDMRCandidateText := AText;
FDMRCandidateSynced := Synced;
FDMRCandidateSince := NowTick;
Exit;
end;
// Потерю уже показанной синхронизации держим дольше, чем появление/смену
// параметров вызова: короткие замирания не должны мигать в шапке слайса.
if FDMRSynced and not Synced then HoldMs := 1200
else if FDMRSynced and Synced then HoldMs := 750
else HoldMs := 250;
if NowTick - FDMRCandidateSince < HoldMs then Exit;
FDMRText := AText;
FDMRSynced := Synced;
FDMRCandidateText := '';
FDMRCandidateSince := 0;
RequestInvalidate;
end;
end.