mirror of
https://git.vladimir.cc/vladimir/ewsdr.git
synced 2026-08-25 20:37:33 +00:00
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:
+120
-1
@@ -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;
|
||||
|
||||
+72
-19
@@ -98,6 +98,9 @@ type
|
||||
FHeaderRateLabel: TLabel; // «NNN kHz»; у панов N>0 кликабелен (селектор rate)
|
||||
FRateClickable: Boolean;
|
||||
FOnRateClick: TNotifyEvent;
|
||||
FHeaderBandLabel: TLabel; // «20m»; кликабелен на КВ (селектор диапазона пана, 3.6)
|
||||
FBandClickable: Boolean;
|
||||
FOnBandClick: TNotifyEvent;
|
||||
FHeaderADCLabel: TLabel; // «RX1»/«RX2»; кликабелен при 2 АЦП (тумблер, 3.4)
|
||||
FADCClickable: Boolean;
|
||||
FOnADCClick: TNotifyEvent;
|
||||
@@ -122,6 +125,9 @@ type
|
||||
procedure BtnAddPanClick(Sender: TObject);
|
||||
procedure HeaderRateClick(Sender: TObject);
|
||||
procedure SetRateClickable(AValue: Boolean);
|
||||
procedure HeaderBandClick(Sender: TObject);
|
||||
procedure SetBandClickable(AValue: Boolean);
|
||||
procedure LayoutHeaderLabels; // каскад left-лейблов (band/rate/adc)
|
||||
procedure HeaderADCClick(Sender: TObject);
|
||||
procedure SetADCClickable(AValue: Boolean);
|
||||
procedure BtnPopPanClick(Sender: TObject);
|
||||
@@ -210,6 +216,12 @@ type
|
||||
procedure SetHeaderRate(const S: string);
|
||||
property RateClickable: Boolean read FRateClickable write SetRateClickable;
|
||||
property OnRateClick: TNotifyEvent read FOnRateClick write FOnRateClick;
|
||||
// Бейдж диапазона «20m» (3.6, только КВ-паны N): пустой текст = скрыт,
|
||||
// клик = меню КВ-диапазонов (хозяин).
|
||||
procedure SetHeaderBand(const S: string);
|
||||
property BandClickable: Boolean read FBandClickable write SetBandClickable;
|
||||
property OnBandClick: TNotifyEvent read FOnBandClick write FOnBandClick;
|
||||
property HeaderBandLabel: TLabel read FHeaderBandLabel;
|
||||
// Бейдж ADC-источника «RX1»/«RX2» (3.4): показывается при NumADCs>1,
|
||||
// клик = тумблер АЦП (хозяин).
|
||||
procedure SetHeaderADC(const S: string);
|
||||
@@ -439,6 +451,12 @@ begin
|
||||
FHeaderRateLabel.Parent := FHeaderPanel;
|
||||
FHeaderRateLabel.AutoSize := True;
|
||||
FHeaderRateLabel.OnClick := HeaderRateClick;
|
||||
// Band-бейдж «20m» (3.6): между текстом пана и rate; пустой = скрыт.
|
||||
FHeaderBandLabel := TLabel.Create(Self);
|
||||
FHeaderBandLabel.Parent := FHeaderPanel;
|
||||
FHeaderBandLabel.AutoSize := True;
|
||||
FHeaderBandLabel.Visible := False;
|
||||
FHeaderBandLabel.OnClick := HeaderBandClick;
|
||||
// ADC-бейдж «RX1»/«RX2» (3.4): пустой текст = скрыт (одноАЦПшные платы).
|
||||
FHeaderADCLabel := TLabel.Create(Self);
|
||||
FHeaderADCLabel.Parent := FHeaderPanel;
|
||||
@@ -483,24 +501,63 @@ begin
|
||||
if Assigned(FOnAddPan) then FOnAddPan(Self);
|
||||
end;
|
||||
|
||||
procedure TPanafallPanel.LayoutHeaderLabels;
|
||||
// Единый каскад left-лейблов шапки: [текст пана] [band?] [rate] [adc?].
|
||||
// Скрытые (Visible=False) лейблы пропускаются, чтобы не оставлять дыр.
|
||||
var X: Integer;
|
||||
begin
|
||||
if FHeaderLabel = nil then Exit;
|
||||
X := FHeaderLabel.Left + FHeaderLabel.Width + 6;
|
||||
if (FHeaderBandLabel <> nil) and FHeaderBandLabel.Visible then
|
||||
begin
|
||||
FHeaderBandLabel.Left := X;
|
||||
Inc(X, FHeaderBandLabel.Width + 10);
|
||||
end;
|
||||
if FHeaderRateLabel <> nil then
|
||||
begin
|
||||
FHeaderRateLabel.Left := X;
|
||||
Inc(X, FHeaderRateLabel.Width + 10);
|
||||
end;
|
||||
if FHeaderADCLabel <> nil then
|
||||
FHeaderADCLabel.Left := X;
|
||||
end;
|
||||
|
||||
procedure TPanafallPanel.SetHeaderText(const S: string);
|
||||
begin
|
||||
if FHeaderLabel = nil then Exit;
|
||||
if FHeaderLabel.Caption = S then Exit;
|
||||
FHeaderLabel.Caption := S;
|
||||
// Rate-лейбл прижат к основному тексту — сдвинуть за новой шириной.
|
||||
if FHeaderRateLabel <> nil then
|
||||
FHeaderRateLabel.Left := FHeaderLabel.Left + FHeaderLabel.Width + 6;
|
||||
LayoutHeaderLabels; // ширина текста изменилась — сдвинуть хвост
|
||||
end;
|
||||
|
||||
procedure TPanafallPanel.SetHeaderRate(const S: string);
|
||||
begin
|
||||
if FHeaderRateLabel = nil then Exit;
|
||||
if FHeaderRateLabel.Caption <> S then FHeaderRateLabel.Caption := S;
|
||||
if FHeaderLabel <> nil then
|
||||
FHeaderRateLabel.Left := FHeaderLabel.Left + FHeaderLabel.Width + 6;
|
||||
if FHeaderADCLabel <> nil then
|
||||
FHeaderADCLabel.Left := FHeaderRateLabel.Left + FHeaderRateLabel.Width + 10;
|
||||
LayoutHeaderLabels;
|
||||
end;
|
||||
|
||||
procedure TPanafallPanel.SetHeaderBand(const S: string);
|
||||
begin
|
||||
if FHeaderBandLabel = nil then Exit;
|
||||
if FHeaderBandLabel.Caption <> S then FHeaderBandLabel.Caption := S;
|
||||
FHeaderBandLabel.Visible := S <> '';
|
||||
LayoutHeaderLabels;
|
||||
end;
|
||||
|
||||
procedure TPanafallPanel.SetBandClickable(AValue: Boolean);
|
||||
begin
|
||||
FBandClickable := AValue;
|
||||
if FHeaderBandLabel <> nil then
|
||||
begin
|
||||
if AValue then FHeaderBandLabel.Cursor := crHandPoint
|
||||
else FHeaderBandLabel.Cursor := crArrow;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TPanafallPanel.HeaderBandClick(Sender: TObject);
|
||||
begin
|
||||
if FBandClickable and Assigned(FOnBandClick) then FOnBandClick(Self);
|
||||
end;
|
||||
|
||||
procedure TPanafallPanel.SetRateClickable(AValue: Boolean);
|
||||
@@ -525,8 +582,7 @@ begin
|
||||
if FHeaderADCLabel = nil then Exit;
|
||||
if FHeaderADCLabel.Caption <> S then FHeaderADCLabel.Caption := S;
|
||||
FHeaderADCLabel.Visible := S <> '';
|
||||
if FHeaderRateLabel <> nil then
|
||||
FHeaderADCLabel.Left := FHeaderRateLabel.Left + FHeaderRateLabel.Width + 10;
|
||||
LayoutHeaderLabels;
|
||||
end;
|
||||
|
||||
procedure TPanafallPanel.SetADCClickable(AValue: Boolean);
|
||||
@@ -569,16 +625,13 @@ var BtnS, X: Integer;
|
||||
begin
|
||||
if FHeaderLabel <> nil then
|
||||
FHeaderLabel.Top := (H - FHeaderLabel.Height) div 2;
|
||||
if (FHeaderRateLabel <> nil) and (FHeaderLabel <> nil) then
|
||||
begin
|
||||
FHeaderRateLabel.Top := (H - FHeaderRateLabel.Height) div 2;
|
||||
FHeaderRateLabel.Left := FHeaderLabel.Left + FHeaderLabel.Width + 6;
|
||||
end;
|
||||
if (FHeaderADCLabel <> nil) and (FHeaderRateLabel <> nil) then
|
||||
begin
|
||||
FHeaderADCLabel.Top := (H - FHeaderADCLabel.Height) div 2;
|
||||
FHeaderADCLabel.Left := FHeaderRateLabel.Left + FHeaderRateLabel.Width + 10;
|
||||
end;
|
||||
if FHeaderBandLabel <> nil then
|
||||
FHeaderBandLabel.Top := (H - FHeaderBandLabel.Height) div 2;
|
||||
if FHeaderRateLabel <> nil then
|
||||
FHeaderRateLabel.Top := (H - FHeaderRateLabel.Height) div 2;
|
||||
if FHeaderADCLabel <> nil then
|
||||
FHeaderADCLabel.Top := (H - FHeaderADCLabel.Height) div 2;
|
||||
LayoutHeaderLabels; // единый каскад left-позиций
|
||||
// Кнопки шапки прижаты вправо, справа налево: [×] [⧉] [◑]. Позиция каждой
|
||||
// зависит от видимости соседей (у пана 0 нет × и ⧉ — ◑ уезжает к краю).
|
||||
BtnS := H - 4;
|
||||
|
||||
Reference in New Issue
Block a user