ui(settings): add responsive page layout

This commit is contained in:
2026-08-04 20:36:17 +03:00
parent 304e574592
commit 612bc73687
+227 -307
View File
@@ -37,6 +37,10 @@ const
// Tag контейнера подвкладки: ApplyTheme красит такие панели фоном страницы, // Tag контейнера подвкладки: ApplyTheme красит такие панели фоном страницы,
// а не цветом группы (иначе подложка выглядела бы как одна гигантская группа). // а не цветом группы (иначе подложка выглядела бы как одна гигантская группа).
TAG_PAGE_BG = 909; TAG_PAGE_BG = 909;
TAG_RESPONSIVE_CARD = 910;
TAG_PAGE_HEADER = 911;
TAG_RESPONSIVE_HALF_LEFT = 912;
TAG_RESPONSIVE_HALF_RIGHT = 913;
type type
// PA: MaxPower в Вт, BandCal[0..10] — калибровка 38.8..100.0 на диапазон // PA: MaxPower в Вт, BandCal[0..10] — калибровка 38.8..100.0 на диапазон
@@ -501,11 +505,17 @@ type
procedure BtnTXProfDelClick(Sender: TObject); procedure BtnTXProfDelClick(Sender: TObject);
procedure BtnTXProfFactoryClick(Sender: TObject); procedure BtnTXProfFactoryClick(Sender: TObject);
procedure ApplyTheme(const T: TAppTheme); procedure ApplyTheme(const T: TAppTheme);
procedure SettingsResize(Sender: TObject);
procedure RelayoutResponsiveCards;
procedure NavClick(Sender: TObject); procedure NavClick(Sender: TObject);
procedure SelectPage(APage: TWinControl; ANav: TFlatButton); procedure SelectPage(APage: TWinControl; ANav: TFlatButton);
function MakeLbl(AParent: TWinControl; const Cap: string; function MakeLbl(AParent: TWinControl; const Cap: string;
ALeft, ATop, AW: Integer): TLabel; ALeft, ATop, AW: Integer): TLabel;
function MakeTextBlock(AParent: TWinControl; const Cap: string;
ALeft, ATop, AW, AH: Integer): TLabel;
procedure MakePageHeader(AParent: TWinControl; const ATitle, ASubtitle: string;
ASubtitleHeight: Integer = 20);
function MakeCombo(AParent: TWinControl; ALeft, ATop, AW: Integer; function MakeCombo(AParent: TWinControl; ALeft, ATop, AW: Integer;
OnChange: TNotifyEvent): TFlatComboBox; OnChange: TNotifyEvent): TFlatComboBox;
function MakeGroupPanel(AParent: TWinControl; const Cap: string; function MakeGroupPanel(AParent: TWinControl; const Cap: string;
@@ -679,7 +689,9 @@ const
CLR_INPUT_TEXT = TColor($00202020); CLR_INPUT_TEXT = TColor($00202020);
BTN_H = 24; BTN_H = 24;
GRP_TITLE_H = 22; // высота заголовка в group-панелях (title label + divider) GRP_TITLE_H = 22; // высота заголовка в group-панелях (title label + divider)
SETTINGS_MARGIN = 22;
SETTINGS_CARD_W = 760; SETTINGS_CARD_W = 760;
SETTINGS_CARD_MAX_W = 934;
SETTINGS_FOOTER_H = 52; SETTINGS_FOOTER_H = 52;
// Первый пункт комбо аудио слайса = «как у приложения» (пустое имя в конфиге). // Первый пункт комбо аудио слайса = «как у приложения» (пустое имя в конфиге).
@@ -774,6 +786,8 @@ begin
FLoading := False; FLoading := False;
FRXDevCount := 0; FRXDevCount := 0;
BuildUI; BuildUI;
OnResize := SettingsResize;
RelayoutResponsiveCards;
ApplyTheme(DarkTheme); ApplyTheme(DarkTheme);
end; end;
@@ -803,6 +817,44 @@ begin
Result.Font.Size := 9; Result.Font.Size := 9;
end; end;
function TSettingsForm.MakeTextBlock(AParent: TWinControl; const Cap: string;
ALeft, ATop, AW, AH: Integer): TLabel;
begin
Result := TLabel.Create(Self);
Result.Parent := AParent;
Result.Caption := Cap;
Result.AutoSize := False;
Result.SetBounds(DpiScale(ALeft), DpiScale(ATop),
DpiScale(AW), DpiScale(AH));
Result.Layout := tlTop;
Result.WordWrap := True;
Result.Font.Size := 9;
Result.Font.Color := CLR_TEXTDIM;
end;
procedure TSettingsForm.MakePageHeader(AParent: TWinControl;
const ATitle, ASubtitle: string; ASubtitleHeight: Integer);
var
L: TLabel;
begin
L := TLabel.Create(Self);
L.Parent := AParent;
L.Caption := ATitle;
L.AutoSize := False;
L.SetBounds(DpiScale(SETTINGS_MARGIN), DpiScale(16),
DpiScale(SETTINGS_CARD_W), DpiScale(32));
L.Tag := TAG_PAGE_HEADER;
L.Layout := tlCenter;
L.Font.Size := 16;
L.Font.Style := [fsBold];
L.Font.Color := CLR_TEXT;
if ASubtitle = '' then Exit;
L := MakeTextBlock(AParent, ASubtitle, SETTINGS_MARGIN, 48,
SETTINGS_CARD_W, ASubtitleHeight);
L.Tag := TAG_PAGE_HEADER;
end;
function TSettingsForm.MakeCombo(AParent: TWinControl; ALeft, ATop, AW: Integer; function TSettingsForm.MakeCombo(AParent: TWinControl; ALeft, ATop, AW: Integer;
OnChange: TNotifyEvent): TFlatComboBox; OnChange: TNotifyEvent): TFlatComboBox;
begin begin
@@ -833,6 +885,8 @@ begin
Result.SetBounds(DpiScale(ALeft), DpiScale(ATop), SAW, DpiScale(AH)); Result.SetBounds(DpiScale(ALeft), DpiScale(ATop), SAW, DpiScale(AH));
Result.BevelOuter := bvNone; Result.BevelOuter := bvNone;
Result.Color := CLR_PANEL; Result.Color := CLR_PANEL;
if AW = SETTINGS_CARD_W then
Result.Tag := TAG_RESPONSIVE_CARD;
TitleLbl := TLabel.Create(Self); TitleLbl := TLabel.Create(Self);
TitleLbl.Parent := Result; TitleLbl.Parent := Result;
@@ -840,6 +894,7 @@ begin
TitleLbl.AutoSize := False; TitleLbl.AutoSize := False;
TitleLbl.SetBounds(DpiScale(10), DpiScale(2), TitleLbl.SetBounds(DpiScale(10), DpiScale(2),
SAW - DpiScale(20), DpiScale(GRP_TITLE_H - 3)); SAW - DpiScale(20), DpiScale(GRP_TITLE_H - 3));
TitleLbl.Anchors := [akLeft, akTop, akRight];
TitleLbl.Layout := tlCenter; TitleLbl.Layout := tlCenter;
TitleLbl.Font.Color := CLR_ACCENT; TitleLbl.Font.Color := CLR_ACCENT;
TitleLbl.Font.Size := 9; TitleLbl.Font.Size := 9;
@@ -848,6 +903,7 @@ begin
Divider := TPanel.Create(Self); Divider := TPanel.Create(Self);
Divider.Parent := Result; Divider.Parent := Result;
Divider.SetBounds(0, DpiScale(GRP_TITLE_H), SAW, 1); Divider.SetBounds(0, DpiScale(GRP_TITLE_H), SAW, 1);
Divider.Anchors := [akLeft, akTop, akRight];
Divider.BevelOuter := bvNone; Divider.BevelOuter := bvNone;
Divider.Color := CLR_BORDER; Divider.Color := CLR_BORDER;
end; end;
@@ -913,7 +969,9 @@ begin
begin begin
Parent := FNavPanel; Parent := FNavPanel;
Caption := 'Settings'; Caption := 'Settings';
AutoSize := False;
SetBounds(DpiScale(14), DpiScale(14), DpiScale(160), DpiScale(24)); SetBounds(DpiScale(14), DpiScale(14), DpiScale(160), DpiScale(24));
Layout := tlCenter;
Font.Size := 14; Font.Size := 14;
Font.Style := [fsBold]; Font.Style := [fsBold];
Font.Color := CLR_TEXT; Font.Color := CLR_TEXT;
@@ -923,7 +981,9 @@ begin
begin begin
Parent := FNavPanel; Parent := FNavPanel;
Caption := 'EWSDR'; Caption := 'EWSDR';
AutoSize := False;
SetBounds(DpiScale(14), DpiScale(39), DpiScale(160), DpiScale(18)); SetBounds(DpiScale(14), DpiScale(39), DpiScale(160), DpiScale(18));
Layout := tlCenter;
Font.Size := 9; Font.Size := 9;
Font.Color := CLR_TEXTDIM; Font.Color := CLR_TEXTDIM;
end; end;
@@ -1013,6 +1073,74 @@ begin
SelectPage(FPageAudio, FNavAudio); SelectPage(FPageAudio, FNavAudio);
end; end;
procedure TSettingsForm.SettingsResize(Sender: TObject);
begin
RelayoutResponsiveCards;
end;
procedure TSettingsForm.RelayoutResponsiveCards;
procedure ResizeTree(AParent: TWinControl; ACardWidth: Integer);
var
i, HalfWidth: Integer;
Ctrl: TControl;
begin
HalfWidth := (ACardWidth - DpiScale(16)) div 2;
for i := 0 to AParent.ControlCount - 1 do
begin
Ctrl := AParent.Controls[i];
if Ctrl.Tag = TAG_PAGE_HEADER then
Ctrl.Width := ACardWidth
else if (Ctrl is TPanel) and (Ctrl.Tag = TAG_RESPONSIVE_CARD) then
Ctrl.Width := ACardWidth
else if (Ctrl is TPanel) and (Ctrl.Tag = TAG_RESPONSIVE_HALF_LEFT) then
begin
Ctrl.Left := DpiScale(SETTINGS_MARGIN);
Ctrl.Width := HalfWidth;
end
else if (Ctrl is TPanel) and (Ctrl.Tag = TAG_RESPONSIVE_HALF_RIGHT) then
begin
Ctrl.Left := DpiScale(SETTINGS_MARGIN) + HalfWidth + DpiScale(16);
Ctrl.Width := HalfWidth;
end
else if (Ctrl is TPanel) and (Ctrl.Tag = TAG_PAGE_BG) then
Ctrl.Width := ACardWidth + DpiScale(2 * SETTINGS_MARGIN);
if Ctrl is TWinControl then
ResizeTree(TWinControl(Ctrl), ACardWidth);
end;
end;
procedure ResizePage(APage: TScrollBox);
var
CardWidth: Integer;
begin
if (APage = nil) or (APage.ClientWidth <= 0) then Exit;
CardWidth := APage.ClientWidth
- DpiScale(2 * SETTINGS_MARGIN) - DpiScale(20);
CardWidth := EnsureRange(CardWidth, DpiScale(SETTINGS_CARD_W),
DpiScale(SETTINGS_CARD_MAX_W));
ResizeTree(APage, CardWidth);
end;
begin
if FContentPanel = nil then Exit;
ResizePage(FPageAudio);
ResizePage(FPageDisplay);
ResizePage(FPageSpectrum);
ResizePage(FPageWaterfall);
ResizePage(FPageTransmit);
ResizePage(FPagePA);
ResizePage(FPageCalib);
ResizePage(FPageAdvanced);
ResizePage(FPageCAT);
ResizePage(FPageSlices);
ResizePage(FPageAlex);
ResizePage(FPageXvtr);
ResizePage(FPageOC);
ResizePage(FPageQO);
end;
procedure TSettingsForm.NavClick(Sender: TObject); procedure TSettingsForm.NavClick(Sender: TObject);
begin begin
if Sender = FNavAudio then SelectPage(FPageAudio, FNavAudio) if Sender = FNavAudio then SelectPage(FPageAudio, FNavAudio)
@@ -1062,6 +1190,10 @@ begin
FNavXvtr.Active := ANav = FNavXvtr; FNavXvtr.Active := ANav = FNavXvtr;
FNavOC.Active := ANav = FNavOC; FNavOC.Active := ANav = FNavOC;
FNavQO.Active := ANav = FNavQO; FNavQO.Active := ANav = FNavQO;
// Скрытые alClient-страницы LCL не всегда перекладывает до показа.
// После переключения берём уже актуальный ClientWidth выбранной страницы.
RelayoutResponsiveCards;
end; end;
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
@@ -1081,24 +1213,8 @@ const
var var
Grp: TPanel; Grp: TPanel;
begin begin
with TLabel.Create(Self) do MakePageHeader(FPageAudio, 'Audio',
begin 'Input and output devices used by the receiver and transmitter.');
Parent := FPageAudio;
Caption := 'Audio';
SetBounds(DpiScale(MARGIN), DpiScale(18), DpiScale(400), DpiScale(28));
Font.Size := 16;
Font.Style := [fsBold];
Font.Color := CLR_TEXT;
end;
with TLabel.Create(Self) do
begin
Parent := FPageAudio;
Caption := 'Input and output devices used by the receiver and transmitter.';
SetBounds(DpiScale(MARGIN), DpiScale(48), DpiScale(560), DpiScale(20));
Font.Size := 9;
Font.Color := CLR_TEXTDIM;
end;
Grp := MakeGroupPanel(FPageAudio, 'Audio Devices', MARGIN, 86, SETTINGS_CARD_W, 184); Grp := MakeGroupPanel(FPageAudio, 'Audio Devices', MARGIN, 86, SETTINGS_CARD_W, 184);
@@ -1146,24 +1262,8 @@ var
i: Integer; i: Integer;
Grp: TPanel; Grp: TPanel;
begin begin
with TLabel.Create(Self) do MakePageHeader(FPageDisplay, 'Display',
begin 'General visual options. Spectrum and waterfall processing are split into their own pages.');
Parent := FPageDisplay;
Caption := 'Display';
SetBounds(DpiScale(MARGIN), DpiScale(18), DpiScale(400), DpiScale(28));
Font.Size := 16;
Font.Style := [fsBold];
Font.Color := CLR_TEXT;
end;
with TLabel.Create(Self) do
begin
Parent := FPageDisplay;
Caption := 'General visual options. Spectrum and waterfall processing are split into their own pages.';
SetBounds(DpiScale(MARGIN), DpiScale(48), DpiScale(680), DpiScale(20));
Font.Size := 9;
Font.Color := CLR_TEXTDIM;
end;
Grp := MakeGroupPanel(FPageDisplay, 'Visible Panes', MARGIN, 86, SETTINGS_CARD_W, 112); Grp := MakeGroupPanel(FPageDisplay, 'Visible Panes', MARGIN, 86, SETTINGS_CARD_W, 112);
@@ -1288,24 +1388,8 @@ var
end; end;
begin begin
with TLabel.Create(Self) do MakePageHeader(FPageSpectrum, 'Spectrum',
begin 'Analyzer, detector, averaging and grid parameters for RX1.');
Parent := FPageSpectrum;
Caption := 'Spectrum';
SetBounds(DpiScale(MARGIN), DpiScale(18), DpiScale(400), DpiScale(28));
Font.Size := 16;
Font.Style := [fsBold];
Font.Color := CLR_TEXT;
end;
with TLabel.Create(Self) do
begin
Parent := FPageSpectrum;
Caption := 'Analyzer, detector, averaging and grid parameters for RX1.';
SetBounds(DpiScale(MARGIN), DpiScale(48), DpiScale(620), DpiScale(20));
Font.Size := 9;
Font.Color := CLR_TEXTDIM;
end;
Grp := MakeGroupPanel(FPageSpectrum, 'Analyzer', MARGIN, 86, SETTINGS_CARD_W, 128); Grp := MakeGroupPanel(FPageSpectrum, 'Analyzer', MARGIN, 86, SETTINGS_CARD_W, 128);
MakeLbl(Grp, 'FFT Size:', PAD, R1 + 5, LW); MakeLbl(Grp, 'FFT Size:', PAD, R1 + 5, LW);
@@ -1342,24 +1426,8 @@ begin
for i := 0 to High(GRID_STEP_NAMES) do FCmbGridStep.Items.Add(GRID_STEP_NAMES[i]); for i := 0 to High(GRID_STEP_NAMES) do FCmbGridStep.Items.Add(GRID_STEP_NAMES[i]);
FCmbGridStep.ItemIndex := 1; FCmbGridStep.ItemIndex := 1;
with TLabel.Create(Self) do MakePageHeader(FPageWaterfall, 'Waterfall',
begin 'Waterfall detector, smoothing and color scale thresholds.');
Parent := FPageWaterfall;
Caption := 'Waterfall';
SetBounds(DpiScale(MARGIN), DpiScale(18), DpiScale(400), DpiScale(28));
Font.Size := 16;
Font.Style := [fsBold];
Font.Color := CLR_TEXT;
end;
with TLabel.Create(Self) do
begin
Parent := FPageWaterfall;
Caption := 'Waterfall detector, smoothing and color scale thresholds.';
SetBounds(DpiScale(MARGIN), DpiScale(48), DpiScale(620), DpiScale(20));
Font.Size := 9;
Font.Color := CLR_TEXTDIM;
end;
Grp := MakeGroupPanel(FPageWaterfall, 'Detection && Averaging', MARGIN, 86, SETTINGS_CARD_W, 170); Grp := MakeGroupPanel(FPageWaterfall, 'Detection && Averaging', MARGIN, 86, SETTINGS_CARD_W, 170);
MakeLbl(Grp, 'Detector:', PAD, R1 + 5, LW); MakeLbl(Grp, 'Detector:', PAD, R1 + 5, LW);
@@ -1515,24 +1583,9 @@ var
end; end;
begin begin
with TLabel.Create(Self) do MakePageHeader(FPageTransmit, 'Transmit',
begin 'Profile and EQ hold the active TX profile; Hardware and Display are '
Parent := FPageTransmit; + 'radio properties and do not follow it.');
Caption := 'Transmit';
SetBounds(DpiScale(MARGIN), DpiScale(18), DpiScale(400), DpiScale(28));
Font.Size := 16;
Font.Style := [fsBold];
Font.Color := CLR_TEXT;
end;
with TLabel.Create(Self) do
begin
Parent := FPageTransmit;
Caption := 'Profile and EQ hold the active TX profile; '
+ 'Hardware and Display are radio properties and do not follow it.';
SetBounds(DpiScale(MARGIN), DpiScale(48), DpiScale(GRP_W), DpiScale(20));
Font.Size := 9;
Font.Color := CLR_TEXTDIM;
end;
// ---- Полоса профиля (над подвкладками, видна всегда) -------------------- // ---- Полоса профиля (над подвкладками, видна всегда) --------------------
Grp := MakeGroupPanel(FPageTransmit, 'TX Profile', MARGIN, 78, GRP_W, 124); Grp := MakeGroupPanel(FPageTransmit, 'TX Profile', MARGIN, 78, GRP_W, 124);
@@ -2209,34 +2262,13 @@ var
Spin: TFlatSpinEdit; Spin: TFlatSpinEdit;
i, r, c, X, Y: Integer; i, r, c, X, Y: Integer;
begin begin
with TLabel.Create(Self) do MakePageHeader(FPagePA, 'Power Amplifier',
begin 'Output limit and per-band calibration values.');
Parent := FPagePA;
Caption := 'Power Amplifier';
SetBounds(DpiScale(MARGIN), DpiScale(18), DpiScale(400), DpiScale(28));
Font.Size := 16;
Font.Style := [fsBold];
Font.Color := CLR_TEXT;
end;
with TLabel.Create(Self) do
begin
Parent := FPagePA;
Caption := 'Output limit and per-band calibration values.';
SetBounds(DpiScale(MARGIN), DpiScale(48), DpiScale(620), DpiScale(20));
Font.Size := 9;
Font.Color := CLR_TEXTDIM;
end;
Grp := MakeGroupPanel(FPagePA, 'Power && Calibration', MARGIN, 86, SETTINGS_CARD_W, GRP_H); Grp := MakeGroupPanel(FPagePA, 'Power && Calibration', MARGIN, 86, SETTINGS_CARD_W, GRP_H);
// Max Power // Max Power
Lbl := TLabel.Create(Self); Lbl := MakeLbl(Grp, 'Max power (W)', GRP_PAD, R1 + 6, 132);
Lbl.Parent := Grp;
Lbl.Caption := 'Max power (W)';
Lbl.SetBounds(DpiScale(GRP_PAD), DpiScale(R1 + 6), DpiScale(132), DpiScale(18));
Lbl.Font.Color := CLR_TEXTDIM;
Lbl.Font.Size := 9;
Spin := TFlatSpinEdit.Create(Self); Spin := TFlatSpinEdit.Create(Self);
Spin.Parent := Grp; Spin.Parent := Grp;
@@ -2252,12 +2284,8 @@ begin
FEdMaxPower := Spin; FEdMaxPower := Spin;
// Pluto TX max attenuation @ drive 100% (потолок под внешний усилитель), дБ<0 // Pluto TX max attenuation @ drive 100% (потолок под внешний усилитель), дБ<0
Lbl := TLabel.Create(Self); Lbl := MakeLbl(Grp, 'Pluto TX att @100% (dB)',
Lbl.Parent := Grp; GRP_PAD + 320, R1 + 6, 180);
Lbl.Caption := 'Pluto TX att @100% (dB)';
Lbl.SetBounds(DpiScale(GRP_PAD + 320), DpiScale(R1 + 6), DpiScale(180), DpiScale(18));
Lbl.Font.Color := CLR_TEXTDIM;
Lbl.Font.Size := 9;
FEdPlutoTxMaxAtt := TFlatFloatSpinEdit.Create(Self); FEdPlutoTxMaxAtt := TFlatFloatSpinEdit.Create(Self);
FEdPlutoTxMaxAtt.Parent := Grp; FEdPlutoTxMaxAtt.Parent := Grp;
@@ -2273,12 +2301,8 @@ begin
FEdPlutoTxMaxAtt.OnChange := OnPlutoTxMaxAttChange; FEdPlutoTxMaxAtt.OnChange := OnPlutoTxMaxAttChange;
// Calibration label // Calibration label
Lbl := TLabel.Create(Self); Lbl := MakeLbl(Grp, 'Calibration per band, 38.8 ... 100.0',
Lbl.Parent := Grp; GRP_PAD, R1 + ROW_H + 12, 320);
Lbl.Caption := 'Calibration per band, 38.8 ... 100.0';
Lbl.SetBounds(DpiScale(GRP_PAD), DpiScale(R1 + ROW_H + 12), DpiScale(320), DpiScale(18));
Lbl.Font.Color := CLR_TEXTDIM;
Lbl.Font.Size := 9;
// Band grid 4 columns × 3 rows // Band grid 4 columns × 3 rows
for i := 0 to 10 do for i := 0 to 10 do
@@ -2288,12 +2312,7 @@ begin
X := GRP_PAD + c * (COL_W + 4); X := GRP_PAD + c * (COL_W + 4);
Y := R1 + ROW_H + 34 + r * ROW_H; Y := R1 + ROW_H + 34 + r * ROW_H;
Lbl := TLabel.Create(Self); Lbl := MakeLbl(Grp, PA_BAND_NAMES[i] + ':', X, Y + 5, LBL_W);
Lbl.Parent := Grp;
Lbl.Caption := PA_BAND_NAMES[i] + ':';
Lbl.SetBounds(DpiScale(X), DpiScale(Y + 5), DpiScale(LBL_W), DpiScale(18));
Lbl.Font.Color := CLR_TEXTDIM;
Lbl.Font.Size := 9;
FEdBandCal[i] := MakeFloatSpin(Grp, X + LBL_W, Y, OnBandCalChange); FEdBandCal[i] := MakeFloatSpin(Grp, X + LBL_W, Y, OnBandCalChange);
FEdBandCal[i].Tag := i; FEdBandCal[i].Tag := i;
@@ -2303,12 +2322,8 @@ begin
Grp := MakeGroupPanel(FPagePA, 'VHF Calibration (Transverters)', Grp := MakeGroupPanel(FPagePA, 'VHF Calibration (Transverters)',
MARGIN, 86 + GRP_H + 14, SETTINGS_CARD_W, R1 + 4 * ROW_H + 44); MARGIN, 86 + GRP_H + 14, SETTINGS_CARD_W, R1 + 4 * ROW_H + 44);
Lbl := TLabel.Create(Self); Lbl := MakeLbl(Grp, 'Calibration per transverter slot, 38.8 ... 100.0',
Lbl.Parent := Grp; GRP_PAD, R1, 420);
Lbl.Caption := 'Calibration per transverter slot, 38.8 ... 100.0';
Lbl.SetBounds(DpiScale(GRP_PAD), DpiScale(R1), DpiScale(420), DpiScale(18));
Lbl.Font.Color := CLR_TEXTDIM;
Lbl.Font.Size := 9;
for i := 0 to CFG_XVTR_COUNT - 1 do for i := 0 to CFG_XVTR_COUNT - 1 do
begin begin
@@ -2389,24 +2404,8 @@ begin
FCalXvtrName[i] := ''; FCalXvtrName[i] := '';
end; end;
with TLabel.Create(Self) do MakePageHeader(FPageCalib, 'Calibration',
begin 'Power/SWR detector, supply voltage/current and S-meter calibration.');
Parent := FPageCalib;
Caption := 'Calibration';
SetBounds(DpiScale(CAL_MARGIN), DpiScale(18), DpiScale(400), DpiScale(28));
Font.Size := 16;
Font.Style := [fsBold];
Font.Color := CLR_TEXT;
end;
with TLabel.Create(Self) do
begin
Parent := FPageCalib;
Caption := 'Power/SWR detector, supply voltage/current and S-meter calibration.';
SetBounds(DpiScale(CAL_MARGIN), DpiScale(48), DpiScale(620), DpiScale(20));
Font.Size := 9;
Font.Color := CLR_TEXTDIM;
end;
// ── Power / SWR detector ──────────────────────────────────────────────── // ── Power / SWR detector ────────────────────────────────────────────────
GTop := 86; GTop := 86;
@@ -2807,15 +2806,8 @@ var
Lbl: TLabel; Lbl: TLabel;
Y, i: Integer; Y, i: Integer;
begin begin
with TLabel.Create(Self) do MakePageHeader(FPageAdvanced, 'Advanced',
begin 'ADC processing and built-in web server settings.');
Parent := FPageAdvanced;
Caption := 'Advanced';
SetBounds(DpiScale(MARGIN), DpiScale(18), DpiScale(400), DpiScale(28));
Font.Size := 16;
Font.Style := [fsBold];
Font.Color := CLR_TEXT;
end;
// ── ADC ─────────────────────────────────────────────────────────────────── // ── ADC ───────────────────────────────────────────────────────────────────
Grp := MakeGroupPanel(FPageAdvanced, 'ADC', MARGIN, 86, SETTINGS_CARD_W, 128); Grp := MakeGroupPanel(FPageAdvanced, 'ADC', MARGIN, 86, SETTINGS_CARD_W, 128);
@@ -2978,12 +2970,24 @@ procedure TSettingsForm.ApplyTheme(const T: TAppTheme);
procedure StyleBtn(B: TFlatButton); procedure StyleBtn(B: TFlatButton);
begin begin
B.ClrNorm := T.Panel; if B.Parent = FNavPanel then
B.ClrBorder := T.Panel; begin
B.ClrHot := T.BG; B.ClrNorm := T.Panel;
B.ClrActive := T.Panel; B.ClrBorder := T.Panel;
B.ClrText := T.Text; B.ClrHot := T.BtnHot;
B.ClrTextAct := T.FreqActive; B.ClrActive := T.BtnActive;
B.ClrText := T.Text;
B.ClrTextAct := T.BtnTextActive;
end
else
begin
B.ClrNorm := T.BtnNorm;
B.ClrBorder := T.BtnBorderNorm;
B.ClrHot := T.BtnHot;
B.ClrActive := T.BtnActive;
B.ClrText := T.BtnText;
B.ClrTextAct := T.BtnTextActive;
end;
B.Invalidate; B.Invalidate;
end; end;
@@ -3777,21 +3781,8 @@ var
Cmb: TFlatComboBox; Cmb: TFlatComboBox;
Spin: TFlatSpinEdit; Spin: TFlatSpinEdit;
begin begin
with TLabel.Create(Self) do MakePageHeader(FPageCAT, 'CAT',
begin 'Computer Aided Transceiver — serial port and TCP server settings.');
Parent := FPageCAT;
Caption := 'CAT';
SetBounds(DpiScale(MARGIN), DpiScale(18), DpiScale(400), DpiScale(28));
Font.Size := 16;
Font.Style := [fsBold]; Font.Color := CLR_TEXT;
end;
with TLabel.Create(Self) do
begin
Parent := FPageCAT;
Caption := 'Computer Aided Transceiver — serial port and TCP server settings.';
SetBounds(DpiScale(MARGIN), DpiScale(48), DpiScale(680), DpiScale(20));
Font.Size := 9; Font.Color := CLR_TEXTDIM;
end;
for i := 0 to 3 do for i := 0 to 3 do
begin begin
@@ -3801,6 +3792,10 @@ begin
gy := 86 + row * (GRP_H + GRP_GAP); gy := 86 + row * (GRP_H + GRP_GAP);
Grp := MakeGroupPanel(FPageCAT, 'Serial Port ' + IntToStr(i + 1), gx, gy, GRP_W, GRP_H); Grp := MakeGroupPanel(FPageCAT, 'Serial Port ' + IntToStr(i + 1), gx, gy, GRP_W, GRP_H);
if col = 0 then
Grp.Tag := TAG_RESPONSIVE_HALF_LEFT
else
Grp.Tag := TAG_RESPONSIVE_HALF_RIGHT;
Chk := TFlatCheckBox.Create(Self); Chk := TFlatCheckBox.Create(Self);
Chk.Parent := Grp; Chk.Parent := Grp;
@@ -3869,6 +3864,7 @@ begin
// TCP server group below the two rows of serial port panels // TCP server group below the two rows of serial port panels
gy := 86 + 2 * (GRP_H + GRP_GAP); gy := 86 + 2 * (GRP_H + GRP_GAP);
Grp := MakeGroupPanel(FPageCAT, 'TCP CAT Server', MARGIN, gy, GRP_W * 2 + GRP_GAP, 116); Grp := MakeGroupPanel(FPageCAT, 'TCP CAT Server', MARGIN, gy, GRP_W * 2 + GRP_GAP, 116);
Grp.Tag := TAG_RESPONSIVE_CARD;
FCATTcpEn := TFlatCheckBox.Create(Self); FCATTcpEn := TFlatCheckBox.Create(Self);
FCATTcpEn.Parent := Grp; FCATTcpEn.Parent := Grp;
@@ -4000,30 +3996,10 @@ var
Spin: TFlatSpinEdit; Spin: TFlatSpinEdit;
Cmb: TFlatComboBox; Cmb: TFlatComboBox;
begin begin
with TLabel.Create(Self) do MakePageHeader(FPageSlices, 'Slices',
begin 'A separate CAT TCP port per additional slice — external software controls '
Parent := FPageSlices; + 'its own slice as a standalone transceiver.' + LineEnding
Caption := 'Slices'; + 'Slice A is the main receiver and is configured on the CAT page.', 38);
SetBounds(DpiScale(MARGIN), DpiScale(18), DpiScale(400), DpiScale(28));
Font.Size := 16;
Font.Style := [fsBold]; Font.Color := CLR_TEXT;
end;
with TLabel.Create(Self) do
begin
Parent := FPageSlices;
Caption := 'A separate CAT TCP port per additional slice — external software '
+ 'controls its own slice as a standalone transceiver.';
SetBounds(DpiScale(MARGIN), DpiScale(48), DpiScale(760), DpiScale(20));
Font.Size := 9; Font.Color := CLR_TEXTDIM;
end;
with TLabel.Create(Self) do
begin
Parent := FPageSlices;
Caption := 'Slice A is the main receiver — it is configured on the CAT tab '
+ '(serial ports and the main TCP port).';
SetBounds(DpiScale(MARGIN), DpiScale(66), DpiScale(760), DpiScale(20));
Font.Size := 9; Font.Color := CLR_TEXTDIM;
end;
for i := 0 to CFG_CAT_SLICE_COUNT - 1 do for i := 0 to CFG_CAT_SLICE_COUNT - 1 do
begin begin
@@ -4033,6 +4009,10 @@ begin
gy := 96 + row * (GRP_H + GRP_GAP); gy := 96 + row * (GRP_H + GRP_GAP);
Grp := MakeGroupPanel(FPageSlices, 'Slice ' + Chr(Ord('B') + i), gx, gy, GRP_W, GRP_H); Grp := MakeGroupPanel(FPageSlices, 'Slice ' + Chr(Ord('B') + i), gx, gy, GRP_W, GRP_H);
if col = 0 then
Grp.Tag := TAG_RESPONSIVE_HALF_LEFT
else
Grp.Tag := TAG_RESPONSIVE_HALF_RIGHT;
Chk := TFlatCheckBox.Create(Self); Chk := TFlatCheckBox.Create(Self);
Chk.Parent := Grp; Chk.Parent := Grp;
@@ -4091,39 +4071,13 @@ begin
end; end;
gy := 96 + ((CFG_CAT_SLICE_COUNT + 1) div 2) * (GRP_H + GRP_GAP); gy := 96 + ((CFG_CAT_SLICE_COUNT + 1) div 2) * (GRP_H + GRP_GAP);
with TLabel.Create(Self) do MakeTextBlock(FPageSlices,
begin 'Audio routing belongs to the slice letter and is inherited by the next '
Parent := FPageSlices; + 'receiver placed in that slot.' + LineEnding
Caption := 'Audio out/in belong to the slice letter: the setting moves the ' + 'Only one slice can transmit. Auto TX selects this slice as the TX source; '
+ 'slice sitting on that slot right away and the next one inherits it.'; + 'without it, CAT can key only the current TX slice.' + LineEnding
SetBounds(DpiScale(MARGIN), DpiScale(gy), DpiScale(760), DpiScale(20)); + 'The CAT port stays open before the slice exists and replies with radio-off status.',
Font.Size := 9; Font.Color := CLR_TEXTDIM; MARGIN, gy, SETTINGS_CARD_W, 62);
end;
Inc(gy, 18);
with TLabel.Create(Self) do
begin
Parent := FPageSlices;
Caption := 'Only one slice transmits at a time: while anything is already on '
+ 'the air the request is ignored — no take-over.';
SetBounds(DpiScale(MARGIN), DpiScale(gy), DpiScale(760), DpiScale(20));
Font.Size := 9; Font.Color := CLR_TEXTDIM;
end;
with TLabel.Create(Self) do
begin
Parent := FPageSlices;
Caption := 'Without Auto TX a slice keys up over CAT only when it is already '
+ 'the transmit source (TX badge on the slice flag).';
SetBounds(DpiScale(MARGIN), DpiScale(gy + 18), DpiScale(760), DpiScale(20));
Font.Size := 9; Font.Color := CLR_TEXTDIM;
end;
with TLabel.Create(Self) do
begin
Parent := FPageSlices;
Caption := 'The port is opened even when the slice does not exist yet — until '
+ 'it is created CAT answers "radio off" (PS0).';
SetBounds(DpiScale(MARGIN), DpiScale(gy + 36), DpiScale(760), DpiScale(20));
Font.Size := 9; Font.Color := CLR_TEXTDIM;
end;
end; end;
procedure TSettingsForm.FireSliceCATChange; procedure TSettingsForm.FireSliceCATChange;
@@ -4289,7 +4243,9 @@ var
Result := TLabel.Create(Self); Result := TLabel.Create(Self);
Result.Parent := AParent; Result.Parent := AParent;
Result.Caption := ACap; Result.Caption := ACap;
Result.AutoSize := False;
Result.SetBounds(DpiScale(AX), DpiScale(AY), DpiScale(84), DpiScale(18)); Result.SetBounds(DpiScale(AX), DpiScale(AY), DpiScale(84), DpiScale(18));
Result.Layout := tlCenter;
Result.Font.Size := 9; Result.Font.Size := 9;
Result.Font.Color := CLR_TEXT; Result.Font.Color := CLR_TEXT;
end; end;
@@ -4300,7 +4256,10 @@ var
begin begin
Parent := AParent; Parent := AParent;
Caption := ACap; Caption := ACap;
AutoSize := False;
SetBounds(DpiScale(AX), DpiScale(AY), DpiScale(30), DpiScale(16)); SetBounds(DpiScale(AX), DpiScale(AY), DpiScale(30), DpiScale(16));
Alignment := taCenter;
Layout := tlCenter;
Font.Size := 9; Font.Size := 9;
Font.Color := CLR_TEXTDIM; Font.Color := CLR_TEXTDIM;
end; end;
@@ -4309,19 +4268,16 @@ var
begin begin
FLoading := True; FLoading := True;
try try
with TLabel.Create(Self) do MakePageHeader(FPageAlex, 'Antenna / Alex', '');
begin
Parent := FPageAlex;
Caption := 'Antenna / Alex';
SetBounds(DpiScale(MARGIN), DpiScale(18), DpiScale(500), DpiScale(28));
Font.Size := 16;
Font.Style := [fsBold]; Font.Color := CLR_TEXT;
end;
FAlexSubtitle := TLabel.Create(Self); FAlexSubtitle := TLabel.Create(Self);
FAlexSubtitle.Parent := FPageAlex; FAlexSubtitle.Parent := FPageAlex;
FAlexSubtitle.Caption := FAlexSubtitle.Caption :=
'Per-band RX/TX antenna and secondary path routing for the Alex filter board.'; 'Per-band RX/TX antenna and secondary path routing for the Alex filter board.';
FAlexSubtitle.SetBounds(DpiScale(MARGIN), DpiScale(48), DpiScale(680), DpiScale(20)); FAlexSubtitle.AutoSize := False;
FAlexSubtitle.SetBounds(DpiScale(MARGIN), DpiScale(48),
DpiScale(SETTINGS_CARD_W), DpiScale(20));
FAlexSubtitle.Layout := tlTop;
FAlexSubtitle.WordWrap := True;
FAlexSubtitle.Font.Size := 9; FAlexSubtitle.Font.Size := 9;
FAlexSubtitle.Font.Color := CLR_TEXTDIM; FAlexSubtitle.Font.Color := CLR_TEXTDIM;
@@ -4812,22 +4768,9 @@ var
begin begin
FLoading := True; FLoading := True;
try try
// --- Заголовок страницы --- MakePageHeader(FPageXvtr, 'Transverters',
L := TLabel.Create(Self); 'Configure up to 8 transverter (XVTR) bands. The VFO shows RF frequency; '
L.Parent := FPageXvtr; + 'the radio tunes to IF: f_IF = f_displayed - LO + LO Error.', 38);
L.Caption := 'Transverters';
L.SetBounds(DpiScale(MARGIN), DpiScale(18), DpiScale(500), DpiScale(28));
L.Font.Size := 16;
L.Font.Style := [fsBold]; L.Font.Color := CLR_TEXT;
L := TLabel.Create(Self);
L.Parent := FPageXvtr;
L.Caption := 'Configure up to 8 transverter (XVTR) bands. When a band is selected on the main form, ' +
'the VFO shows the displayed (RF) frequency.' + LineEnding +
'The radio is internally tuned to the IF: f_IF = f_displayed - LO + LO Error.';
L.SetBounds(DpiScale(MARGIN), DpiScale(50), DpiScale(GRP_W), DpiScale(34));
L.Font.Size := 9; L.Font.Color := CLR_TEXTDIM;
L.WordWrap := True;
// --- Group "Slots" --- (последний слот зарезервирован под QO-100 — не строим) // --- Group "Slots" --- (последний слот зарезервирован под QO-100 — не строим)
GrpY := 100; GrpY := 100;
@@ -4890,18 +4833,14 @@ begin
FChkXvtrTunPwr.OnChange := OnXvtrAnyChange; FChkXvtrTunPwr.OnChange := OnXvtrAnyChange;
// --- Подсказка про колонки внизу --- // --- Подсказка про колонки внизу ---
L := TLabel.Create(Self); L := MakeTextBlock(FPageXvtr,
L.Parent := FPageXvtr;
L.Caption :=
'EN - enable the slot. Label - band button caption. Begin/End - displayed VFO range. ' + 'EN - enable the slot. Label - band button caption. Begin/End - displayed VFO range. ' +
'LO - local oscillator. LO Error - fine correction in Hz.' + LineEnding + 'LO - local oscillator. LO Error - fine correction in Hz.' + LineEnding +
'RX Gain - transverter RX gain offset. RX Only - block TX. TX Pwr - drive limit. ' + 'RX Gain - transverter RX gain offset. RX Only - block TX. TX Pwr - drive limit. ' +
'XVTR RF - use the main HF RF path.' + LineEnding + 'XVTR RF - use the main HF RF path.' + LineEnding +
'Disable PA - keep the internal PA in RX position during TX. ' + 'Disable PA - keep the internal PA in RX position during TX. ' +
'RX Antenna - override the per-band antenna for this slot.'; 'RX Antenna - override the per-band antenna for this slot.',
L.SetBounds(DpiScale(MARGIN), DpiScale(GrpY + GRP_TITLE_H + 36 + 16), DpiScale(GRP_W), DpiScale(52)); MARGIN, GrpY + GRP_TITLE_H + 36 + 16, GRP_W, 52);
L.Font.Size := 9; L.Font.Color := CLR_TEXTDIM;
L.WordWrap := True;
// Применяем начальное состояние disabled-строк // Применяем начальное состояние disabled-строк
for i := 0 to CFG_XVTR_COUNT - 1 do for i := 0 to CFG_XVTR_COUNT - 1 do
@@ -5062,19 +5001,16 @@ var
begin begin
FLoading := True; FLoading := True;
try try
with TLabel.Create(Self) do MakePageHeader(FPageOC, 'OC Control', '');
begin
Parent := FPageOC;
Caption := 'OC Control';
SetBounds(DpiScale(MARGIN), DpiScale(18), DpiScale(500), DpiScale(28));
Font.Size := 16;
Font.Style := [fsBold]; Font.Color := CLR_TEXT;
end;
FOCSubtitle := TLabel.Create(Self); FOCSubtitle := TLabel.Create(Self);
FOCSubtitle.Parent := FPageOC; FOCSubtitle.Parent := FPageOC;
FOCSubtitle.Caption := FOCSubtitle.Caption :=
'Open Collector outputs (Penny/Alex board), per-band and per-transverter. openHPSDR only.'; 'Open Collector outputs (Penny/Alex board), per-band and per-transverter. openHPSDR only.';
FOCSubtitle.SetBounds(DpiScale(MARGIN), DpiScale(48), DpiScale(680), DpiScale(20)); FOCSubtitle.AutoSize := False;
FOCSubtitle.SetBounds(DpiScale(MARGIN), DpiScale(48),
DpiScale(SETTINGS_CARD_W), DpiScale(20));
FOCSubtitle.Layout := tlTop;
FOCSubtitle.WordWrap := True;
FOCSubtitle.Font.Size := 9; FOCSubtitle.Font.Size := 9;
FOCSubtitle.Font.Color := CLR_TEXTDIM; FOCSubtitle.Font.Color := CLR_TEXTDIM;
@@ -5278,29 +5214,16 @@ var
procedure QoLabel(const Cap: string; AY: Integer); procedure QoLabel(const Cap: string; AY: Integer);
begin begin
L := TLabel.Create(Self); L := MakeLbl(Grp, Cap, LBL_X, AY + 4, LBL_W);
L.Parent := Grp; L.Caption := Cap;
L.SetBounds(DpiScale(LBL_X), DpiScale(AY + 4), DpiScale(LBL_W), DpiScale(18));
L.Font.Color := CLR_TEXTDIM; L.Font.Size := 9;
end; end;
begin begin
FLoading := True; FLoading := True;
try try
L := TLabel.Create(Self); MakePageHeader(FPageQO, 'QO-100 (Es''hail-2 NB transponder)',
L.Parent := FPageQO; L.Caption := 'QO-100 (Es''hail-2 NB transponder)'; 'Geostationary transponder: RX downlink (~10489.5 MHz) via LNB and direct '
L.SetBounds(DpiScale(MARGIN), DpiScale(18), DpiScale(600), DpiScale(28)); + 'TX uplink (~2400 MHz). Separate LOs are used; beacon lock corrects drift.',
L.Font.Size := 16; 38);
L.Font.Style := [fsBold]; L.Font.Color := CLR_TEXT;
L := TLabel.Create(Self);
L.Parent := FPageQO;
L.Caption := 'Geostationary transponder: RX downlink (~10489.5 MHz) via LNB, ' +
'TX uplink (~2400 MHz) direct. VFO is displayed in downlink terms; ' +
'RX and TX use separate LOs. Drift is corrected by the beacon lock.';
L.SetBounds(DpiScale(MARGIN), DpiScale(50), DpiScale(700), DpiScale(34));
L.Font.Size := 9; L.Font.Color := CLR_TEXTDIM;
L.WordWrap := True;
Grp := MakeGroupPanel(FPageQO, 'Transponder', MARGIN, 100, Grp := MakeGroupPanel(FPageQO, 'Transponder', MARGIN, 100,
SETTINGS_CARD_W, GRP_TITLE_H + 9 * 30 + 16); SETTINGS_CARD_W, GRP_TITLE_H + 9 * 30 + 16);
@@ -5349,11 +5272,8 @@ begin
FQoPower.Font.Size := 9; FQoPower.Font.Size := 9;
FQoPower.OnChange := OnQOAnyChange; FQoPower.OnChange := OnQOAnyChange;
FQoInfo := TLabel.Create(Self); FQoInfo := MakeTextBlock(FPageQO, '',
FQoInfo.Parent := FPageQO; MARGIN, 100 + GRP_TITLE_H + 9 * 30 + 28, SETTINGS_CARD_W, 52);
FQoInfo.SetBounds(DpiScale(MARGIN), DpiScale(100 + GRP_TITLE_H + 9 * 30 + 28), DpiScale(700), DpiScale(52));
FQoInfo.Font.Color := CLR_TEXTDIM;
FQoInfo.Font.Size := 9; FQoInfo.WordWrap := True;
finally finally
FLoading := False; FLoading := False;
end; end;