feat(overlay): scrollable audio device list in AUD fly-out

Список устройств вывода ограничен AUD_MAX_VIS=6 видимыми строками — панель
больше не растёт бесконечно и не уезжает за низ спектра. При большем числе
устройств справа появляется полоса прокрутки ▲/▼ (строки сужаются под
колонку стрелок), клик листает FAudScroll не закрывая панель; стрелки гаснут
на краях. ≤6 устройств — как раньше, без стрелок.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-01 17:40:31 +03:00
co-authored by Claude Opus 4.8
parent 737af0d227
commit a04c592f1d
+68 -7
View File
@@ -58,6 +58,7 @@ type
FCurDev: string; // имя текущего устройства ('' = default)
FPanel: TFlyPanel; // открытая fly-out панель
FAudScroll: Integer; // прокрутка списка аудио-устройств (верхний индекс)
FVolTrack: TRect; // трек громкости (локальные координаты)
FVolDrag: Boolean;
@@ -159,6 +160,8 @@ const
HIT_VOL = -24;
HIT_SPLIT = -25;
HIT_TX = -26;
HIT_AUD_UP = -27;
HIT_AUD_DN = -28;
HIT_AGC_PICK_BASE = 100000;
HIT_DEV_BASE = 200000;
@@ -173,6 +176,8 @@ const
BAR_H = 24;
ROW_H = 20;
IGAP = 2;
AUD_ROW_H = 17; // высота строки устройства в AUD-панели
AUD_MAX_VIS = 6; // максимум видимых устройств (дальше — прокрутка)
OVL_BASE_H = PAD + HDR_H + MET_H + FRQ_H + BAR_H + PAD; // = 96
// Палитра
@@ -334,6 +339,7 @@ begin
FVolume := 70; FMuted := False; FSplit := False; FTx := False;
FTxSel := True; FSlice := 'A';
FPanel := fpNone;
FAudScroll := 0;
FVolDrag := False;
FDevNames := TStringList.Create;
FCurDev := '';
@@ -387,7 +393,7 @@ begin
case FPanel of
fpMode: Result := ROW_H*2 + IGAP + ROW_H + IGAP; // 2 ряда режимов + ряд фильтров
fpDsp: Result := ROW_H + IGAP + ROW_H; // toggles + AGC
fpAud: Result := Max(1, FDevNames.Count) * 17;
fpAud: Result := Min(Max(1, FDevNames.Count), AUD_MAX_VIS) * AUD_ROW_H;
else Result := 0;
end;
end;
@@ -401,6 +407,7 @@ end;
procedure TVfoOverlay.SetPanel(P: TFlyPanel);
begin
if FPanel = P then FPanel := fpNone else FPanel := P;
if FPanel = fpAud then FAudScroll := 0; // список с начала
Height := ComputeHeight;
RequestInvalidate;
end;
@@ -681,11 +688,14 @@ begin
end;
procedure TVfoOverlay.DrawAudPanel(C: TCanvas; X, Y, RowW: Integer);
const
ARROW_W = 15;
var
I: Integer;
I, Slot, VisN, RowWidth, MaxScroll, PanelH, ArrowX, MidY, CY: Integer;
HR: TRect;
Nm: string;
Cur: Boolean;
Cur, Scrollable, CanUp, CanDn: Boolean;
Tri: array[0..2] of TPoint;
begin
C.Font.Name := 'Courier New';
C.Font.Size := 7;
@@ -697,9 +707,18 @@ begin
C.TextOut(X, Y + 2, '(нет устройств)');
Exit;
end;
for I := 0 to FDevNames.Count - 1 do
VisN := Min(FDevNames.Count, AUD_MAX_VIS);
Scrollable := FDevNames.Count > AUD_MAX_VIS;
MaxScroll := FDevNames.Count - VisN;
if FAudScroll > MaxScroll then FAudScroll := MaxScroll;
if FAudScroll < 0 then FAudScroll := 0;
if Scrollable then RowWidth := RowW - ARROW_W - 2 else RowWidth := RowW;
for Slot := 0 to VisN - 1 do
begin
HR := Rect(X, Y + I*17, X + RowW, Y + I*17 + 16);
I := FAudScroll + Slot;
HR := Rect(X, Y + Slot*AUD_ROW_H, X + RowWidth, Y + Slot*AUD_ROW_H + 16);
Nm := FDevNames[I];
Cur := (Nm = FCurDev) or ((FCurDev = '') and (I = 0));
if Cur then C.Brush.Color := CLR_ACCENT_D
@@ -712,13 +731,50 @@ begin
C.RoundRect(HR.Left, HR.Top, HR.Right, HR.Bottom, 4, 4);
if Cur then C.Font.Color := CLR_ACCENT else C.Font.Color := CLR_TEXT;
C.Brush.Style := bsClear;
if C.TextWidth(Nm) > RowW - 8 then
while (Length(Nm) > 1) and (C.TextWidth(Nm + '…') > RowW - 8) do
if C.TextWidth(Nm) > RowWidth - 8 then
while (Length(Nm) > 1) and (C.TextWidth(Nm + '…') > RowWidth - 8) do
SetLength(Nm, Length(Nm) - 1);
if Nm <> FDevNames[I] then Nm := Nm + '…';
C.TextOut(HR.Left + 4, HR.Top + (16 - C.TextHeight('A')) div 2, Nm);
RegisterHit(HR, HIT_DEV_BASE + I);
end;
if not Scrollable then Exit;
// Полоса прокрутки справа: ▲ (верх) / ▼ (низ) + счётчик позиции.
PanelH := VisN * AUD_ROW_H - 1;
ArrowX := X + RowWidth + 2;
MidY := Y + PanelH div 2;
CanUp := FAudScroll > 0;
CanDn := FAudScroll < MaxScroll;
// верхняя стрелка
HR := Rect(ArrowX, Y, X + RowW, MidY - 1);
C.Brush.Color := CLR_BTN_NORM; C.Brush.Style := bsSolid;
C.Pen.Color := CLR_BTN_NORM; C.Pen.Style := psSolid;
C.RoundRect(HR.Left, HR.Top, HR.Right, HR.Bottom, 4, 4);
CY := (HR.Top + HR.Bottom) div 2;
if CanUp then C.Brush.Color := CLR_TEXT else C.Brush.Color := CLR_DIM;
C.Pen.Color := C.Brush.Color;
Tri[0] := Point((HR.Left+HR.Right) div 2, CY - 3);
Tri[1] := Point((HR.Left+HR.Right) div 2 - 4, CY + 2);
Tri[2] := Point((HR.Left+HR.Right) div 2 + 4, CY + 2);
C.Polygon(Tri);
RegisterHit(HR, HIT_AUD_UP);
// нижняя стрелка
HR := Rect(ArrowX, MidY + 1, X + RowW, Y + PanelH);
C.Brush.Color := CLR_BTN_NORM; C.Brush.Style := bsSolid;
C.Pen.Color := CLR_BTN_NORM; C.Pen.Style := psSolid;
C.RoundRect(HR.Left, HR.Top, HR.Right, HR.Bottom, 4, 4);
CY := (HR.Top + HR.Bottom) div 2;
if CanDn then C.Brush.Color := CLR_TEXT else C.Brush.Color := CLR_DIM;
C.Pen.Color := C.Brush.Color;
Tri[0] := Point((HR.Left+HR.Right) div 2, CY + 3);
Tri[1] := Point((HR.Left+HR.Right) div 2 - 4, CY - 2);
Tri[2] := Point((HR.Left+HR.Right) div 2 + 4, CY - 2);
C.Polygon(Tri);
RegisterHit(HR, HIT_AUD_DN);
end;
{ ---- Основная отрисовка ---- }
@@ -893,6 +949,11 @@ begin
if Assigned(FOnMute) then FOnMute(Self); Exit; end;
HIT_SPLIT: begin if Assigned(FOnSplit) then FOnSplit(Self); Exit; end;
HIT_TX: begin if Assigned(FOnTxSelect) then FOnTxSelect(Self); Exit; end;
HIT_AUD_UP: begin if FAudScroll > 0 then Dec(FAudScroll);
RequestInvalidate; Exit; end;
HIT_AUD_DN: begin if FAudScroll < FDevNames.Count - AUD_MAX_VIS then
Inc(FAudScroll);
RequestInvalidate; Exit; end;
end;
// Устройство аудио