mirror of
https://git.vladimir.cc/vladimir/ewsdr.git
synced 2026-08-25 18:43:51 +00:00
ui(pans): грид из двух колонок — пан 0 и доп. пан бок о бок
Раньше «▦» имел смысл только при >=2 доп. панах (с одним грид совпадал со стеком) — основной и единственный дополнительный приёмник могли стоять лишь друг под другом. Теперь при ОДНОМ доп. пане грид = две колонки во всю высоту: пан 0 слева, доп. справа, между ними тот же вертикальный сплиттер. Wideband при этом остаётся сверху во всю ширину PanelRight, обе колонки начинаются под ним (StackTop=AWideH, пан 0 раскладывается с ATopOffset=0, шапки панов уходят в свои колонки — Pan0HeaderH над wideband не резервируется). MainForm: ExtraPanCount/PanGridTwoCol; FPanColShare расширен на индекс 0 (пан 0 — тоже колонка), доля персистится через Cfg.Pans[0].ColShare — ключ col_share в Settings уже был, теперь пишется для всех панов; отдельная ветка в LayoutPanStack (горизонтальные сплиттеры спрятаны, кламп 15% ширины на колонку); vsplit-хендлеры ищут левую колонку downto 0 в two-col и нормируют доли с учётом пана 0; PanSplitMouseDown/Up гейтятся not PanGridTwoCol; кнопка «▦» видна уже при одном доп. пане. Отдельно исправлен sync-гард в тике дисплея: он сверял PbWideband.Width с FPan.SpectrumWidth, а в two-col пан 0 — половина ширины при wideband во всю, поэтому условие было вечно истинным и ResizeSpectrumPanels вызывался каждый кадр — раскладка пересчитывалась под курсором (вертикальный сплиттер не тянулся) и зря сбрасывались битмапы. Теперь wideband сверяется с PanelRight.ClientWidth — его настоящим инвариантом. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+94
-26
@@ -189,11 +189,12 @@ type
|
||||
FPanSplitTop0: Integer;
|
||||
FPanSplitMinTop: Integer;
|
||||
FPanSplitMaxTop: Integer;
|
||||
// Грид-раскладка: пан 0 сверху во всю ширину, доп. паны колонками снизу.
|
||||
// Активна при FPanGridMode и >=2 доп. панах в стеке (иначе обычный стек).
|
||||
// Грид-раскладка: при >=2 доп. панах — пан 0 сверху во всю ширину, доп.
|
||||
// паны колонками снизу; при ОДНОМ доп. пане — две колонки бок о бок
|
||||
// (пан 0 слева, доп. справа). Активна при FPanGridMode и >=1 доп. пане.
|
||||
FPanGridMode: Boolean;
|
||||
FPanGridRowShare: Double; // доля высоты верхнего ряда (пан 0), 0.15..0.85
|
||||
FPanColShare: array[1..MAX_PANS-1] of Double; // доли ширины колонок
|
||||
FPanColShare: array[0..MAX_PANS-1] of Double; // доли ширины колонок
|
||||
FPanVSplitters: array[1..MAX_PANS-1] of TPanel; // слева от колонки пана i
|
||||
FPansRestored: Boolean; // персист панов восстановлен в этой Run-сессии
|
||||
// Drag вертикального сплиттера (индекс пана справа; 0 = нет).
|
||||
@@ -684,6 +685,8 @@ type
|
||||
procedure PanSplitMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
|
||||
procedure OnPanGridToggle(Sender: TObject); // «▦/▤» в ряду зума пана 0
|
||||
function PanGridActive: Boolean; // грид реально применён
|
||||
function PanGridTwoCol: Boolean; // грид из 2 колонок (пан 0 + 1)
|
||||
function ExtraPanCount: Integer; // доп. паны в раскладке
|
||||
procedure PanVSplitMouseDown(Sender: TObject; Button: TMouseButton;
|
||||
Shift: TShiftState; X, Y: Integer);
|
||||
procedure PanVSplitMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
|
||||
@@ -1172,6 +1175,7 @@ begin
|
||||
FPan.OnDisplayClick := OnPanDisplayClick; // «◑» дисплей-поповер (3.6)
|
||||
FPans[0] := FPan;
|
||||
FPanShare[0] := 1.0;
|
||||
FPanColShare[0] := 1.0; // грид «две колонки»: пан 0 — тоже колонка
|
||||
FPanGridMode := False;
|
||||
FPanGridRowShare := 0.5; // грид: пан 0 — верхняя половина
|
||||
FPanCbLock := SyncObjs.TCriticalSection.Create;
|
||||
@@ -2700,7 +2704,7 @@ procedure TMainForm.ResizeSpectrumPanels;
|
||||
const
|
||||
WB_H = 118;
|
||||
var
|
||||
RW, RH, WideH, WideSpecH: Integer;
|
||||
RW, RH, WideH, WideSpecH, WideTop: Integer;
|
||||
RULER_H: Integer;
|
||||
PANZOOM_H: Integer;
|
||||
begin
|
||||
@@ -2721,10 +2725,13 @@ begin
|
||||
WideH := Min(MulDiv(WB_H, Screen.PixelsPerInch, 96), Max(40, RH div 2));
|
||||
if WideH > 0 then
|
||||
WideSpecH := Max(1, WideH - RULER_H);
|
||||
// Wideband — всегда во всю ширину PanelRight, самой верхней полосой.
|
||||
// В обычном стеке над ним стоит шапка пана 0; в гриде «две колонки» шапки
|
||||
// панов уходят в свои колонки ПОД wideband, поэтому он начинается с нуля.
|
||||
WideTop := IfThen(PanGridTwoCol, 0, Pan0HeaderH);
|
||||
if PbWideband <> nil then
|
||||
begin
|
||||
// При стеке панов над wideband стоит шапка пана 0.
|
||||
PbWideband.SetBounds(0, Pan0HeaderH, RW, WideSpecH);
|
||||
PbWideband.SetBounds(0, WideTop, RW, WideSpecH);
|
||||
PbWideband.Visible := FController.FShowWideband and (WideSpecH > 0);
|
||||
if FController.FShowWideband and (RW > 0) and (WideSpecH > 0) then
|
||||
begin
|
||||
@@ -2734,7 +2741,7 @@ begin
|
||||
end;
|
||||
if PbWidebandRuler <> nil then
|
||||
begin
|
||||
PbWidebandRuler.SetBounds(0, Pan0HeaderH + WideSpecH, RW, IfThen(FController.FShowWideband and (WideH > 0), RULER_H, 0));
|
||||
PbWidebandRuler.SetBounds(0, WideTop + WideSpecH, RW, IfThen(FController.FShowWideband and (WideH > 0), RULER_H, 0));
|
||||
PbWidebandRuler.Visible := FController.FShowWideband and (WideH > 0);
|
||||
if FController.FShowWideband and (RW > 0) and (WideH > 0) then
|
||||
begin
|
||||
@@ -2768,15 +2775,26 @@ begin
|
||||
Result := 0;
|
||||
end;
|
||||
|
||||
function TMainForm.PanGridActive: Boolean;
|
||||
// Грид реально применён: включён юзером и в стеке >=2 доп. панов
|
||||
// (с одним доп. паном грид совпадает со стеком).
|
||||
var i, N: Integer;
|
||||
function TMainForm.ExtraPanCount: Integer;
|
||||
// Доп. паны (без пана 0), участвующие в раскладке (плавающие не в счёт).
|
||||
var i: Integer;
|
||||
begin
|
||||
N := 0;
|
||||
Result := 0;
|
||||
for i := 1 to MAX_PANS - 1 do
|
||||
if (FPans[i] <> nil) and not PanFloating(i) then Inc(N);
|
||||
Result := FPanGridMode and (N >= 2);
|
||||
if (FPans[i] <> nil) and not PanFloating(i) then Inc(Result);
|
||||
end;
|
||||
|
||||
function TMainForm.PanGridActive: Boolean;
|
||||
// Грид реально применён: включён юзером и есть хотя бы один доп. пан в стеке.
|
||||
begin
|
||||
Result := FPanGridMode and (ExtraPanCount >= 1);
|
||||
end;
|
||||
|
||||
function TMainForm.PanGridTwoCol: Boolean;
|
||||
// Частный случай грида: пан 0 + ОДИН доп. пан = две колонки бок о бок во всю
|
||||
// высоту (верхнего ряда/горизонтального сплиттера нет).
|
||||
begin
|
||||
Result := FPanGridMode and (ExtraPanCount = 1);
|
||||
end;
|
||||
|
||||
function TMainForm.PanMinHeight(PanId: Integer): Integer;
|
||||
@@ -2795,7 +2813,7 @@ const
|
||||
var
|
||||
ActiveIds: array[0..MAX_PANS-1] of Integer;
|
||||
MinH: array[0..MAX_PANS-1] of Integer;
|
||||
N, i, k, TotalH, TotalW, UsableH, UsableW, Y, X, Hi, Wi: Integer;
|
||||
N, i, k, TotalH, TotalW, UsableH, UsableW, Y, X, Hi, Wi, MinW: Integer;
|
||||
Row0H, RowTop, RowH, M, MinRest, RemainH, BotMin: Integer;
|
||||
ShareSum: Double;
|
||||
Grid: Boolean;
|
||||
@@ -2809,14 +2827,15 @@ begin
|
||||
FPanWideH := AWideH; // для PanMinHeight (в т.ч. из сплиттер-драга)
|
||||
TotalH := PanelRight.ClientHeight;
|
||||
TotalW := PanelRight.ClientWidth;
|
||||
Grid := PanGridActive; // пан 0 + >=2 доп. панов колонками
|
||||
Grid := PanGridActive; // 2 колонки (пан 0 + 1 доп.) либо ряд + колонки
|
||||
|
||||
// Тумблер «▦/▤» в ряду зума пана 0: есть смысл при >=2 доп. панах в стеке.
|
||||
// Тумблер «▦/▤» в ряду зума пана 0: есть смысл уже при ОДНОМ доп. пане
|
||||
// (грид = пан 0 и доп. пан бок о бок вместо «друг под другом»).
|
||||
if FPans[0] <> nil then
|
||||
begin
|
||||
// Виден при >=2 доп. панах; при включённом гриде — всегда (выключить можно
|
||||
// Виден при >=1 доп. пане; при включённом гриде — всегда (выключить можно
|
||||
// даже когда паны закрылись и раскладка сама откатилась в стек).
|
||||
FPans[0].GridBtnEnabled := FPanGridMode or (N >= 3);
|
||||
FPans[0].GridBtnEnabled := FPanGridMode or (N >= 2);
|
||||
if FPans[0].BtnGrid <> nil then
|
||||
begin
|
||||
if FPanGridMode then FPans[0].BtnGrid.Caption := '▤'
|
||||
@@ -2885,6 +2904,47 @@ begin
|
||||
Exit;
|
||||
end;
|
||||
|
||||
if PanGridTwoCol then
|
||||
begin
|
||||
// ---- Грид «две колонки»: пан 0 слева, единственный доп. пан справа ----
|
||||
// Wideband остаётся сверху во всю ширину (его полосу кладёт
|
||||
// ResizeSpectrumPanels), обе колонки начинаются ПОД ним и идут до низа;
|
||||
// между ними вертикальный сплиттер (доли FPanColShare[0]/FPanColShare[доп]).
|
||||
i := ActiveIds[1]; // единственный застекованный доп. пан
|
||||
for k := 1 to MAX_PANS - 1 do
|
||||
begin
|
||||
if FPanSplitters[k] <> nil then FPanSplitters[k].Visible := False;
|
||||
if (k <> i) and (FPanVSplitters[k] <> nil) then
|
||||
FPanVSplitters[k].Visible := False;
|
||||
end;
|
||||
FPans[0].HeaderVisible := Pan0HeaderVisible;
|
||||
FPans[i].HeaderVisible := True;
|
||||
UsableW := TotalW - PAN_SPLIT_H;
|
||||
ShareSum := FPanColShare[0] + FPanColShare[i];
|
||||
if ShareSum <= 0 then ShareSum := 1;
|
||||
MinW := Round(0.15 * TotalW); // = кламп драга вертикального сплиттера
|
||||
Wi := Round(UsableW * FPanColShare[0] / ShareSum);
|
||||
if Wi < MinW then Wi := MinW;
|
||||
if Wi > UsableW - MinW then Wi := UsableW - MinW;
|
||||
if Wi < 1 then Wi := 1;
|
||||
RowTop := AWideH; // низ полосы wideband = верх обеих колонок
|
||||
RowH := TotalH - RowTop;
|
||||
FPans[0].StackTop := RowTop;
|
||||
FPans[0].StackHeight := RowH;
|
||||
FPans[0].StackLeft := 0;
|
||||
FPans[0].StackWidth := Wi;
|
||||
// ATopOffset=0: wideband вне региона пана 0 (он уже учтён в StackTop).
|
||||
FPans[0].Layout(0, FController.FShowSpectrum, FController.FShowWaterfall);
|
||||
FPanVSplitters[i].Visible := True;
|
||||
FPanVSplitters[i].SetBounds(Wi, RowTop, PAN_SPLIT_H, RowH);
|
||||
FPans[i].StackTop := RowTop;
|
||||
FPans[i].StackHeight := RowH;
|
||||
FPans[i].StackLeft := Wi + PAN_SPLIT_H;
|
||||
FPans[i].StackWidth := TotalW - Wi - PAN_SPLIT_H;
|
||||
FPans[i].Layout(0, FController.FShowSpectrum, FController.FShowWaterfall);
|
||||
Exit;
|
||||
end;
|
||||
|
||||
// ---- Грид: пан 0 сверху во всю ширину, доп. паны колонками снизу ----
|
||||
FPanGridRowShare := EnsureRange(FPanGridRowShare, 0.15, 0.85);
|
||||
Row0H := Round((TotalH - PAN_SPLIT_H) * FPanGridRowShare);
|
||||
@@ -3466,8 +3526,13 @@ begin
|
||||
// Важно: проверяем только видимые панели — когда спектр скрыт,
|
||||
// PbSpectrum.Height может быть ненулевым при FPan.SpectrumHeight=0,
|
||||
// что вызвало бы сброс буфера водопада на каждом тике.
|
||||
// Wideband сверяем с шириной PanelRight, а НЕ со спектром пана 0: в гриде
|
||||
// «две колонки» пан 0 — половина ширины, а wideband остаётся во всю (иначе
|
||||
// условие вечно истинно и тик дёргает ResizeSpectrumPanels каждый кадр —
|
||||
// раскладка пересчитывается под курсором и сплиттеры не тянутся).
|
||||
if (FPan.SpectrumWidth = 0) or
|
||||
(FController.FShowWideband and (PbWideband.Width <> FPan.SpectrumWidth)) or
|
||||
(FController.FShowWideband and (PanelRight <> nil) and
|
||||
(PbWideband.Width <> PanelRight.ClientWidth)) or
|
||||
(FController.FShowSpectrum and ((PbSpectrum.Width <> FPan.SpectrumWidth) or
|
||||
(PbSpectrum.Height <> FPan.SpectrumHeight))) or
|
||||
(FController.FShowWaterfall and not FController.FShowSpectrum and
|
||||
@@ -6714,6 +6779,7 @@ begin
|
||||
for i := 0 to MAX_PANS - 1 do
|
||||
begin
|
||||
Cfg.Pans[i].StackShare := FPanShare[i];
|
||||
Cfg.Pans[i].ColShare := FPanColShare[i]; // пан 0 — колонка в гриде «2 колонки»
|
||||
if FPans[i] <> nil then Cfg.Pans[i].SplitRatio := FPans[i].SplitterRatio;
|
||||
if i > 0 then
|
||||
begin
|
||||
@@ -6724,7 +6790,6 @@ begin
|
||||
Cfg.Pans[i].ADC := FController.PanADC(i);
|
||||
Cfg.Pans[i].Zoom := FController.PanZoomFactor(i);
|
||||
Cfg.Pans[i].PanSlider := FController.PanPanSlider(i);
|
||||
Cfg.Pans[i].ColShare := FPanColShare[i];
|
||||
// Per-pan дисплей (3.6): пишем override только если пан его задал.
|
||||
if (FPans[i] <> nil) and FPans[i].DisplayOverride then
|
||||
begin
|
||||
@@ -6823,6 +6888,7 @@ begin
|
||||
|
||||
// Пан 0: восстановить его доп. слайсы (главный VFO — не слайс) + доли.
|
||||
FPanShare[0] := Cfg.Pans[0].StackShare;
|
||||
FPanColShare[0] := Cfg.Pans[0].ColShare;
|
||||
FPan.SplitterRatio := Cfg.Pans[0].SplitRatio;
|
||||
for k := 0 to High(Cfg.Pans[0].Slices) do
|
||||
RestoreSliceInto(FPan, 0, Cfg.Pans[0].Slices[k]);
|
||||
@@ -7086,6 +7152,7 @@ var
|
||||
Split: TPanel;
|
||||
begin
|
||||
if Button <> mbLeft then Exit;
|
||||
if PanGridTwoCol then Exit; // две колонки: горизонтальных сплиттеров нет
|
||||
Split := TPanel(Sender);
|
||||
BelowId := Split.Tag;
|
||||
TotalH := PanelRight.ClientHeight;
|
||||
@@ -7148,7 +7215,7 @@ begin
|
||||
TPanel(Sender).Color := TColor($00303030);
|
||||
TotalH := PanelRight.ClientHeight;
|
||||
if TotalH <= 0 then Exit;
|
||||
if PanGridActive then
|
||||
if PanGridActive and not PanGridTwoCol then
|
||||
begin
|
||||
// Грид: сплиттер задаёт долю высоты верхнего ряда (пан 0) напрямую.
|
||||
FPanGridRowShare := EnsureRange(TPanel(Sender).Top / TotalH, 0.15, 0.85);
|
||||
@@ -7190,9 +7257,10 @@ begin
|
||||
if (Button <> mbLeft) or not PanGridActive then Exit;
|
||||
Split := TPanel(Sender);
|
||||
RightId := Split.Tag;
|
||||
// Колонка слева — ближайший застекованный доп. пан с меньшим PanId.
|
||||
// Колонка слева — ближайший застекованный пан с меньшим PanId (в гриде из
|
||||
// двух колонок левая колонка — сам пан 0, в ряду колонок — доп. пан).
|
||||
LeftId := -1;
|
||||
for i := RightId - 1 downto 1 do
|
||||
for i := RightId - 1 downto IfThen(PanGridTwoCol, 0, 1) do
|
||||
if (FPans[i] <> nil) and not PanFloating(i) then begin LeftId := i; Break; end;
|
||||
if (LeftId < 0) or (FPans[RightId] = nil) then Exit;
|
||||
TotalW := PanelRight.ClientWidth;
|
||||
@@ -7231,14 +7299,14 @@ begin
|
||||
FPanVSplitDrag := 0;
|
||||
TPanel(Sender).Color := TColor($00303030);
|
||||
LeftId := -1;
|
||||
for i := RightId - 1 downto 1 do
|
||||
for i := RightId - 1 downto IfThen(PanGridTwoCol, 0, 1) do
|
||||
if (FPans[i] <> nil) and not PanFloating(i) then begin LeftId := i; Break; end;
|
||||
if (LeftId < 0) or (FPans[RightId] = nil) then Exit;
|
||||
TotalW := PanelRight.ClientWidth;
|
||||
if TotalW <= 0 then Exit;
|
||||
// Доли колонок нормируются суммой (init 1.0 на пан) — пиксели → доли через неё.
|
||||
ShareSum := 0;
|
||||
for i := 1 to MAX_PANS - 1 do
|
||||
for i := IfThen(PanGridTwoCol, 0, 1) to MAX_PANS - 1 do
|
||||
if (FPans[i] <> nil) and not PanFloating(i) then
|
||||
ShareSum := ShareSum + FPanColShare[i];
|
||||
if ShareSum <= 0 then ShareSum := 1;
|
||||
|
||||
@@ -17,9 +17,9 @@
|
||||
<UseVersionInfo Value="True"/>
|
||||
<AutoIncrementBuild Value="True"/>
|
||||
<MinorVersionNr Value="9"/>
|
||||
<BuildNr Value="278"/>
|
||||
<BuildNr Value="283"/>
|
||||
</VersionInfo>
|
||||
<MacroValues Count="110">
|
||||
<MacroValues Count="115">
|
||||
<Macro1 Name="LCLWidgetType" Value="qt6"/>
|
||||
<Macro2 Name="LCLWidgetType" Value="qt6"/>
|
||||
<Macro3 Name="LCLWidgetType" Value="qt6"/>
|
||||
@@ -130,6 +130,11 @@
|
||||
<Macro108 Name="LCLWidgetType" Value="qt6"/>
|
||||
<Macro109 Name="LCLWidgetType" Value="qt6"/>
|
||||
<Macro110 Name="LCLWidgetType" Value="qt6"/>
|
||||
<Macro111 Name="LCLWidgetType" Value="qt6"/>
|
||||
<Macro112 Name="LCLWidgetType" Value="qt6"/>
|
||||
<Macro113 Name="LCLWidgetType" Value="qt6"/>
|
||||
<Macro114 Name="LCLWidgetType" Value="qt6"/>
|
||||
<Macro115 Name="LCLWidgetType" Value="qt6"/>
|
||||
</MacroValues>
|
||||
<BuildModes>
|
||||
<Item Name="Debug" Default="True"/>
|
||||
|
||||
Reference in New Issue
Block a user