feat(filter): произвольные кромки фильтра — таблица по режиму, редактор, drag, TX-полоска

Фильтр главного приёмника перестал быть одним числом. Теперь это таблица
(Name, Low, High) на режим: 10 пресетов + VAR1/VAR2, знаковые кромки (у LSB
своя строка, у USB своя), паритет с Thetis FilterForm. Слайсы независимые
кромки умели и раньше — главный приёмник настраивался только пресетами.

Модель (Settings/RadioController):
- TFilterSlot/TFilterSet/TFilterTable + заводские дефолты из прежних FILT_*_BW
  и FILT_*_NAMES (переехали в Settings — из них строится таблица).
- FFilterLo/FFilterHi = истина, FFilterBW производная (FilterBWFromEdges),
  поэтому все прежние читатели ширины (web, зум, band-cache, CAT) не тронуты.
- ApplyFilterSlot — единственная точка «слот → состояние тракта».
- SetFilterEdges: произвольные кромки уходят в VAR1 с автопереключением,
  пресеты нельзя испортить случайным движением мыши или энкодера.
- Персист: секция "filters" под MAC, пишутся только режимы, отличные от
  заводских; секция режима сносится целиком, иначе возврат слота к
  заводскому оставлял бы старый ключ.
- Диапазон помнит только выбранный слот: кромки живут в таблице, второго
  источника истины у VAR нет.

UI:
- 12 кнопок фильтра (2 ряда по 6), подписи из живой таблицы.
- FilterPopup — поповер редактора по ПКМ на кнопке фильтра, по образцу
  PureSignalPopup (дочерний контрол формы, не top-level: Wayland). Слева
  слоты, справа Name/Low/High/Width, внизу форма полосы на знаковой оси.
- Перетаскивание края полосы на спектре: ±5px, снап 10 Гц, Alt = симметрично
  (не Ctrl — он создаёт слайс). Отказ на передаче и когда полоса на экране
  уже 12px, иначе захват съедал бы клик-тюн.

Отрисовка:
- Полоса главного VFO рисуется по РЕАЛЬНЫМ кромкам приёмника, слайса — по его
  кромкам; вывод из режима и ширины остался фолбэком. Пара→ширина→пара было
  преобразованием с потерями: несимметричный фильтр из слайс-CAT (ZZFL/ZZFH)
  рисовался не там, где звучал.
- На передаче полоса считается по TX-фильтру (TXSignedEdges), а не по
  приёмной ширине — TX-полоса от RX не зависит (модель Thetis).
- CalcFilterBandXFor больше не держит свою копию таблицы знака боковой:
  единственная таблица теперь FilterEdgesFromBW.

Управление:
- CAT ZZFL/ZZFH заработали на основном порту (были заглушками).
- Андромеда: энкодеры #4/#5 — независимые Filter High/Low, как на железе G2
  (было: #4 ширина, #5 не реализован).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-04 13:15:33 +03:00
co-authored by Claude Opus 5
parent a820182716
commit 25521a2d10
13 changed files with 1329 additions and 308 deletions
+10 -4
View File
@@ -586,7 +586,13 @@ begin
TXX1 := 0;
TXX2 := 0;
TXVfoX := 0;
if FActiveVfo = 0 then CalcFilterBandX(FVfoA, W, X1, X2, VfoX)
// На передаче полоса активного VFO рисуется по TX-фильтру (см. CPU-путь).
if FTXOverlay and (FTXVfoIndex = FActiveVfo) then
begin
if FActiveVfo = 0 then CalcTXBandX(FVfoA, W, X1, X2, VfoX)
else CalcTXBandX(FVfoB, W, X1, X2, VfoX);
end
else if FActiveVfo = 0 then CalcFilterBandX(FVfoA, W, X1, X2, VfoX)
else CalcFilterBandX(FVfoB, W, X1, X2, VfoX);
if FTXOverlay then
@@ -594,8 +600,8 @@ begin
if FTXVfoIndex <> FActiveVfo then
begin
if X2 > X1 then DrawRect(X1, 0, X2, H, TColor($0030C030), 100 / 255);
if FTXVfoIndex = 0 then CalcFilterBandX(FVfoA, W, TXX1, TXX2, TXVfoX)
else CalcFilterBandX(FVfoB, W, TXX1, TXX2, TXVfoX);
if FTXVfoIndex = 0 then CalcTXBandX(FVfoA, W, TXX1, TXX2, TXVfoX)
else CalcTXBandX(FVfoB, W, TXX1, TXX2, TXVfoX);
if TXX2 > TXX1 then DrawRect(TXX1, 0, TXX2, H, CLR_TX_BAND, 100 / 255);
end
else if X2 > X1 then DrawRect(X1, 0, X2, H, CLR_TX_BAND, 100 / 255);
@@ -638,7 +644,7 @@ begin
// Передающий слайс — красная полоса (как у главного при TX), иначе оттенок бейджа.
if O.TxActive then Clr := CLR_TX_BAND
else Clr := SliceColor(O.SliceLetter);
CalcFilterBandXFor(O.DisplayVfoHz, O.DisplayMode, O.DisplayBW, W, SX1, SX2, SVfoX);
CalcSliceBandX(O, W, SX1, SX2, SVfoX);
if SX2 > SX1 then
DrawRect(SX1, 0, SX2, H, Clr, IfThen(O.TxActive, 100, 60) / 255);
DrawLine(SX1, 0, SX1, H, Clr, 1, False);