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