mirror of
https://git.vladimir.cc/vladimir/ewsdr.git
synced 2026-08-25 17:27:32 +00:00
feat(slices): per-pan tuning wheel and per-slice RX mute on TX
Mouse wheel over an extra panadapter now retunes that pan's primary slice (first created on that receiver) instead of the main VFO; a slice flag under the cursor still wins, pan 0 keeps driving the VFO. Slice flags get a clickable RXM badge: RX audio of the slice is muted while transmitting (default), so the operator no longer hears himself on every slice in full duplex. State persists with the workspace. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+31
-8
@@ -77,6 +77,7 @@ type
|
||||
FSplit: Boolean;
|
||||
FTx: Boolean; // передача идёт (красный)
|
||||
FTxSel: Boolean; // этот слайс выбран для передачи (янтарный)
|
||||
FRxMuteTx: Boolean; // RXM: приём слайса глушится на передаче (умолч. True)
|
||||
FSlice: Char; // буква слайса ('A')
|
||||
FSMeterThreshold: Double; // мин. изменение S-метра (дБ) для перерисовки
|
||||
FDMRText: string; // стабильный краткий статус рядом с полосой
|
||||
@@ -105,6 +106,7 @@ type
|
||||
FOnAudioDevice: TAudioDeviceEvent;
|
||||
FOnSplit: TNotifyEvent;
|
||||
FOnTxSelect: TNotifyEvent;
|
||||
FOnRxMuteTx: TNotifyEvent;
|
||||
FOnInvalidate: TNotifyEvent;
|
||||
|
||||
FTheme: TAppTheme; // тема приложения (цвета кнопок и т.п.)
|
||||
@@ -183,6 +185,7 @@ type
|
||||
AAGCMode: Integer);
|
||||
procedure SetSquelchState(AOn: Boolean; ALevel: Integer);
|
||||
procedure SetExtState(AVolume: Integer; AMuted, ASplit, ATx, ATxSel: Boolean);
|
||||
procedure SetRxMuteTxState(AOn: Boolean); // бейдж RXM (self-monitor на TX)
|
||||
procedure SetAudioDevices(Names: TStrings; const Indices: array of Integer;
|
||||
const CurName: string);
|
||||
procedure UpdateSMeter(DB: Double);
|
||||
@@ -218,6 +221,7 @@ type
|
||||
property OnAudioDevice: TAudioDeviceEvent read FOnAudioDevice write FOnAudioDevice;
|
||||
property OnSplit: TNotifyEvent read FOnSplit write FOnSplit;
|
||||
property OnTxSelect: TNotifyEvent read FOnTxSelect write FOnTxSelect;
|
||||
property OnRxMuteTx: TNotifyEvent read FOnRxMuteTx write FOnRxMuteTx;
|
||||
property OnSliceSelect: TNotifyEvent read FOnSliceSelect write FOnSliceSelect;
|
||||
property OnClose: TNotifyEvent read FOnClose write FOnClose;
|
||||
property OnInvalidate: TNotifyEvent read FOnInvalidate write FOnInvalidate;
|
||||
@@ -258,9 +262,10 @@ const
|
||||
HIT_CLOSE = -30; // × закрытия слайса (неглавные)
|
||||
HIT_SQL = -31; // тумблер FM-squelch (в DSP-панели)
|
||||
HIT_SQL_SLIDER = -32; // трек порога FM-squelch
|
||||
HIT_RXMUTE = -33; // бейдж RXM: приём слайса на своей передаче
|
||||
HIT_AGC_PICK_BASE = 100000;
|
||||
HIT_DEV_BASE = 200000;
|
||||
// Свои диапазоны, чтобы не пересекаться с тумблерами (-9..-32) и с базами выше.
|
||||
// Свои диапазоны, чтобы не пересекаться с тумблерами (-9..-33) и с базами выше.
|
||||
HIT_MODE_BASE = -1000; // -1000 .. -1000-(OVL_MODE_COUNT-1)
|
||||
HIT_FILT_BASE = 1000; // 1000 + индекс кнопки в ряду фильтров
|
||||
|
||||
@@ -455,7 +460,7 @@ begin
|
||||
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';
|
||||
FTxSel := True; FSlice := 'A'; FRxMuteTx := True;
|
||||
FDMRText := ''; FDMRSynced := False;
|
||||
FDMRCandidateText := ''; FDMRCandidateSynced := False;
|
||||
FDMRCandidateSince := 0;
|
||||
@@ -987,7 +992,7 @@ end;
|
||||
procedure TVfoOverlay.DrawSelf(C: TCanvas; W, H: Integer);
|
||||
var
|
||||
FreqStr, SStr, BWStr, DMRStr: string;
|
||||
CurY, X, RW, i, DMRX, DMRRight: Integer;
|
||||
CurY, X, RW, i, DMRX, DMRRight, TxRight: Integer;
|
||||
R: TRect;
|
||||
BarL, BarW, MeterX: Integer;
|
||||
begin
|
||||
@@ -1005,16 +1010,25 @@ begin
|
||||
RW := W - PAD*2;
|
||||
CurY := PAD;
|
||||
|
||||
{ --- Шапка: главный [A] BW ... SPLIT [TX]; слайс [B] BW ... [×] [TX] --- }
|
||||
{ --- Шапка: главный [A] BW ... SPLIT [TX]; слайс [B] BW ... [RXM] [TX] [×] --- }
|
||||
DrawBadge(C, Rect(X, CurY, X+16, CurY+HDR_BADGE_H), FSlice, SliceColor(FSlice), CLR_BADGE_TXT);
|
||||
RegisterHit(Rect(X, CurY, X+16, CurY+HDR_BADGE_H), HIT_SLICE_SEL); // бейдж = выбор слайса
|
||||
// Кнопка закрытия × — только у неглавных слайсов (главный приёмник не закрыть).
|
||||
// Место освободилось от SPLIT (его у слайсов нет) — ставим вплотную к TX.
|
||||
// Она крайняя справа, TX и RXM выстраиваются левее неё.
|
||||
TxRight := W - PAD;
|
||||
if not FIsMain then
|
||||
begin
|
||||
R := Rect(W - PAD - 26 - 6 - 14, CurY, W - PAD - 26 - 6, CurY + HDR_BADGE_H);
|
||||
R := Rect(W - PAD - 14, CurY, W - PAD, CurY + HDR_BADGE_H);
|
||||
DrawBadge(C, R, #$C3#$97, CLR_BTN_NORM, CLR_DIM); // UTF-8 '×'
|
||||
RegisterHit(R, HIT_CLOSE);
|
||||
TxRight := W - PAD - 14 - 6;
|
||||
// RXM — приём слайса на СВОЕЙ передаче. Подсвечен (умолч.) = глушим, т.е.
|
||||
// себя не слышим; погашен = self-monitor (свой downlink через транспондер).
|
||||
// У главного приёмника то же самое делает кнопка RX MUTE на левой панели.
|
||||
R := Rect(TxRight - 26 - 4 - 32, CurY, TxRight - 26 - 4, CurY + HDR_BADGE_H);
|
||||
if FRxMuteTx then DrawBadge(C, R, 'RXM', CLR_ACCENT, CLR_BADGE_TXT)
|
||||
else DrawBadge(C, R, 'RXM', CLR_BTN_NORM, CLR_DIM);
|
||||
RegisterHit(R, HIT_RXMUTE);
|
||||
end;
|
||||
// ширина фильтра
|
||||
if FFilterBW >= 1000 then BWStr := Format('%.1fk', [FFilterBW/1000.0])
|
||||
@@ -1029,7 +1043,7 @@ begin
|
||||
begin
|
||||
DMRStr := FDMRText;
|
||||
DMRX := X + 22 + C.TextWidth(BWStr) + 8;
|
||||
DMRRight := W - PAD - 26 - 6 - 14 - 5; // до кнопок × и TX
|
||||
DMRRight := TxRight - 26 - 4 - 32 - 5; // до бейджей RXM, TX и ×
|
||||
while (Length(DMRStr) > 1) and
|
||||
(C.TextWidth(DMRStr) > DMRRight - DMRX) do
|
||||
SetLength(DMRStr, Length(DMRStr) - 1);
|
||||
@@ -1038,7 +1052,7 @@ begin
|
||||
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);
|
||||
R := Rect(TxRight - 26, CurY, TxRight, CurY + HDR_BADGE_H);
|
||||
if not TxSelectionAllowed then
|
||||
DrawBadge(C, R, 'TX', CLR_CARD_BG, CLR_DIM)
|
||||
else if FTx then DrawBadge(C, R, 'TX', CLR_TX_ON, clWhite)
|
||||
@@ -1205,6 +1219,8 @@ begin
|
||||
FOnTxSelect(Self);
|
||||
Exit;
|
||||
end;
|
||||
HIT_RXMUTE: begin FRxMuteTx := not FRxMuteTx; RequestInvalidate;
|
||||
if Assigned(FOnRxMuteTx) then FOnRxMuteTx(Self); Exit; end;
|
||||
HIT_SLICE_SEL: begin if Assigned(FOnSliceSelect) then FOnSliceSelect(Self); Exit; end;
|
||||
HIT_CLOSE: begin if Assigned(FOnClose) then FOnClose(Self); Exit; end;
|
||||
HIT_AUD_UP: begin if FAudScroll > 0 then Dec(FAudScroll);
|
||||
@@ -1487,6 +1503,13 @@ begin
|
||||
RequestInvalidate;
|
||||
end;
|
||||
|
||||
procedure TVfoOverlay.SetRxMuteTxState(AOn: Boolean);
|
||||
begin
|
||||
if FRxMuteTx = AOn then Exit;
|
||||
FRxMuteTx := AOn;
|
||||
RequestInvalidate;
|
||||
end;
|
||||
|
||||
procedure TVfoOverlay.SetSliceInfo(ASliceId: Integer; ALetter: Char; AIsMain: Boolean);
|
||||
begin
|
||||
if (FSliceId = ASliceId) and (FSlice = ALetter) and (FIsMain = AIsMain) then Exit;
|
||||
|
||||
Reference in New Issue
Block a user