Reorganize left panel: add RX block, move AGC, fix DUP placement and panel restore

- Add PanelRXBlock between TX and Band with CTUN, VOL, NR/NB/SNB/ANF/MUTE
- Move AGC panel to sit directly below RX block for logical RX grouping
- Move DUP button into TX panel below MOX/TUN
- Rename PanelRX → PanelAGC to match its content
- Standardize block label top (2px) and first-control top (16px) across all
  panels: AGC, SQL, CTCSS, STEP were inconsistent
- Fix left panel width restore after hide: save actual DPI-scaled width before
  zeroing instead of hardcoding 232, preventing panel truncation on Win32 with
  display scaling enabled

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-27 12:37:47 +03:00
co-authored by Claude Sonnet 4.6
parent 047c9b0997
commit e6f02db399
+122 -117
View File
@@ -150,6 +150,7 @@ type
FVfoOverlay: TVfoOverlay; // накладка SmartSDR-стиль на спектре
FSampleRateOverlay: TSampleRateOverlay; // оверлей span/hide слева вверху спектра
FPanelHidden: Boolean; // True = левая панель скрыта
FLeftPanelW: Integer; // ширина панели до скрытия (DPI-safe restore)
FPendingBoardType: Integer; // BoardType устройства для подключения
// ---- DSP + Audio ----
@@ -373,7 +374,7 @@ type
BtnFMStepSel: TFlatButton;
FStepDropDown: TFlatDropDown;
PanelRX: TPanel;
PanelAGC: TPanel;
BtnAGCMode: array[0..4] of TFlatButton; // FAST MED SLOW LONG OFF
LblAGCTop: TLabel; // показывает значение уровня
TrkAGC: TFlatSlider;
@@ -393,6 +394,8 @@ type
BtnMOX: TFlatButton;
BtnTUN: TFlatButton;
PanelRXBlock: TPanel;
// ---- Right panel ----
PanelRight: TPanel;
PbSpectrum: TControl;
@@ -1645,7 +1648,7 @@ begin
// TX controls
PanelTX := TPanel.Create(Self);
PanelTX.Parent := PanelLeft;
PanelTX.SetBounds(0, Y, LEFT_W, 72);
PanelTX.SetBounds(0, Y, LEFT_W, 96);
PanelTX.BevelOuter := bvNone;
MakeLbl(PanelTX, 'TX', 4, 2);
@@ -1656,18 +1659,118 @@ begin
LeftPanelButtonLeft(LEFT_W, 4, 1), 16,
LeftPanelButtonWidth(LEFT_W, 4, 1), BTN_SM, BtnTUNClick);
BtnDUP := MakeBtn(PanelTX, 'DUP',
LeftPanelButtonLeft(LEFT_W, 4, 0), 44,
LeftPanelButtonWidth(LEFT_W, 4, 0), BTN_SM, BtnDUPClick);
BtnDUP.Tag := 0;
StyleButton(BtnDUP, FDisplayDuplex);
LblDrv := TLabel.Create(Self);
LblDrv.Parent := PanelTX; LblDrv.Left := 4; LblDrv.Top := 53;
LblDrv.Parent := PanelTX; LblDrv.Left := 4; LblDrv.Top := 77;
LblDrv.Caption := 'DRV'; LblDrv.Font.Color := CLR_TEXTDIM;
LblDrv.Font.Name := 'Courier New'; LblDrv.Font.Size := 7;
TrkDrive := TFlatSlider.Create(Self);
TrkDrive.Parent := PanelTX; TrkDrive.Left := 34; TrkDrive.Top := 50;
TrkDrive.Parent := PanelTX; TrkDrive.Left := 34; TrkDrive.Top := 74;
TrkDrive.Width := 150; TrkDrive.Height := 16;
TrkDrive.Min := 0; TrkDrive.Max := 100; TrkDrive.Position := 50;
TrkDrive.OnChange := TrkDriveChange;
Inc(Y, 76);
Inc(Y, 100);
// RX block: CTUN + VOL + NR/NB/SNB/ANF/MUTE
PanelRXBlock := TPanel.Create(Self);
PanelRXBlock.Parent := PanelLeft;
PanelRXBlock.SetBounds(0, Y, LEFT_W, 96);
PanelRXBlock.BevelOuter := bvNone;
MakeLbl(PanelRXBlock, 'RX', 4, 2);
BtnCTun := MakeBtn(PanelRXBlock, 'CTUN',
LeftPanelButtonLeft(LEFT_W, 3, 0), 16,
LeftPanelButtonWidth(LEFT_W, 3, 0),
BTN_H, BtnCTunClick);
BtnCTun.Tag := 0;
StyleButton(BtnCTun, FCTun);
MakeLbl(PanelRXBlock, 'VOL', 4, 50);
TrkVolume := TFlatSlider.Create(Self);
TrkVolume.Parent := PanelRXBlock; TrkVolume.Left := 34; TrkVolume.Top := 46;
TrkVolume.Width := LEFT_W - 38; TrkVolume.Height := 16;
TrkVolume.Min := 0; TrkVolume.Max := 100;
TrkVolume.Position := FVolume;
TrkVolume.OnChange := TrkVolumeChange;
BtnNR := MakeBtn(PanelRXBlock, 'NR',
LeftPanelButtonLeft(LEFT_W, 5, 0), 70,
LeftPanelButtonWidth(LEFT_W, 5, 0), BTN_SM, BtnNRClick);
BtnNB := MakeBtn(PanelRXBlock, 'NB',
LeftPanelButtonLeft(LEFT_W, 5, 1), 70,
LeftPanelButtonWidth(LEFT_W, 5, 1), BTN_SM, BtnNBClick);
BtnSNB := MakeBtn(PanelRXBlock, 'SNB',
LeftPanelButtonLeft(LEFT_W, 5, 2), 70,
LeftPanelButtonWidth(LEFT_W, 5, 2), BTN_SM, BtnSNBClick);
BtnANF := MakeBtn(PanelRXBlock, 'ANF',
LeftPanelButtonLeft(LEFT_W, 5, 3), 70,
LeftPanelButtonWidth(LEFT_W, 5, 3), BTN_SM, BtnANFClick);
BtnMute := MakeBtn(PanelRXBlock, 'MUTE',
LeftPanelButtonLeft(LEFT_W, 5, 4), 70,
LeftPanelButtonWidth(LEFT_W, 5, 4), BTN_SM, BtnMuteClick);
Inc(Y, 100);
// AGC controls
PanelAGC := TPanel.Create(Self);
PanelAGC.Parent := PanelLeft;
PanelAGC.SetBounds(0, Y, LEFT_W, 70);
PanelAGC.BevelOuter := bvNone;
// AGC mode — 5 кнопок в ряд
MakeLbl(PanelAGC, 'AGC', 4, 2);
BtnAGCMode[0] := MakeBtn(PanelAGC, 'FAST',
LeftPanelButtonLeft(LEFT_W, 5, 0), 16,
LeftPanelButtonWidth(LEFT_W, 5, 0),
BTN_SM, BtnAGCModeClick);
BtnAGCMode[0].Tag := 0;
BtnAGCMode[1] := MakeBtn(PanelAGC, 'MED',
LeftPanelButtonLeft(LEFT_W, 5, 1), 16,
LeftPanelButtonWidth(LEFT_W, 5, 1),
BTN_SM, BtnAGCModeClick);
BtnAGCMode[1].Tag := 1;
BtnAGCMode[2] := MakeBtn(PanelAGC, 'SLOW',
LeftPanelButtonLeft(LEFT_W, 5, 2), 16,
LeftPanelButtonWidth(LEFT_W, 5, 2),
BTN_SM, BtnAGCModeClick);
BtnAGCMode[2].Tag := 2;
BtnAGCMode[3] := MakeBtn(PanelAGC, 'LONG',
LeftPanelButtonLeft(LEFT_W, 5, 3), 16,
LeftPanelButtonWidth(LEFT_W, 5, 3),
BTN_SM, BtnAGCModeClick);
BtnAGCMode[3].Tag := 3;
BtnAGCMode[4] := MakeBtn(PanelAGC, 'OFF',
LeftPanelButtonLeft(LEFT_W, 5, 4), 16,
LeftPanelButtonWidth(LEFT_W, 5, 4),
BTN_SM, BtnAGCModeClick);
BtnAGCMode[4].Tag := 4;
for i := 0 to 4 do StyleButton(BtnAGCMode[i], i = FAGCMode);
// AGC level slider
MakeLbl(PanelAGC, 'THRESH', 4, 44);
TrkAGC := TFlatSlider.Create(Self);
TrkAGC.Parent := PanelAGC; TrkAGC.Left := 56; TrkAGC.Top := 40;
TrkAGC.Width := LEFT_W - 96; TrkAGC.Height := 16;
TrkAGC.Min := 20; TrkAGC.Max := 120; // 20..120 → 20..-120 dBm
TrkAGC.Position := FAGCTop;
TrkAGC.Reversed := True;
TrkAGC.OnChange := TrkAGCChange;
LblAGCTop := TLabel.Create(Self);
LblAGCTop.Parent := PanelAGC;
LblAGCTop.Left := LEFT_W - 38; LblAGCTop.Top := 44;
LblAGCTop.Caption := Format('-%ddB', [FAGCTop]);
LblAGCTop.Font.Color := CLR_TEXT; LblAGCTop.Font.Name := 'Courier New';
LblAGCTop.Font.Size := 7;
Inc(Y, 74);
// Band selector. Высота резервирует 2 дополнительных ряда под XVTR-кнопки
// (создаются динамически в RebuildXvtrButtons). В каждом ряду до 6 кнопок,
@@ -1738,18 +1841,18 @@ begin
PanelFMSQ.SetBounds(0, Y, LEFT_W, 50);
PanelFMSQ.BevelOuter := bvNone;
PanelFMSQ.Visible := False;
MakeLbl(PanelFMSQ, 'SQL', 4, 4);
MakeLbl(PanelFMSQ, 'SQL', 4, 2);
W := LeftPanelButtonWidth(LEFT_W, 4, 0);
BtnFMSQ := MakeBtn(PanelFMSQ, 'SQL',
LeftPanelButtonLeft(LEFT_W, 4, 0), 20,
LeftPanelButtonLeft(LEFT_W, 4, 0), 16,
W, BTN_SM, BtnFMSQClick);
StyleButton(BtnFMSQ, False);
// Slider: vertically centred with button text (button top=20, h=24 → center=32)
// Slider: vertically centred with button text (button top=16, h=24 → center=28)
// Reserve 28px on the right for the numeric label
TrkFMSQ := TFlatSlider.Create(Self);
TrkFMSQ.Parent := PanelFMSQ;
TrkFMSQ.Left := BtnFMSQ.Left + BtnFMSQ.Width + 3;
TrkFMSQ.Top := 25; // 32 - 14/2 = 25
TrkFMSQ.Top := 21; // 28 - 14/2 = 21
TrkFMSQ.Width := LEFT_W - TrkFMSQ.Left - 30 - 4; // leave 30px for label + margins
TrkFMSQ.Height := 14;
TrkFMSQ.Min := 0; TrkFMSQ.Max := 100;
@@ -1759,7 +1862,7 @@ begin
LblFMSQ := TLabel.Create(Self);
LblFMSQ.Parent := PanelFMSQ;
LblFMSQ.Left := TrkFMSQ.Left + TrkFMSQ.Width + 3;
LblFMSQ.Top := 25;
LblFMSQ.Top := 21;
LblFMSQ.Width := 26;
LblFMSQ.Caption := IntToStr(FFMSQLevel);
LblFMSQ.Font.Color := CLR_TEXT;
@@ -1772,14 +1875,14 @@ begin
PanelFMCTCSS.SetBounds(0, Y, LEFT_W, 50);
PanelFMCTCSS.BevelOuter := bvNone;
PanelFMCTCSS.Visible := False;
MakeLbl(PanelFMCTCSS, 'CTCSS', 4, 4);
MakeLbl(PanelFMCTCSS, 'CTCSS', 4, 2);
BtnFMCTCSS := MakeBtn(PanelFMCTCSS, 'CTCSS',
LeftPanelButtonLeft(LEFT_W, 3, 0), 20,
LeftPanelButtonLeft(LEFT_W, 3, 0), 16,
LeftPanelButtonWidth(LEFT_W, 3, 0),
BTN_H, BtnFMCTCSSClick);
StyleButton(BtnFMCTCSS, False);
BtnFMCTCSSTone := MakeBtn(PanelFMCTCSS, CTCSS_NAMES[0],
LeftPanelButtonLeft(LEFT_W, 3, 1), 20,
LeftPanelButtonLeft(LEFT_W, 3, 1), 16,
LEFT_W - LEFT_PANEL_BTN_PAD -
LeftPanelButtonLeft(LEFT_W, 3, 1),
BTN_H, BtnFMCTCSSToneClick);
@@ -1796,14 +1899,14 @@ begin
PanelFMStep.SetBounds(0, Y, LEFT_W, 50);
PanelFMStep.BevelOuter := bvNone;
PanelFMStep.Visible := False;
MakeLbl(PanelFMStep, 'STEP', 4, 4);
MakeLbl(PanelFMStep, 'STEP', 4, 2);
BtnFMStep := MakeBtn(PanelFMStep, 'STEP',
LeftPanelButtonLeft(LEFT_W, 3, 0), 20,
LeftPanelButtonLeft(LEFT_W, 3, 0), 16,
LeftPanelButtonWidth(LEFT_W, 3, 0),
BTN_H, BtnFMStepClick);
StyleButton(BtnFMStep, FFMStepOn);
BtnFMStepSel := MakeBtn(PanelFMStep, FM_STEP_NAMES[FFMStepIdx],
LeftPanelButtonLeft(LEFT_W, 3, 1), 20,
LeftPanelButtonLeft(LEFT_W, 3, 1), 16,
LEFT_W - LEFT_PANEL_BTN_PAD -
LeftPanelButtonLeft(LEFT_W, 3, 1),
BTN_H, BtnFMStepSelClick);
@@ -1814,103 +1917,6 @@ begin
FStepDropDown.SetItemIndex(FFMStepIdx);
FStepDropDown.OnSelect := OnStepDropDownSelect;
// CTUN / DUP buttons (display-related)
BtnCTun := MakeBtn(PanelLeft, 'CTUN',
LeftPanelButtonLeft(LEFT_W, 3, 0), Y,
LeftPanelButtonWidth(LEFT_W, 3, 0),
BTN_H, BtnCTunClick);
BtnCTun.Tag := 0;
StyleButton(BtnCTun, FCTun);
BtnDUP := MakeBtn(PanelLeft, 'DUP',
LeftPanelButtonLeft(LEFT_W, 3, 1), Y,
LeftPanelButtonWidth(LEFT_W, 3, 1),
BTN_H, BtnDUPClick);
BtnDUP.Tag := 0;
StyleButton(BtnDUP, FDisplayDuplex);
Inc(Y, BTN_H + 4);
// RX controls
PanelRX := TPanel.Create(Self);
PanelRX.Parent := PanelLeft;
PanelRX.SetBounds(0, Y, LEFT_W, 122);
PanelRX.BevelOuter := bvNone;
// AGC mode — 5 кнопок в ряд
MakeLbl(PanelRX, 'AGC', 4, 4);
BtnAGCMode[0] := MakeBtn(PanelRX, 'FAST',
LeftPanelButtonLeft(LEFT_W, 5, 0), 22,
LeftPanelButtonWidth(LEFT_W, 5, 0),
BTN_SM, BtnAGCModeClick);
BtnAGCMode[0].Tag := 0;
BtnAGCMode[1] := MakeBtn(PanelRX, 'MED',
LeftPanelButtonLeft(LEFT_W, 5, 1), 22,
LeftPanelButtonWidth(LEFT_W, 5, 1),
BTN_SM, BtnAGCModeClick);
BtnAGCMode[1].Tag := 1;
BtnAGCMode[2] := MakeBtn(PanelRX, 'SLOW',
LeftPanelButtonLeft(LEFT_W, 5, 2), 22,
LeftPanelButtonWidth(LEFT_W, 5, 2),
BTN_SM, BtnAGCModeClick);
BtnAGCMode[2].Tag := 2;
BtnAGCMode[3] := MakeBtn(PanelRX, 'LONG',
LeftPanelButtonLeft(LEFT_W, 5, 3), 22,
LeftPanelButtonWidth(LEFT_W, 5, 3),
BTN_SM, BtnAGCModeClick);
BtnAGCMode[3].Tag := 3;
BtnAGCMode[4] := MakeBtn(PanelRX, 'OFF',
LeftPanelButtonLeft(LEFT_W, 5, 4), 22,
LeftPanelButtonWidth(LEFT_W, 5, 4),
BTN_SM, BtnAGCModeClick);
BtnAGCMode[4].Tag := 4;
for i := 0 to 4 do StyleButton(BtnAGCMode[i], i = FAGCMode);
// AGC level slider
MakeLbl(PanelRX, 'THRESH', 4, 50);
TrkAGC := TFlatSlider.Create(Self);
TrkAGC.Parent := PanelRX; TrkAGC.Left := 56; TrkAGC.Top := 46;
TrkAGC.Width := LEFT_W - 96; TrkAGC.Height := 16;
TrkAGC.Min := 20; TrkAGC.Max := 120; // 20..120 → 20..-120 dBm
TrkAGC.Position := FAGCTop;
TrkAGC.Reversed := True;
TrkAGC.OnChange := TrkAGCChange;
LblAGCTop := TLabel.Create(Self);
LblAGCTop.Parent := PanelRX;
LblAGCTop.Left := LEFT_W - 38; LblAGCTop.Top := 50;
LblAGCTop.Caption := Format('-%ddB', [FAGCTop]);
LblAGCTop.Font.Color := CLR_TEXT; LblAGCTop.Font.Name := 'Courier New';
LblAGCTop.Font.Size := 7;
// VOL
MakeLbl(PanelRX, 'VOL', 4, 74);
TrkVolume := TFlatSlider.Create(Self);
TrkVolume.Parent := PanelRX; TrkVolume.Left := 34; TrkVolume.Top := 70;
TrkVolume.Width := LEFT_W - 38; TrkVolume.Height := 16;
TrkVolume.Min := 0; TrkVolume.Max := 100;
TrkVolume.Position := FVolume;
TrkVolume.OnChange := TrkVolumeChange;
// NR NB SNB ANF MUTE
BtnNR := MakeBtn(PanelRX, 'NR',
LeftPanelButtonLeft(LEFT_W, 5, 0), 96,
LeftPanelButtonWidth(LEFT_W, 5, 0), BTN_SM, BtnNRClick);
BtnNB := MakeBtn(PanelRX, 'NB',
LeftPanelButtonLeft(LEFT_W, 5, 1), 96,
LeftPanelButtonWidth(LEFT_W, 5, 1), BTN_SM, BtnNBClick);
BtnSNB := MakeBtn(PanelRX, 'SNB',
LeftPanelButtonLeft(LEFT_W, 5, 2), 96,
LeftPanelButtonWidth(LEFT_W, 5, 2), BTN_SM, BtnSNBClick);
BtnANF := MakeBtn(PanelRX, 'ANF',
LeftPanelButtonLeft(LEFT_W, 5, 3), 96,
LeftPanelButtonWidth(LEFT_W, 5, 3), BTN_SM, BtnANFClick);
BtnMute := MakeBtn(PanelRX, 'MUTE',
LeftPanelButtonLeft(LEFT_W, 5, 4), 96,
LeftPanelButtonWidth(LEFT_W, 5, 4), BTN_SM, BtnMuteClick);
Inc(Y, 126);
// ---- Right panel ----
PanelRight := TPanel.Create(Self);
PanelRight.Parent := Self;
@@ -2495,7 +2501,7 @@ begin
if PanelFMSQ <> nil then DP(PanelFMSQ);
if PanelFMCTCSS <> nil then DP(PanelFMCTCSS);
if PanelFMStep <> nil then DP(PanelFMStep);
DP(PanelRX); DP(PanelTX);
DP(PanelRXBlock); DP(PanelAGC); DP(PanelTX);
DP(PanelRight);
if PbSpectrum is TPaintBox then
@@ -5519,6 +5525,7 @@ begin
FPanelHidden := not FPanelHidden;
if FPanelHidden then
begin
FLeftPanelW := PanelLeft.Width; // сохраняем реальную ширину (учитывает DPI на Win32)
PanelLeft.Width := 0;
FVfoOverlay.Width := 260;
FVfoOverlay.Height := 136; // OVL_H_NORM — расширяется сам при открытии AGC-пикера
@@ -5529,7 +5536,7 @@ begin
end
else
begin
PanelLeft.Width := 232;
PanelLeft.Width := FLeftPanelW; // восстанавливаем сохранённую ширину
FVfoOverlay.HandleMouseLeave;
FVfoOverlay.Visible := False;
end;
@@ -6520,8 +6527,6 @@ begin
begin
PanelFMCTCSS.Top := Y; Y := Y + PanelFMCTCSS.Height + 2;
end;
BtnCTun.Top := Y; BtnDUP.Top := Y; Y := Y + BtnCTun.Height + 4;
PanelRX.Top := Y;
end;
procedure TMainForm.RebuildXvtrButtons;