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