add splitter

This commit is contained in:
2026-03-11 21:18:18 +03:00
parent e131af9ae5
commit 99463fa22f
+145 -8
View File
@@ -233,6 +233,12 @@ type
FSpectrumWidth: Integer;
FSpectrumHeight: Integer;
FWaterfallHeight:Integer;
// ---- Splitter между спектром и водопадом ----
FSplitter: TPanel; // визуальная полоса-разделитель
FSplitterDrag: Boolean; // True = идёт перетаскивание
FSplitterDragY0: Integer; // Y мыши в начале перетаскивания (в координатах PanelRight)
FSplitterSH0: Integer; // FSpectrumHeight в начале перетаскивания
FSplitterRatio: Double; // соотношение высоты спектра (0.15..0.85), сохраняется
// Водопад: прямой массив пикселей BGRA + RawImage bitmap без IntfImage roundtrip
FWfPixels: array of LongWord; // W×H пикселей, строка 0 = верх (новейшая)
FWfBitmap: TBitmap;
@@ -347,6 +353,7 @@ type
BtnHidePanel: TFlatButton; // скрыть/показать левую панель
PbSpectrum: TPaintBox;
PbRuler: TPaintBox; // полоса частотных меток между спектром и водопадом
PanelSplitter: TPanel; // перетаскиваемый разделитель спектр/водопад
PbWaterfall: TPaintBox;
// ---- Status bar ----
@@ -502,6 +509,13 @@ type
procedure SpectrumTimerTick(Sender: TObject);
procedure RightPanelResize(Sender: TObject);
procedure AfterShowTick(Sender: TObject);
// Splitter handlers
procedure SplitterMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
procedure SplitterMouseMove(Sender: TObject; Shift: TShiftState;
X, Y: Integer);
procedure SplitterMouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
published
// Обработчики событий формы — должны быть в published для RTTI/LFM
@@ -821,6 +835,8 @@ begin
FVfoOverlay.OnSelect := OnModeFilterSelect;
FVfoOverlay.Visible := False;
FPanelHidden := False;
FSplitterRatio := 0.40; // 40% спектр, 60% водопад по умолчанию
FSplitterDrag := False;
FSpectrumBitmap := TBitmap.Create;
FWaterfallBitmap := TBitmap.Create;
@@ -1331,6 +1347,17 @@ begin
PbRuler.OnPaint := PbRulerPaint;
PbRuler.Cursor := crDefault;
// ---- Splitter между спектром+линейкой и водопадом ----
PanelSplitter := TPanel.Create(Self);
PanelSplitter.Parent := PanelRight;
PanelSplitter.BevelOuter := bvNone;
PanelSplitter.Color := TColor($00303030);
PanelSplitter.Cursor := crVSplit;
PanelSplitter.Height := 5;
PanelSplitter.OnMouseDown := SplitterMouseDown;
PanelSplitter.OnMouseMove := SplitterMouseMove;
PanelSplitter.OnMouseUp := SplitterMouseUp;
PbWaterfall := TPaintBox.Create(Self);
PbWaterfall.Parent := PanelRight;
PbWaterfall.OnPaint := PbWaterfallPaint;
@@ -1352,6 +1379,97 @@ begin
ResizeSpectrumPanels;
end;
// ===========================================================================
// Splitter — перетаскивание границы спектр/водопад
// Обновление размеров происходит только при отпускании мыши (MouseUp),
// чтобы исключить фризы во время перетаскивания.
// Во время перетаскивания рисуем только призрак-линию на PanelSplitter.
// ===========================================================================
procedure TMainForm.SplitterMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
var
P: TPoint;
begin
if Button <> mbLeft then Exit;
FSplitterDrag := True;
FSplitterSH0 := FSpectrumHeight;
// Y в координатах PanelRight
P := PanelRight.ScreenToClient(PanelSplitter.ClientToScreen(Point(X, Y)));
FSplitterDragY0 := P.Y;
PanelSplitter.Color := TColor($005050A0); // подсветка при захвате
{$IFDEF WINDOWS}
SetCapture(PanelSplitter.Handle);
{$ENDIF}
end;
procedure TMainForm.SplitterMouseMove(Sender: TObject; Shift: TShiftState;
X, Y: Integer);
var
P: TPoint;
DeltaY: Integer;
NewSH: Integer;
TopOff, AvailH: Integer;
const
RULER_H = 18;
SPLITTER_H = 5;
MIN_SH = 60;
MIN_WH = 40;
begin
if not FSplitterDrag then Exit;
P := PanelRight.ScreenToClient(PanelSplitter.ClientToScreen(Point(X, Y)));
DeltaY := P.Y - FSplitterDragY0;
TopOff := PanelSpanButtons.Height;
AvailH := PanelRight.ClientHeight - TopOff - RULER_H - SPLITTER_H;
if AvailH <= 0 then Exit;
NewSH := FSplitterSH0 + DeltaY;
if NewSH < MIN_SH then NewSH := MIN_SH;
if NewSH > AvailH - MIN_WH then NewSH := AvailH - MIN_WH;
// Только двигаем сплиттер визуально — без пересчёта битмапов
PanelSplitter.Top := TopOff + NewSH + RULER_H;
PbWaterfall.Top := TopOff + NewSH + RULER_H + SPLITTER_H;
PbWaterfall.Height := AvailH - NewSH;
end;
procedure TMainForm.SplitterMouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
var
P: TPoint;
DeltaY: Integer;
NewSH: Integer;
TopOff, AvailH: Integer;
const
RULER_H = 18;
SPLITTER_H = 5;
MIN_SH = 60;
MIN_WH = 40;
begin
if not FSplitterDrag then Exit;
FSplitterDrag := False;
{$IFDEF WINDOWS}
ReleaseCapture;
{$ENDIF}
PanelSplitter.Color := TColor($00303030); // обычный цвет
P := PanelRight.ScreenToClient(PanelSplitter.ClientToScreen(Point(X, Y)));
DeltaY := P.Y - FSplitterDragY0;
TopOff := PanelSpanButtons.Height;
AvailH := PanelRight.ClientHeight - TopOff - RULER_H - SPLITTER_H;
if AvailH <= 0 then Exit;
NewSH := FSplitterSH0 + DeltaY;
if NewSH < MIN_SH then NewSH := MIN_SH;
if NewSH > AvailH - MIN_WH then NewSH := AvailH - MIN_WH;
// Сохраняем новое соотношение и делаем полный пересчёт с перерисовкой
FSplitterRatio := NewSH / AvailH;
ResizeSpectrumPanels;
end;
// ===========================================================================
// Resize handler (normal method instead of anonymous procedure)
// ===========================================================================
@@ -1398,8 +1516,14 @@ begin
end;
procedure TMainForm.ResizeSpectrumPanels;
const
RULER_H = 18;
SPLITTER_H = 5;
MIN_SH = 60; // минимальная высота спектра
MIN_WH = 40; // минимальная высота водопада
var
RW, RH, SH, WH, TopOff: Integer;
AvailH: Integer;
begin
if PanelRight = nil then Exit;
RW := PanelRight.ClientWidth;
@@ -1407,16 +1531,29 @@ begin
TopOff := PanelSpanButtons.Height;
// S-метр позиционируется отдельно в ResizeSMeter
ResizeSMeter;
RH := RH - TopOff;
if RH < 10 then Exit;
AvailH := RH - TopOff - RULER_H - SPLITTER_H;
if AvailH < (MIN_SH + MIN_WH) then Exit;
SH := RH * 4 div 10;
WH := RH - SH - 18;
if WH < 10 then WH := 10;
// Ограничиваем соотношение, чтобы каждая зона имела минимальный размер
if FSplitterRatio < MIN_SH / AvailH then
FSplitterRatio := MIN_SH / AvailH;
if FSplitterRatio > 1.0 - MIN_WH / AvailH then
FSplitterRatio := 1.0 - MIN_WH / AvailH;
SH := Round(AvailH * FSplitterRatio);
WH := AvailH - SH;
if WH < MIN_WH then WH := MIN_WH;
// Спектр
PbSpectrum.SetBounds(0, TopOff, RW, SH);
// Линейка частот — всегда прижата к низу спектра
PbRuler.SetBounds(0, TopOff + SH, RW, RULER_H);
// Сплиттер — между линейкой и водопадом
if PanelSplitter <> nil then
PanelSplitter.SetBounds(0, TopOff + SH + RULER_H, RW, SPLITTER_H);
// Водопад — под сплиттером
PbWaterfall.SetBounds(0, TopOff + SH + RULER_H + SPLITTER_H, RW, WH);
PbSpectrum.SetBounds(0, TopOff, RW, SH);
PbRuler.SetBounds (0, TopOff + SH, RW, 18);
PbWaterfall.SetBounds(0, TopOff + SH + 18, RW, WH);
PositionSpanOverlayButtons;
// Обновляем переменные размеров