ui(tx): вкладка Transmit целиком на английском + сетка группы Microphone

Страница была единственной с русскими подписями — переведены подсказки всех
четырёх подвкладок, надпись «Profile drive: N %» и диалоги профиля (New /
Rename / Delete / Factory / лимит). Строковых литералов с кириллицей в
SettingsForm больше нет.

Группа Microphone приведена к сетке страницы: подписи в колонке PAD, поля в
колонке CX (комбобокс Mic jack и спин Mic gain теперь в одной колонке),
Connector переехал во вторую пару колонок к Mic gain — на не-Orion он скрыт и
дыры не оставляет, поэтому отдельная третья строка не нужна (176 → 134 px).
Тумблеры boost/bias/PTT расставлены с равным шагом; пара «Line In gain»
занимает место чекбокса boost и заканчивается до «Mic bias».

Английские подписи длиннее русских — все проверены рендером формы offscreen,
в группы вписываются.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-04 18:08:59 +03:00
co-authored by Claude Opus 5
parent 36af031767
commit 8f95795bbb
+55 -48
View File
@@ -1502,8 +1502,8 @@ begin
with TLabel.Create(Self) do
begin
Parent := FPageTransmit;
Caption := 'Profile и EQ — содержимое активного TX-профиля; '
+ 'Hardware и Display — свойства устройства, от профиля не зависят.';
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.Name := UI_FONT;
Font.Size := 9;
@@ -1520,10 +1520,11 @@ begin
FBtnTXProfDel := MkBtn(Grp, PAD + 514, R1, 100, 'Delete', BtnTXProfDelClick);
FBtnTXProfFactory := MkBtn(Grp, PAD + 620, R1, 100, 'Factory', BtnTXProfFactoryClick);
MakeLbl(Grp, 'Правки на вкладках Profile и EQ пишутся в активный профиль сразу — '
+ 'отдельной кнопки «сохранить» нет.', PAD, R1 + 30, GRP_W - 2 * PAD);
MakeLbl(Grp, 'New... создаёт копию текущего звука под новым именем. '
+ 'Factory — заводской набор заново, эквалайзер в ноль.',
MakeLbl(Grp, 'Edits on the Profile and EQ tabs go into the active profile '
+ 'immediately — there is no separate Save button.',
PAD, R1 + 30, GRP_W - 2 * PAD);
MakeLbl(Grp, 'New... forks the current sound under a new name. '
+ 'Factory rebuilds the stock set and flattens the equalizer.',
PAD, R1 + 48, GRP_W - 2 * PAD);
// ---- Сегменты подвкладок ------------------------------------------------
@@ -1543,31 +1544,34 @@ begin
SubPage(0, SubTop);
// ---- Микрофонный вход (часть профиля: гарнитура vs микшер-ESSB) ----
Grp := MakeGroupPanel(FTXSubPage[0], 'Microphone', MARGIN, 0, GRP_W, 176);
// Две строки, обе по общей сетке страницы: подпись в колонке PAD (ширина LW),
// поле в колонке CX, вторая пара — от PAD+320. Тумблеры справа с равным
// шагом. Connector скрыт на не-Orion и потому делит строку с Mic gain —
// спрятанная пара не оставляет дыры.
Grp := MakeGroupPanel(FTXSubPage[0], 'Microphone', MARGIN, 0, GRP_W, 134);
// Row 0: Mic jack selector | Boost (Mic In) или LineInGain (Line In) | Bias | PTT
MakeLbl(Grp, 'Mic jack', PAD, R1 + 5, 65);
FCmbTXMicJack := MkCmb(Grp, PAD + 68, R1, 110);
// Row 0: Mic jack | Boost (Mic In) либо Line In gain (Line In) | Bias | PTT
MakeLbl(Grp, 'Mic jack', PAD, R1 + 5, LW);
FCmbTXMicJack := MkCmb(Grp, CX, R1, 110);
FCmbTXMicJack.Items.Add('Mic In');
FCmbTXMicJack.Items.Add('Line In');
FCmbTXMicJack.ItemIndex := 0;
FCmbTXMicJack.OnChange := OnMicJackChange;
FChkTXMicBoost := MkChk(Grp, PAD + 190, R1, 165, 'Mic boost +20 dB');
FLblLineInGain := MakeLbl(Grp, 'Line In gain', PAD + 190, R1 + 5, 80);
FEdLineInGain := MkFloat(Grp, PAD + 190 + 82, R1, 0.0, -34.5, 12.0, 1.5);
// Boost и пара «Line In gain» занимают одно и то же место (видима одна из них).
FChkTXMicBoost := MkChk(Grp, PAD + 302, R1, 170, 'Mic boost +20 dB');
FLblLineInGain := MakeLbl(Grp, 'Line In gain', PAD + 302, R1 + 5, 76);
FEdLineInGain := MkFloat(Grp, PAD + 382, R1, 0.0, -34.5, 12.0, 1.5);
FEdLineInGain.DecimalPlaces := 1;
FChkTXMicBias := MkChk(Grp, PAD + 375, R1, 105, 'Mic bias');
FChkTXMicPTT := MkChk(Grp, PAD + 490, R1, 160, 'Mic-jack PTT');
FChkTXMicBias := MkChk(Grp, PAD + 490, R1, 100, 'Mic bias');
FChkTXMicPTT := MkChk(Grp, PAD + 596, R1, 130, 'Mic-jack PTT');
// Row 1: Mic gain
// Row 1: Mic gain | Connector Tip/Ring (только Orion boards 4 и 5)
MakeLbl(Grp, 'Mic gain (dB)', PAD, R1 + STEP + 5, LW);
FEdTXMicGain := MkFloat(Grp, CX, R1 + STEP, 10.0, -20.0, 60.0, 0.5);
// Row 2: Connector Tip/Ring (только Orion boards 4 и 5). Стоит последним —
// на не-Orion строка скрыта и не оставляет дыру посреди группы.
FLblTipRing := MakeLbl(Grp, 'Connector', PAD, R1 + 2 * STEP + 5, 70);
FCmbTXMicTR := MkCmb(Grp, PAD + 73, R1 + 2 * STEP, 90);
FLblTipRing := MakeLbl(Grp, 'Connector', PAD + 302, R1 + STEP + 5, 76);
FCmbTXMicTR := MkCmb(Grp, PAD + 382, R1 + STEP, 96);
FCmbTXMicTR.Items.Add('Tip'); // MicTipRing=False, bit3=0
FCmbTXMicTR.Items.Add('Ring'); // MicTipRing=True, bit3=1
FCmbTXMicTR.ItemIndex := 0;
@@ -1575,15 +1579,15 @@ begin
FCmbTXMicTR.Visible := False;
// ---- Кромки TX-фильтра (FIR/фаза/окно — на вкладке Hardware) ----
Y := 176 + 12;
Y := 134 + 12;
Grp := MakeGroupPanel(FTXSubPage[0], 'TX Filter (bandpass)', MARGIN, Y, GRP_W, 108);
MakeLbl(Grp, 'Low cut (Hz)', PAD, R1 + 5, LW);
FEdTXFiltLow := MkSpin(Grp, CX, R1, 0, 8000, 200);
MakeLbl(Grp, 'High cut (Hz)', PAD + 320, R1 + 5, 90);
FEdTXFiltHigh := MkSpin(Grp, PAD + 320 + 100, R1, 100, 12000, 3100);
MakeLbl(Grp, 'Ширина полосы голоса. ESSB — просто High cut 3500..5000; '
+ 'знак боковой ставит движок, здесь всегда магнитуда.',
MakeLbl(Grp, 'Voice bandwidth. ESSB is simply a high cut of 3500..5000 Hz; '
+ 'sideband sign is applied by the engine, these are magnitudes.',
PAD, R1 + 32, GRP_W - 2 * PAD);
// ---- Динамическая обработка ----
@@ -1621,9 +1625,9 @@ begin
MakeLbl(Grp, 'AM carrier level', PAD + 320, R1 + 5, 110);
FEdTXAMCarrier := MkFloat(Grp, PAD + 320 + 120, R1, 0.5, 0.0, 1.0, 0.05);
FLblTXProfDrive := MakeLbl(Grp, 'Drive профиля: —', PAD, R1 + STEP + 5, 300);
MakeLbl(Grp, 'Мощность — часть профиля, но крутится ползунком DRV в главном окне: '
+ 'текущее значение уходит в активный профиль само.',
FLblTXProfDrive := MakeLbl(Grp, 'Profile drive: —', PAD, R1 + STEP + 5, 300);
MakeLbl(Grp, 'Drive belongs to the profile, but you set it with the DRV slider '
+ 'in the main window — the active profile follows it.',
PAD, R1 + STEP + 28, GRP_W - 2 * PAD);
FTXSubPage[0].Height := DpiScale(Y + 148 + 12);
@@ -1646,13 +1650,15 @@ begin
// Главное, что надо знать про этот EQ: точки — не «полосы», а узлы кривой,
// и за крайними узлами WDSP обрывает АЧХ (см. WDSPEngine.PushTXEQProfile).
MakeLbl(Grp, 'Точки — узлы кривой: между ними интерполяция по дБ, '
+ 'за крайними точками АЧХ обрывается.', PAD, R1 + 332, GRP_W - 2 * PAD);
MakeLbl(Grp, 'Нижняя точка должна быть не выше нижней кромки TX-фильтра, '
+ 'верхняя — не ниже верхней, иначе EQ сам срежет полосу.',
MakeLbl(Grp, 'Points are curve nodes: the response is interpolated in dB '
+ 'between them and cut off beyond the outer ones.',
PAD, R1 + 332, GRP_W - 2 * PAD);
MakeLbl(Grp, 'Keep the lowest node at or below the TX filter low cut and the '
+ 'highest at or above the high cut, or the EQ will clip the passband.',
PAD, R1 + 352, GRP_W - 2 * PAD);
MakeLbl(Grp, 'В режиме 3 полос частоты задаёт WDSP (150 / 1500 / 6000 Гц) — '
+ 'двигается только усиление.', PAD, R1 + 372, GRP_W - 2 * PAD);
MakeLbl(Grp, 'In 3-band mode the frequencies are fixed by WDSP '
+ '(150 / 1500 / 6000 Hz) — only the gains move.',
PAD, R1 + 372, GRP_W - 2 * PAD);
FTXSubPage[1].Height := DpiScale(434 + 12);
@@ -1667,8 +1673,9 @@ begin
FEdTXAttOnTX := MkSpin(Grp, CX, R1, 0, 31, 31);
MakeLbl(Grp, 'Tune tone (Hz)', PAD + 320, R1 + 5, 110);
FEdTUNFreq := MkSpin(Grp, PAD + 320 + 120, R1, 100, 5000, 750);
MakeLbl(Grp, 'ATT on TX — ослабление RX-ADC на передаче (защита входа), '
+ 'не путать с мощностью.', PAD, R1 + 32, GRP_W - 2 * PAD);
MakeLbl(Grp, 'ATT on TX attenuates the RX ADC while transmitting (input '
+ 'protection) — not to be confused with output power.',
PAD, R1 + 32, GRP_W - 2 * PAD);
Y := 108 + 12;
Grp := MakeGroupPanel(FTXSubPage[2], 'TX Filter (DSP)', MARGIN, Y, GRP_W, 190);
@@ -1689,8 +1696,8 @@ begin
FCmbTXFiltWindow.Items.Add(WINDOW_NAMES[i]);
FCmbTXFiltWindow.ItemIndex := 1;
MakeLbl(Grp, 'Крутизна и задержка тракта, а не тембр — поэтому вне профиля.',
PAD, R1 + 3 * STEP + 5, GRP_W - 2 * PAD);
MakeLbl(Grp, 'Skirt steepness and path latency, not timbre — hence outside '
+ 'the profile.', PAD, R1 + 3 * STEP + 5, GRP_W - 2 * PAD);
Inc(Y, 190 + 12);
Grp := MakeGroupPanel(FTXSubPage[2], 'FM / CTCSS', MARGIN, Y, GRP_W, 250);
@@ -1712,8 +1719,8 @@ begin
MakeLbl(Grp, 'Freq (Hz)', PAD + 230, R1 + 3 * STEP + 5, 80);
FEdTXCTCSSFreq := MkFloat(Grp, PAD + 320, R1 + 3 * STEP, 100.0, 50.0, 300.0, 0.1);
MakeLbl(Grp, 'Свойство канала/репитера — профилем голоса не переключается.',
PAD, R1 + 4 * STEP + 5, GRP_W - 2 * PAD);
MakeLbl(Grp, 'A property of the channel or repeater — a voice profile does '
+ 'not switch it.', PAD, R1 + 4 * STEP + 5, GRP_W - 2 * PAD);
FTXSubPage[2].Height := DpiScale(Y + 250 + 12);
@@ -1934,12 +1941,12 @@ begin
if FCmbTXProfile.Items.Count >= TXPROF_MAX then
begin
MessageDlg('TX Profile',
Format('Больше %d профилей на устройство не поддерживается.', [TXPROF_MAX]),
Format('No more than %d profiles per radio.', [TXPROF_MAX]),
mtInformation, [mbOK], 0);
Exit;
end;
S := 'Profile ' + IntToStr(FCmbTXProfile.Items.Count + 1);
if not InputQuery('TX Profile', 'Имя нового профиля (копия текущего звука):', S) then Exit;
if not InputQuery('TX Profile', 'Name for the new profile (copy of the current sound):', S) then Exit;
if Trim(S) = '' then Exit;
if Assigned(FOnTXProfileAdd) then FOnTXProfileAdd(Trim(S));
end;
@@ -1951,7 +1958,7 @@ begin
Idx := FCmbTXProfile.ItemIndex;
if (Idx < 0) or (Idx >= FCmbTXProfile.Items.Count) then Exit;
S := FCmbTXProfile.Items[Idx];
if not InputQuery('TX Profile', 'Новое имя профиля:', S) then Exit;
if not InputQuery('TX Profile', 'New profile name:', S) then Exit;
if Trim(S) = '' then Exit;
if Assigned(FOnTXProfileRename) then FOnTXProfileRename(Idx, Trim(S));
end;
@@ -1964,7 +1971,7 @@ begin
// Последний профиль не удаляем — активному профилю всегда есть куда писать.
if (Idx < 0) or (FCmbTXProfile.Items.Count <= 1) then Exit;
if MessageDlg('TX Profile',
'Удалить профиль «' + FCmbTXProfile.Items[Idx] + '»?',
'Delete profile "' + FCmbTXProfile.Items[Idx] + '"?',
mtConfirmation, [mbYes, mbNo], 0) <> mrYes then Exit;
if Assigned(FOnTXProfileDelete) then FOnTXProfileDelete(Idx);
end;
@@ -1976,10 +1983,10 @@ procedure TSettingsForm.BtnTXProfFactoryClick(Sender: TObject);
// не доехали бы до тех, у кого секция tx_profiles уже записана.
begin
if MessageDlg('TX Profile',
'Заменить весь список профилей заводским набором?'#13#10
+ 'Свои профили и правки заводских будут потеряны.'#13#10
+ 'Активным станет «Default»: микрофон, полоса, динамика и мощность '
+ 'останутся как сейчас, эквалайзер вернётся в ноль.',
'Replace the whole profile list with the factory set?'#13#10
+ 'Your own profiles and edits to the stock ones will be lost.'#13#10
+ '"Default" becomes active: microphone, bandwidth, dynamics and '
+ 'drive stay as they are, the equalizer returns to flat.',
mtConfirmation, [mbYes, mbNo], 0) <> mrYes then Exit;
if Assigned(FOnTXProfileFactory) then FOnTXProfileFactory(Self);
end;
@@ -2001,7 +2008,7 @@ begin
if FBtnTXProfDel <> nil then
FBtnTXProfDel.Enabled := FCmbTXProfile.Items.Count > 1;
if FLblTXProfDrive <> nil then
FLblTXProfDrive.Caption := Format('Drive профиля: %d %%', [DrivePct]);
FLblTXProfDrive.Caption := Format('Profile drive: %d %%', [DrivePct]);
finally
FLoading := WasLoading;
end;
@@ -2010,7 +2017,7 @@ end;
procedure TSettingsForm.SetTXProfileDrive(DrivePct: Integer);
begin
if FLblTXProfDrive <> nil then
FLblTXProfDrive.Caption := Format('Drive профиля: %d %%', [DrivePct]);
FLblTXProfDrive.Caption := Format('Profile drive: %d %%', [DrivePct]);
end;
procedure TSettingsForm.ShowTransmitPage(SubIdx: Integer = 0);