feat(slices): non-overlapping flag packing + per-slice colored letters

Flags (A + B+) now lay out in one pass: each sits beside its own filter
band as before, but a left neighbour pushes the next flag right so they
never cross (forward pack + backward pull-in on right-edge overflow).
LayoutFlags replaces the per-flag positioning; PositionVfoOverlay/
PositionSliceFlags are thin wrappers.

Identity is shown by a letter drawn at the top of each filter band in
the spectrum (both CPU + GL renderers), so a flag pushed away from its
band is still traceable. Letters and the flag letter-badge share one
per-slice palette (SliceColor); F is violet, not red, to avoid clashing
with the TX indicator.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-02 13:19:50 +03:00
co-authored by Claude Opus 4.8
parent 3fbde19b9f
commit 30a6616dbe
4 changed files with 177 additions and 102 deletions
+27
View File
@@ -110,6 +110,7 @@ type
procedure DrawSpectrumGradient(const SpPts: array of TPoint; W, H: Integer);
procedure DrawMarkerLine(C: TCanvas; W, H: Integer);
procedure DrawSliceFilterMarkers(C: TCanvas; W, H: Integer);
procedure DrawBandLetter(C: TCanvas; X1, X2: Integer; L: Char; Clr: TColor);
procedure DrawBeaconMarkers(C: TCanvas; W, H: Integer);
procedure BlendBand(X1, X2, H: Integer; R, G, B, Alpha: Byte);
procedure CopyGridToSpectrum(W, H: Integer);
@@ -640,9 +641,29 @@ begin
C.Pen.Width := 2;
C.MoveTo(SVfoX, 0); C.LineTo(SVfoX, H - 12);
C.Pen.Width := 1;
// Буква слайса внутри полосы фильтра сверху — привязка полосы к флагу,
// цвет = цвет слайса (совпадает с бейджем во флаге).
DrawBandLetter(C, SX1, SX2, O.SliceLetter, SliceColor(O.SliceLetter));
end;
end;
// Буква слайса (A/B/…) по центру полосы фильтра, у самого верха — чтобы
// понять, какому флагу принадлежит полоса, даже когда флаги стоят рядом.
procedure TSpectrumView.DrawBandLetter(C: TCanvas; X1, X2: Integer;
L: Char; Clr: TColor);
var
cx: Integer;
begin
if (L < 'A') or (L > 'Z') then Exit;
cx := (X1 + X2) div 2;
C.Font.Name := 'Courier New'; C.Font.Size := 8; C.Font.Style := [fsBold];
C.Font.Color := Clr;
C.Brush.Style := bsClear;
C.TextOut(cx - C.TextWidth(L) div 2, 0, L);
C.Brush.Style := bsSolid;
C.Font.Style := [];
end;
procedure TSpectrumView.CopyGridToSpectrum(W, H: Integer);
var
Y: Integer;
@@ -979,6 +1000,12 @@ begin
C.Pen.Style := psSolid;
end;
// Буква главного флага (A) на его полосе — только когда есть слайсы,
// иначе одиночный приём не засоряем.
if (FSliceOverlays <> nil) and (FSliceOverlays.Count > 0) and
(FActiveVfo = 0) and Assigned(FVfoOverlay) then
DrawBandLetter(C, X1, X2, FVfoOverlay.SliceLetter, SliceColor(FVfoOverlay.SliceLetter));
// Полосы фильтра слайсов (B+) — под кривой спектра, как в GL-вьюхе.
DrawSliceFilterMarkers(C, W, H);