feat(slices): выбор КВ-диапазона на панах N (Flex-style band-селектор)

Кликабельный бейдж диапазона («20m») в шапке пана N → popup-меню КВ-бандов.
На выбор — по-флексовому: ретюн DDC на VfoA диапазона (из общего band-кэша,
как пан 0), активный приёмник садится на VfoA с mode/filter/AGC + zoom/pan
этого диапазона, прочие слайсы пана убираются (вне новой полосы). Только на
настоящих КВ (не XVTR, не Pluto).

- TPanafallPanel: FHeaderBandLabel + OnBandClick/BandClickable/SetHeaderBand;
  каскад left-лейблов шапки вынесен в LayoutHeaderLabels (band/rate/adc без
  дыр при скрытии).
- MainForm: OnPanBandClick/PanBandMenuItemClick/ApplyPanBand +
  PanBandSelectorVisible (гейт КВ); band-лейбл в UpdatePanHeaders (по
  FreqToBandIdx частоты DDC), тема в ApplyPanHeaderTheme.

Ограничение: per-slice AGC-Top в движке нет — подтягиваются
mode/filter/AGC-mode/zoom/pan, но не индивидуальный AGC-Top диапазона.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-13 14:27:17 +03:00
co-authored by Claude Opus 4.8
parent 3566f10191
commit 3ee996154a
2 changed files with 192 additions and 20 deletions
+120 -1
View File
@@ -202,6 +202,8 @@ type
// Меню rate пана («NNN kHz» в шапке панов N; создаётся лениво).
FPanRateMenu: TPopupMenu;
FPanRateMenuPanId: Integer;
FPanBandMenu: TPopupMenu; // селектор диапазона пана N (3.6, КВ)
FPanBandMenuPanId: Integer;
// Pop-out (3.5): пан N в отдельном OS-окне. nil = пан в стеке.
FPanFloatForm: array[1..MAX_PANS-1] of TForm;
FSpecView: TSpectrumView;
@@ -604,6 +606,10 @@ type
procedure UpdateAddPanButton;
procedure OnPanRateClick(Sender: TObject); // «NNN kHz» шапки пана N
procedure PanRateMenuItemClick(Sender: TObject);
procedure OnPanBandClick(Sender: TObject); // «20m» шапки пана N (3.6)
procedure PanBandMenuItemClick(Sender: TObject);
procedure ApplyPanBand(P: TPanafallPanel; BandIdx: Integer); // Flex-style смена банда пана
function PanBandSelectorVisible: Boolean; // КВ (не XVTR, не Pluto)
procedure OnPanADCClick(Sender: TObject); // бейдж «RX1/RX2» (3.4)
// Per-pan дисплей (3.6): «◑» → поповер палитры/gamma/уровней.
procedure OnPanDisplayClick(Sender: TObject);
@@ -5638,6 +5644,95 @@ begin
MarkPanDirty(P);
end;
function TMainForm.PanBandSelectorVisible: Boolean;
// Селектор диапазона пана — только на «настоящих КВ»: не в XVTR и не Pluto
// (у Pluto/трансвертера band-стек другой, сценарий не актуален).
begin
Result := (FController.FCurrentXvtr < 0) and not FController.IsPluto;
end;
procedure TMainForm.OnPanBandClick(Sender: TObject);
// Клик по бейджу диапазона в шапке пана N: меню КВ-диапазонов, текущий (по
// частоте DDC пана) отмечен галкой.
var
P: TPanafallPanel;
MI: TMenuItem;
i, CurBand: Integer;
begin
if not (Sender is TPanafallPanel) then Exit;
P := TPanafallPanel(Sender);
if (P.PanId < 1) or not FController.PanDDCActive(P.PanId) then Exit;
if not PanBandSelectorVisible then Exit;
CurBand := FreqToBandIdx(FController.PanDDCFreq(P.PanId), False);
if FPanBandMenu = nil then
FPanBandMenu := TPopupMenu.Create(Self);
FPanBandMenu.Items.Clear;
for i := 0 to CFG_BAND_COUNT - 1 do
begin
if BandName(i, False) = '' then Continue; // 6m-дубль/пустые слоты
MI := TMenuItem.Create(FPanBandMenu);
MI.Caption := BandName(i, False);
MI.Tag := i;
MI.Checked := i = CurBand;
MI.OnClick := PanBandMenuItemClick;
FPanBandMenu.Items.Add(MI);
end;
if FPanBandMenu.Items.Count = 0 then Exit;
FPanBandMenuPanId := P.PanId;
FPanBandMenu.PopUp;
end;
procedure TMainForm.PanBandMenuItemClick(Sender: TObject);
var PanId: Integer;
begin
PanId := FPanBandMenuPanId;
FPanBandMenuPanId := 0;
if (PanId < 1) or (PanId >= MAX_PANS) then Exit;
if FPans[PanId] = nil then Exit;
ApplyPanBand(FPans[PanId], TMenuItem(Sender).Tag);
end;
procedure TMainForm.ApplyPanBand(P: TPanafallPanel; BandIdx: Integer);
// Flex-style смена диапазона пана N: ретюн DDC на VfoA диапазона (из общего
// band-кэша, как пан 0), активный приёмник садится на VfoA с mode/filter/AGC
// (+zoom/pan) диапазона, прочие слайсы пана убираются (они вне новой полосы).
var
B: TBandSettings;
Freq: Double;
Lo, Hi, Id: Integer;
begin
if (P = nil) or (P.PanId < 1) then Exit;
if not PanBandSelectorVisible then Exit;
if (BandIdx < 0) or (BandIdx >= CFG_BAND_COUNT) then Exit;
B := FController.FBandCache[BandIdx];
Freq := B.VfoA;
// Кэш диапазона мог нести чужую частоту (дефолты/битый конфиг) — тогда центр
// диапазона по плану.
if FreqToBandIdx(Freq, False) <> BandIdx then
Freq := BandDefaultFreq(BandIdx, False);
// Слайсы вне новой полосы — снести до ретюна DDC.
P.DestroyAllSliceFlags;
FActiveSliceId := 0;
// Ретюн DDC пана + зум/пан диапазона.
FController.SetPanDDCFreq(P.PanId, Freq);
FController.SetPanZoomPan(P.PanId,
EnsureRange(B.ZoomFactor, 0.0, 1.0), EnsureRange(B.PanSlider, 0.0, 1.0));
// Один приёмник на VfoA с настройками диапазона.
TRadioController.FilterEdgesFor(B.Mode, B.FilterBW, Lo, Hi);
Id := FController.AddSlice(Freq, B.Mode, Lo, Hi,
TRadioController.AGCModeFromUI(B.AGCMode), 0.7, -1, '', P.PanId);
if Id >= 0 then
begin
P.CreateSliceFlag(Id);
FActiveSliceId := Id;
end;
// Синк вьюхи/зум-бара/флагов/шапки под новый центр.
P.PushAllSliceFlagStates;
RefreshPanZoom(P);
UpdatePanHeaders;
end;
procedure TMainForm.OnPanADCClick(Sender: TObject);
// Бейдж «RX1/RX2»: тумблер ADC-источника пана (двух-АЦПшные платы, 3.4).
var
@@ -5934,6 +6029,14 @@ begin
P.HeaderRateLabel.Font.Color := T.BtnTextActive
else
P.HeaderRateLabel.Font.Color := T.Text;
// Band-лейбл (3.6): кликабельный — акцент селектора.
if P.HeaderBandLabel <> nil then
begin
if P.BandClickable then
P.HeaderBandLabel.Font.Color := T.BtnTextActive
else
P.HeaderBandLabel.Font.Color := T.Text;
end;
// ADC-бейдж кликабелен на 2-АЦПшных платах — тоже акцент.
if P.ADCClickable then
P.HeaderADCLabel.Font.Color := T.BtnTextActive
@@ -5945,7 +6048,7 @@ procedure TMainForm.UpdatePanHeaders;
// Текст/видимость шапок: у одного пана шапки нет, у стека — у всех;
// у плавающего (pop-out) пана шапка всегда (там его rate/ADC/×).
var
i, N, NADC: Integer;
i, N, NADC, bi: Integer;
P: TPanafallPanel;
begin
N := PanCount;
@@ -5972,6 +6075,21 @@ begin
[i, FController.PanDDCFreq(i) / 1e6]));
P.SetHeaderRate(Format('%d kHz', [FController.PanDDCRateKHz(i)]));
end;
// Бейдж диапазона (3.6): только паны N на «настоящих КВ»; клик = меню.
if (i > 0) and PanBandSelectorVisible then
begin
bi := FreqToBandIdx(FController.PanDDCFreq(i), False);
if (bi >= 0) and (BandName(bi, False) <> '') then
P.SetHeaderBand(BandName(bi, False))
else
P.SetHeaderBand('—');
P.BandClickable := True;
end
else
begin
P.SetHeaderBand('');
P.BandClickable := False;
end;
// Бейдж АЦП: только на двух-АЦПшных платах; клик = тумблер.
if NADC > 1 then
begin
@@ -6027,6 +6145,7 @@ begin
P.OnADCClick := OnPanADCClick; // «RX1/RX2» тумблер АЦП (3.4)
P.OnPanPopOut := OnPanPopOutClick; // «⧉» в отдельное окно (3.5)
P.OnDisplayClick := OnPanDisplayClick; // «◑» дисплей-поповер (3.6)
P.OnBandClick := OnPanBandClick; // «20m» селектор диапазона (3.6, КВ)
P.SpectrumMouseDown := PanNSpectrumMouseDown;
P.SpectrumMouseMove := PanNSpectrumMouseMove;
P.SpectrumMouseUp := PanNSpectrumMouseUp;