mirror of
https://git.vladimir.cc/vladimir/ewsdr.git
synced 2026-08-26 21:37:32 +00:00
Render sample rate overlay into spectrum bitmap
This commit is contained in:
+35
-5
@@ -438,6 +438,7 @@ type
|
|||||||
procedure SetRadioOfflineStatus(const ConnText: string; ClearDevice: Boolean = False);
|
procedure SetRadioOfflineStatus(const ConnText: string; ClearDevice: Boolean = False);
|
||||||
procedure ApplyStatusTheme(const T: TAppTheme);
|
procedure ApplyStatusTheme(const T: TAppTheme);
|
||||||
procedure PositionSampleRateOverlay;
|
procedure PositionSampleRateOverlay;
|
||||||
|
procedure OnSampleRateOverlayInvalidate(Sender: TObject);
|
||||||
procedure OnSampleRateSelect(SampleRate: Integer);
|
procedure OnSampleRateSelect(SampleRate: Integer);
|
||||||
procedure OnSampleRateHidePanel;
|
procedure OnSampleRateHidePanel;
|
||||||
procedure UpdateVfoDisplay;
|
procedure UpdateVfoDisplay;
|
||||||
@@ -523,6 +524,7 @@ type
|
|||||||
X, Y: Integer);
|
X, Y: Integer);
|
||||||
procedure PbSpectrumMouseUp(Sender: TObject; Button: TMouseButton;
|
procedure PbSpectrumMouseUp(Sender: TObject; Button: TMouseButton;
|
||||||
Shift: TShiftState; X, Y: Integer);
|
Shift: TShiftState; X, Y: Integer);
|
||||||
|
procedure PbSpectrumMouseLeave(Sender: TObject);
|
||||||
procedure PbWaterfallMouseDown(Sender: TObject; Button: TMouseButton;
|
procedure PbWaterfallMouseDown(Sender: TObject; Button: TMouseButton;
|
||||||
Shift: TShiftState; X, Y: Integer);
|
Shift: TShiftState; X, Y: Integer);
|
||||||
procedure PbWaterfallMouseMove(Sender: TObject; Shift: TShiftState;
|
procedure PbWaterfallMouseMove(Sender: TObject; Shift: TShiftState;
|
||||||
@@ -1929,10 +1931,11 @@ begin
|
|||||||
PanelRight.OnResize := RightPanelResize;
|
PanelRight.OnResize := RightPanelResize;
|
||||||
|
|
||||||
FSampleRateOverlay := TSampleRateOverlay.Create(Self);
|
FSampleRateOverlay := TSampleRateOverlay.Create(Self);
|
||||||
FSampleRateOverlay.Parent := PanelRight;
|
|
||||||
FSampleRateOverlay.OnSpanSelect := OnSampleRateSelect;
|
FSampleRateOverlay.OnSpanSelect := OnSampleRateSelect;
|
||||||
FSampleRateOverlay.OnHidePanel := OnSampleRateHidePanel;
|
FSampleRateOverlay.OnHidePanel := OnSampleRateHidePanel;
|
||||||
|
FSampleRateOverlay.OnInvalidate := OnSampleRateOverlayInvalidate;
|
||||||
FSampleRateOverlay.SetCurrentRate(FSampleRate);
|
FSampleRateOverlay.SetCurrentRate(FSampleRate);
|
||||||
|
FSpecView.SampleRateOverlay := FSampleRateOverlay;
|
||||||
|
|
||||||
// S-метр — внутри PanelToolbar, справа
|
// S-метр — внутри PanelToolbar, справа
|
||||||
PanelSMeterRight := TPanel.Create(Self);
|
PanelSMeterRight := TPanel.Create(Self);
|
||||||
@@ -1952,6 +1955,7 @@ begin
|
|||||||
PbSpectrum.OnDblClick := PbSpectrumDblClick;
|
PbSpectrum.OnDblClick := PbSpectrumDblClick;
|
||||||
PbSpectrum.OnMouseMove := PbSpectrumMouseMove;
|
PbSpectrum.OnMouseMove := PbSpectrumMouseMove;
|
||||||
PbSpectrum.OnMouseUp := PbSpectrumMouseUp;
|
PbSpectrum.OnMouseUp := PbSpectrumMouseUp;
|
||||||
|
PbSpectrum.OnMouseLeave := PbSpectrumMouseLeave;
|
||||||
|
|
||||||
PbRuler := TPaintBox.Create(Self);
|
PbRuler := TPaintBox.Create(Self);
|
||||||
PbRuler.Parent := PanelRight;
|
PbRuler.Parent := PanelRight;
|
||||||
@@ -2538,15 +2542,22 @@ end;
|
|||||||
|
|
||||||
procedure TMainForm.PositionSampleRateOverlay;
|
procedure TMainForm.PositionSampleRateOverlay;
|
||||||
const
|
const
|
||||||
MARGIN = 4;
|
MARGIN_X = 34;
|
||||||
|
MARGIN_Y = 4;
|
||||||
begin
|
begin
|
||||||
if (FSampleRateOverlay = nil) or (PbSpectrum = nil) then Exit;
|
if (FSampleRateOverlay = nil) or (PbSpectrum = nil) then Exit;
|
||||||
FSampleRateOverlay.SetBounds(
|
FSampleRateOverlay.SetBounds(
|
||||||
PbSpectrum.Left + MARGIN,
|
MARGIN_X,
|
||||||
PbSpectrum.Top + MARGIN,
|
MARGIN_Y,
|
||||||
FSampleRateOverlay.Width,
|
FSampleRateOverlay.Width,
|
||||||
FSampleRateOverlay.Height);
|
FSampleRateOverlay.Height);
|
||||||
FSampleRateOverlay.BringToFront;
|
end;
|
||||||
|
|
||||||
|
procedure TMainForm.OnSampleRateOverlayInvalidate(Sender: TObject);
|
||||||
|
begin
|
||||||
|
FSpectrumDirty := True;
|
||||||
|
if Assigned(FSpecView) then FSpecView.SpectrumDirty := True;
|
||||||
|
if Assigned(PbSpectrum) then PbSpectrum.Invalidate;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
// ===========================================================================
|
// ===========================================================================
|
||||||
@@ -4637,6 +4648,9 @@ end;
|
|||||||
procedure TMainForm.PbSpectrumMouseDown(Sender: TObject; Button: TMouseButton;
|
procedure TMainForm.PbSpectrumMouseDown(Sender: TObject; Button: TMouseButton;
|
||||||
Shift: TShiftState; X, Y: Integer);
|
Shift: TShiftState; X, Y: Integer);
|
||||||
begin
|
begin
|
||||||
|
if Assigned(FSampleRateOverlay) and
|
||||||
|
FSampleRateOverlay.HandleMouseDown(Button, X, Y) then Exit;
|
||||||
|
|
||||||
if Button = mbLeft then
|
if Button = mbLeft then
|
||||||
begin
|
begin
|
||||||
// Левая кнопка: сбрасываем маркер если активен
|
// Левая кнопка: сбрасываем маркер если активен
|
||||||
@@ -4677,6 +4691,15 @@ end;
|
|||||||
procedure TMainForm.PbSpectrumMouseMove(Sender: TObject; Shift: TShiftState;
|
procedure TMainForm.PbSpectrumMouseMove(Sender: TObject; Shift: TShiftState;
|
||||||
X, Y: Integer);
|
X, Y: Integer);
|
||||||
begin
|
begin
|
||||||
|
if Assigned(FSampleRateOverlay) then
|
||||||
|
begin
|
||||||
|
FSampleRateOverlay.HandleMouseMove(X, Y);
|
||||||
|
if (FSampleRateOverlay.HotIdx >= 0) and (not FSpecDrag) then
|
||||||
|
PbSpectrum.Cursor := crHandPoint
|
||||||
|
else if not FSpecDrag then
|
||||||
|
PbSpectrum.Cursor := crDefault;
|
||||||
|
end;
|
||||||
|
|
||||||
if FSpecDrag and (ssLeft in Shift) then
|
if FSpecDrag and (ssLeft in Shift) then
|
||||||
DoSpectrumDrag(X, PbSpectrum.Width);
|
DoSpectrumDrag(X, PbSpectrum.Width);
|
||||||
// Маркер следует за курсором — обновление произойдёт в ближайшем тике таймера (50ms)
|
// Маркер следует за курсором — обновление произойдёт в ближайшем тике таймера (50ms)
|
||||||
@@ -4699,6 +4722,13 @@ begin
|
|||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
procedure TMainForm.PbSpectrumMouseLeave(Sender: TObject);
|
||||||
|
begin
|
||||||
|
if Assigned(FSampleRateOverlay) then
|
||||||
|
FSampleRateOverlay.HandleMouseLeave;
|
||||||
|
if not FSpecDrag then PbSpectrum.Cursor := crDefault;
|
||||||
|
end;
|
||||||
|
|
||||||
// Водопад (аналогично)
|
// Водопад (аналогично)
|
||||||
procedure TMainForm.PbWaterfallMouseDown(Sender: TObject; Button: TMouseButton;
|
procedure TMainForm.PbWaterfallMouseDown(Sender: TObject; Button: TMouseButton;
|
||||||
Shift: TShiftState; X, Y: Integer);
|
Shift: TShiftState; X, Y: Integer);
|
||||||
|
|||||||
+155
-84
@@ -1,9 +1,9 @@
|
|||||||
unit SampleRateOverlay;
|
unit SampleRateOverlay;
|
||||||
|
|
||||||
{
|
{
|
||||||
Overlay виджет для выбора sample rate (span) спектра.
|
Bitmap overlay для выбора sample rate (span) спектра.
|
||||||
Размещается в верхнем левом углу спектрограммы поверх PbSpectrum.
|
Рисуется прямо в bitmap спектра, как AlertOverlay, поэтому фон под ним
|
||||||
Содержит кнопку скрытия левой панели и ряд кнопок span.
|
смешивается вручную через alpha.
|
||||||
}
|
}
|
||||||
|
|
||||||
{$IFDEF FPC}
|
{$IFDEF FPC}
|
||||||
@@ -19,32 +19,43 @@ type
|
|||||||
TSpanSelectEvent = procedure(SampleRate: Integer) of object;
|
TSpanSelectEvent = procedure(SampleRate: Integer) of object;
|
||||||
THidePanelEvent = procedure of object;
|
THidePanelEvent = procedure of object;
|
||||||
|
|
||||||
TSampleRateOverlay = class(TCustomControl)
|
TSampleRateOverlay = class(TComponent)
|
||||||
private
|
private
|
||||||
FCurrentRate: Integer;
|
FCurrentRate: Integer;
|
||||||
FPanelHidden: Boolean;
|
FPanelHidden: Boolean;
|
||||||
FOnSpanSelect: TSpanSelectEvent;
|
FOnSpanSelect: TSpanSelectEvent;
|
||||||
FOnHidePanel: THidePanelEvent;
|
FOnHidePanel: THidePanelEvent;
|
||||||
|
FOnInvalidate: TNotifyEvent;
|
||||||
|
FLeft: Integer;
|
||||||
|
FTop: Integer;
|
||||||
|
FWidth: Integer;
|
||||||
|
FHeight: Integer;
|
||||||
|
|
||||||
FHitRects: array[0..6] of TRect; // [0]=hide button, [1..6]=span buttons
|
FHitRects: array[0..6] of TRect; // [0]=hide button, [1..6]=span buttons
|
||||||
FHitValues: array[0..6] of Integer; // 0=hide, else sample rate
|
FHitValues: array[0..6] of Integer; // 0=hide, else sample rate
|
||||||
FHitCount: Integer;
|
FHitCount: Integer;
|
||||||
FHotIdx: Integer;
|
FHotIdx: Integer;
|
||||||
|
|
||||||
procedure DrawSelf(C: TCanvas; W, H: Integer);
|
procedure DrawSelf(Target: TBitmap; C: TCanvas; OriginX, OriginY, W, H: Integer);
|
||||||
procedure RegisterHit(const HR: TRect; Value: Integer);
|
procedure RegisterHit(const HR: TRect; Value: Integer);
|
||||||
protected
|
procedure RequestInvalidate;
|
||||||
procedure Paint; override;
|
|
||||||
procedure MouseMove(Shift: TShiftState; X, Y: Integer); override;
|
|
||||||
procedure MouseDown(Button: TMouseButton; Shift: TShiftState;
|
|
||||||
X, Y: Integer); override;
|
|
||||||
procedure MouseLeave; override;
|
|
||||||
public
|
public
|
||||||
constructor Create(AOwner: TComponent); override;
|
constructor Create(AOwner: TComponent); override;
|
||||||
|
procedure SetBounds(ALeft, ATop, AWidth, AHeight: Integer);
|
||||||
|
procedure DrawOverlay(Target: TBitmap; C: TCanvas; W, H: Integer);
|
||||||
|
function HandleMouseMove(X, Y: Integer): Boolean;
|
||||||
|
function HandleMouseDown(Button: TMouseButton; X, Y: Integer): Boolean;
|
||||||
|
function HandleMouseLeave: Boolean;
|
||||||
procedure SetCurrentRate(Rate: Integer);
|
procedure SetCurrentRate(Rate: Integer);
|
||||||
procedure SetPanelHidden(Hidden: Boolean);
|
procedure SetPanelHidden(Hidden: Boolean);
|
||||||
|
property Left: Integer read FLeft;
|
||||||
|
property Top: Integer read FTop;
|
||||||
|
property Width: Integer read FWidth;
|
||||||
|
property Height: Integer read FHeight;
|
||||||
|
property HotIdx: Integer read FHotIdx;
|
||||||
property OnSpanSelect: TSpanSelectEvent read FOnSpanSelect write FOnSpanSelect;
|
property OnSpanSelect: TSpanSelectEvent read FOnSpanSelect write FOnSpanSelect;
|
||||||
property OnHidePanel: THidePanelEvent read FOnHidePanel write FOnHidePanel;
|
property OnHidePanel: THidePanelEvent read FOnHidePanel write FOnHidePanel;
|
||||||
|
property OnInvalidate: TNotifyEvent read FOnInvalidate write FOnInvalidate;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
implementation
|
implementation
|
||||||
@@ -59,14 +70,44 @@ const
|
|||||||
PAD_Y = 4;
|
PAD_Y = 4;
|
||||||
BTN_GAP = 3;
|
BTN_GAP = 3;
|
||||||
|
|
||||||
CLR_BG = TColor($00141414);
|
|
||||||
CLR_BORDER = TColor($00505050);
|
|
||||||
CLR_TEXT = TColor($00D8E6EE);
|
CLR_TEXT = TColor($00D8E6EE);
|
||||||
CLR_BTN_NORM = TColor($00543F18);
|
|
||||||
CLR_BTN_HOT = TColor($00624B1D);
|
|
||||||
CLR_BTN_ACT = TColor($00785A20);
|
|
||||||
CLR_BTN_BDR = TColor($00685624);
|
CLR_BTN_BDR = TColor($00685624);
|
||||||
|
|
||||||
|
procedure BlendRect(Target: TBitmap; const Rct: TRect; R, G, B, Alpha: Byte);
|
||||||
|
var
|
||||||
|
Y, X, InvA: Integer;
|
||||||
|
Row: PByte;
|
||||||
|
RR: TRect;
|
||||||
|
begin
|
||||||
|
if (Target = nil) or (Alpha = 0) then Exit;
|
||||||
|
RR := Rct;
|
||||||
|
if RR.Left < 0 then RR.Left := 0;
|
||||||
|
if RR.Top < 0 then RR.Top := 0;
|
||||||
|
if RR.Right > Target.Width then RR.Right := Target.Width;
|
||||||
|
if RR.Bottom > Target.Height then RR.Bottom := Target.Height;
|
||||||
|
if (RR.Right <= RR.Left) or (RR.Bottom <= RR.Top) then Exit;
|
||||||
|
|
||||||
|
InvA := 255 - Alpha;
|
||||||
|
Target.BeginUpdate(False);
|
||||||
|
try
|
||||||
|
for Y := RR.Top to RR.Bottom - 1 do
|
||||||
|
begin
|
||||||
|
Row := PByte(Target.ScanLine[Y]);
|
||||||
|
if Row = nil then Continue;
|
||||||
|
Inc(Row, RR.Left * 4);
|
||||||
|
for X := RR.Left to RR.Right - 1 do
|
||||||
|
begin
|
||||||
|
Row[0] := Byte((Alpha * B + InvA * Row[0]) div 255);
|
||||||
|
Row[1] := Byte((Alpha * G + InvA * Row[1]) div 255);
|
||||||
|
Row[2] := Byte((Alpha * R + InvA * Row[2]) div 255);
|
||||||
|
Inc(Row, 4);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
finally
|
||||||
|
Target.EndUpdate(False);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
function SpanBtnWidth(const Name: string; C: TCanvas): Integer;
|
function SpanBtnWidth(const Name: string; C: TCanvas): Integer;
|
||||||
begin
|
begin
|
||||||
C.Font.Name := 'Courier New';
|
C.Font.Name := 'Courier New';
|
||||||
@@ -79,108 +120,133 @@ end;
|
|||||||
constructor TSampleRateOverlay.Create(AOwner: TComponent);
|
constructor TSampleRateOverlay.Create(AOwner: TComponent);
|
||||||
begin
|
begin
|
||||||
inherited Create(AOwner);
|
inherited Create(AOwner);
|
||||||
ControlStyle := ControlStyle + [csOpaque];
|
FCurrentRate := 192000;
|
||||||
FCurrentRate := 192000;
|
FPanelHidden := False;
|
||||||
FPanelHidden := False;
|
FHitCount := 0;
|
||||||
FHitCount := 0;
|
FHotIdx := -1;
|
||||||
FHotIdx := -1;
|
FWidth := 320;
|
||||||
Cursor := crHandPoint;
|
FHeight := SPAN_H + PAD_Y * 2;
|
||||||
Width := 320;
|
end;
|
||||||
Height := SPAN_H + PAD_Y * 2;
|
|
||||||
|
procedure TSampleRateOverlay.RequestInvalidate;
|
||||||
|
begin
|
||||||
|
if Assigned(FOnInvalidate) then FOnInvalidate(Self);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TSampleRateOverlay.SetBounds(ALeft, ATop, AWidth, AHeight: Integer);
|
||||||
|
begin
|
||||||
|
if (FLeft = ALeft) and (FTop = ATop) and
|
||||||
|
(FWidth = AWidth) and (FHeight = AHeight) then Exit;
|
||||||
|
FLeft := ALeft;
|
||||||
|
FTop := ATop;
|
||||||
|
FWidth := AWidth;
|
||||||
|
FHeight := AHeight;
|
||||||
|
RequestInvalidate;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TSampleRateOverlay.RegisterHit(const HR: TRect; Value: Integer);
|
procedure TSampleRateOverlay.RegisterHit(const HR: TRect; Value: Integer);
|
||||||
begin
|
begin
|
||||||
if FHitCount > High(FHitRects) then Exit;
|
if FHitCount > High(FHitRects) then Exit;
|
||||||
FHitRects[FHitCount] := HR;
|
FHitRects[FHitCount] := HR;
|
||||||
FHitValues[FHitCount] := Value;
|
FHitValues[FHitCount] := Value;
|
||||||
Inc(FHitCount);
|
Inc(FHitCount);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TSampleRateOverlay.DrawSelf(C: TCanvas; W, H: Integer);
|
procedure TSampleRateOverlay.DrawSelf(Target: TBitmap; C: TCanvas;
|
||||||
|
OriginX, OriginY, W, H: Integer);
|
||||||
var
|
var
|
||||||
X, I, TW, TH, BtnW: Integer;
|
X, I, TW, TH, BtnW, ContentW: Integer;
|
||||||
HR: TRect;
|
HR, FullR: TRect;
|
||||||
IsAct, IsHot: Boolean;
|
IsAct, IsHot: Boolean;
|
||||||
Cap: string;
|
Cap: string;
|
||||||
begin
|
begin
|
||||||
|
if (Target = nil) or (C = nil) then Exit;
|
||||||
FHitCount := 0;
|
FHitCount := 0;
|
||||||
|
|
||||||
// Фон
|
C.Font.Name := 'Courier New';
|
||||||
C.Brush.Color := CLR_BG;
|
C.Font.Size := 7;
|
||||||
C.Brush.Style := bsSolid;
|
|
||||||
C.Pen.Style := psClear;
|
|
||||||
C.FillRect(Rect(0, 0, W, H));
|
|
||||||
|
|
||||||
// Рамка
|
|
||||||
C.Pen.Style := psSolid;
|
|
||||||
C.Pen.Color := CLR_BORDER;
|
|
||||||
C.Brush.Style := bsClear;
|
|
||||||
C.Rectangle(0, 0, W, H);
|
|
||||||
|
|
||||||
C.Font.Name := 'Courier New';
|
|
||||||
C.Font.Size := 7;
|
|
||||||
C.Font.Style := [];
|
C.Font.Style := [];
|
||||||
TH := C.TextHeight('A');
|
TH := C.TextHeight('A');
|
||||||
X := PAD_X;
|
|
||||||
|
|
||||||
// Кнопка скрытия панели (◀ или ▶)
|
ContentW := PAD_X + HIDE_W + BTN_GAP;
|
||||||
HR := Rect(X, PAD_Y, X + HIDE_W, PAD_Y + SPAN_H);
|
for I := 0 to High(SPAN_NAMES) do
|
||||||
IsHot := (FHotIdx = 0);
|
begin
|
||||||
if IsHot then C.Brush.Color := CLR_BTN_HOT
|
Inc(ContentW, SpanBtnWidth(SPAN_NAMES[I], C));
|
||||||
else C.Brush.Color := CLR_BTN_NORM;
|
if I < High(SPAN_NAMES) then Inc(ContentW, BTN_GAP);
|
||||||
C.Brush.Style := bsSolid; C.Pen.Style := psClear;
|
end;
|
||||||
C.FillRect(HR);
|
Inc(ContentW, PAD_X);
|
||||||
C.Pen.Style := psSolid; C.Pen.Color := CLR_BTN_BDR;
|
if ContentW > W then ContentW := W;
|
||||||
C.Brush.Style := bsClear; C.Rectangle(HR);
|
|
||||||
C.Font.Size := 8;
|
FullR := Rect(OriginX, OriginY, OriginX + ContentW, OriginY + H);
|
||||||
if FPanelHidden then Cap := '▶' else Cap := '◀';
|
BlendRect(Target, FullR, $14, $14, $14, 104);
|
||||||
TW := C.TextWidth(Cap); C.Font.Size := 7;
|
BlendRect(Target, Rect(FullR.Left, FullR.Top, FullR.Right, FullR.Top + 1),
|
||||||
C.Font.Color := CLR_TEXT;
|
$80, $80, $80, 56);
|
||||||
|
BlendRect(Target, Rect(FullR.Left, FullR.Bottom - 1, FullR.Right, FullR.Bottom),
|
||||||
|
$00, $00, $00, 48);
|
||||||
|
BlendRect(Target, Rect(FullR.Left, FullR.Top, FullR.Left + 1, FullR.Bottom),
|
||||||
|
$80, $80, $80, 42);
|
||||||
|
BlendRect(Target, Rect(FullR.Right - 1, FullR.Top, FullR.Right, FullR.Bottom),
|
||||||
|
$00, $00, $00, 42);
|
||||||
|
|
||||||
|
X := OriginX + PAD_X;
|
||||||
|
|
||||||
|
HR := Rect(X, OriginY + PAD_Y, X + HIDE_W, OriginY + PAD_Y + SPAN_H);
|
||||||
|
IsHot := FHotIdx = 0;
|
||||||
|
if IsHot then BlendRect(Target, HR, $1D, $4B, $62, 206)
|
||||||
|
else BlendRect(Target, HR, $18, $3F, $54, 182);
|
||||||
|
C.Pen.Style := psSolid;
|
||||||
|
C.Pen.Color := CLR_BTN_BDR;
|
||||||
|
C.Brush.Style := bsClear;
|
||||||
|
C.Rectangle(HR);
|
||||||
|
C.Font.Size := 8;
|
||||||
|
if FPanelHidden then Cap := '>' else Cap := '<';
|
||||||
|
TW := C.TextWidth(Cap);
|
||||||
|
C.Font.Color := CLR_TEXT;
|
||||||
C.Brush.Style := bsClear;
|
C.Brush.Style := bsClear;
|
||||||
C.Font.Size := 8;
|
|
||||||
C.TextOut(HR.Left + (HIDE_W - TW) div 2,
|
C.TextOut(HR.Left + (HIDE_W - TW) div 2,
|
||||||
HR.Top + (SPAN_H - TH) div 2, Cap);
|
HR.Top + (SPAN_H - TH) div 2, Cap);
|
||||||
C.Font.Size := 7;
|
C.Font.Size := 7;
|
||||||
RegisterHit(HR, 0);
|
RegisterHit(HR, 0);
|
||||||
Inc(X, HIDE_W + BTN_GAP);
|
Inc(X, HIDE_W + BTN_GAP);
|
||||||
|
|
||||||
// Кнопки span
|
|
||||||
for I := 0 to High(SPAN_RATES) do
|
for I := 0 to High(SPAN_RATES) do
|
||||||
begin
|
begin
|
||||||
BtnW := SpanBtnWidth(SPAN_NAMES[I], C);
|
BtnW := SpanBtnWidth(SPAN_NAMES[I], C);
|
||||||
HR := Rect(X, PAD_Y, X + BtnW, PAD_Y + SPAN_H);
|
HR := Rect(X, OriginY + PAD_Y, X + BtnW, OriginY + PAD_Y + SPAN_H);
|
||||||
IsAct := (SPAN_RATES[I] = FCurrentRate);
|
IsAct := SPAN_RATES[I] = FCurrentRate;
|
||||||
IsHot := (FHotIdx = I + 1);
|
IsHot := FHotIdx = I + 1;
|
||||||
|
|
||||||
if IsAct then C.Brush.Color := CLR_BTN_ACT
|
if IsAct then BlendRect(Target, HR, $20, $5A, $78, 220)
|
||||||
else if IsHot then C.Brush.Color := CLR_BTN_HOT
|
else if IsHot then BlendRect(Target, HR, $1D, $4B, $62, 206)
|
||||||
else C.Brush.Color := CLR_BTN_NORM;
|
else BlendRect(Target, HR, $18, $3F, $54, 182);
|
||||||
C.Brush.Style := bsSolid; C.Pen.Style := psClear;
|
C.Pen.Style := psSolid;
|
||||||
C.FillRect(HR);
|
C.Pen.Color := CLR_BTN_BDR;
|
||||||
C.Pen.Style := psSolid; C.Pen.Color := CLR_BTN_BDR;
|
C.Brush.Style := bsClear;
|
||||||
C.Brush.Style := bsClear; C.Rectangle(HR);
|
C.Rectangle(HR);
|
||||||
|
|
||||||
TW := C.TextWidth(SPAN_NAMES[I]);
|
TW := C.TextWidth(SPAN_NAMES[I]);
|
||||||
C.Font.Color := CLR_TEXT;
|
C.Font.Color := CLR_TEXT;
|
||||||
C.Brush.Style := bsClear;
|
C.Brush.Style := bsClear;
|
||||||
C.TextOut(HR.Left + (BtnW - TW) div 2,
|
C.TextOut(HR.Left + (BtnW - TW) div 2,
|
||||||
HR.Top + (SPAN_H - TH) div 2, SPAN_NAMES[I]);
|
HR.Top + (SPAN_H - TH) div 2, SPAN_NAMES[I]);
|
||||||
|
|
||||||
RegisterHit(HR, SPAN_RATES[I]);
|
RegisterHit(HR, SPAN_RATES[I]);
|
||||||
Inc(X, BtnW + BTN_GAP);
|
Inc(X, BtnW + BTN_GAP);
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TSampleRateOverlay.Paint;
|
procedure TSampleRateOverlay.DrawOverlay(Target: TBitmap; C: TCanvas; W, H: Integer);
|
||||||
begin
|
begin
|
||||||
DrawSelf(Canvas, Width, Height);
|
if (FWidth <= 0) or (FHeight <= 0) then Exit;
|
||||||
|
if (FLeft >= W) or (FTop >= H) then Exit;
|
||||||
|
DrawSelf(Target, C, FLeft, FTop, Min(FWidth, W - FLeft), Min(FHeight, H - FTop));
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TSampleRateOverlay.MouseMove(Shift: TShiftState; X, Y: Integer);
|
function TSampleRateOverlay.HandleMouseMove(X, Y: Integer): Boolean;
|
||||||
var
|
var
|
||||||
I, OldHot: Integer;
|
I, OldHot: Integer;
|
||||||
begin
|
begin
|
||||||
|
Result := False;
|
||||||
OldHot := FHotIdx;
|
OldHot := FHotIdx;
|
||||||
FHotIdx := -1;
|
FHotIdx := -1;
|
||||||
for I := 0 to FHitCount - 1 do
|
for I := 0 to FHitCount - 1 do
|
||||||
@@ -189,15 +255,18 @@ begin
|
|||||||
FHotIdx := I;
|
FHotIdx := I;
|
||||||
Break;
|
Break;
|
||||||
end;
|
end;
|
||||||
if FHotIdx <> OldHot then Invalidate;
|
if FHotIdx <> OldHot then
|
||||||
inherited MouseMove(Shift, X, Y);
|
begin
|
||||||
|
RequestInvalidate;
|
||||||
|
Result := True;
|
||||||
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TSampleRateOverlay.MouseDown(Button: TMouseButton;
|
function TSampleRateOverlay.HandleMouseDown(Button: TMouseButton; X, Y: Integer): Boolean;
|
||||||
Shift: TShiftState; X, Y: Integer);
|
|
||||||
var
|
var
|
||||||
I: Integer;
|
I: Integer;
|
||||||
begin
|
begin
|
||||||
|
Result := False;
|
||||||
if Button <> mbLeft then Exit;
|
if Button <> mbLeft then Exit;
|
||||||
for I := 0 to FHitCount - 1 do
|
for I := 0 to FHitCount - 1 do
|
||||||
if PtInRect(FHitRects[I], Point(X, Y)) then
|
if PtInRect(FHitRects[I], Point(X, Y)) then
|
||||||
@@ -210,32 +279,34 @@ begin
|
|||||||
begin
|
begin
|
||||||
if Assigned(FOnSpanSelect) then FOnSpanSelect(FHitValues[I]);
|
if Assigned(FOnSpanSelect) then FOnSpanSelect(FHitValues[I]);
|
||||||
end;
|
end;
|
||||||
|
Result := True;
|
||||||
Break;
|
Break;
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TSampleRateOverlay.MouseLeave;
|
function TSampleRateOverlay.HandleMouseLeave: Boolean;
|
||||||
begin
|
begin
|
||||||
|
Result := False;
|
||||||
if FHotIdx >= 0 then
|
if FHotIdx >= 0 then
|
||||||
begin
|
begin
|
||||||
FHotIdx := -1;
|
FHotIdx := -1;
|
||||||
Invalidate;
|
RequestInvalidate;
|
||||||
|
Result := True;
|
||||||
end;
|
end;
|
||||||
inherited MouseLeave;
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TSampleRateOverlay.SetCurrentRate(Rate: Integer);
|
procedure TSampleRateOverlay.SetCurrentRate(Rate: Integer);
|
||||||
begin
|
begin
|
||||||
if FCurrentRate = Rate then Exit;
|
if FCurrentRate = Rate then Exit;
|
||||||
FCurrentRate := Rate;
|
FCurrentRate := Rate;
|
||||||
Invalidate;
|
RequestInvalidate;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TSampleRateOverlay.SetPanelHidden(Hidden: Boolean);
|
procedure TSampleRateOverlay.SetPanelHidden(Hidden: Boolean);
|
||||||
begin
|
begin
|
||||||
if FPanelHidden = Hidden then Exit;
|
if FPanelHidden = Hidden then Exit;
|
||||||
FPanelHidden := Hidden;
|
FPanelHidden := Hidden;
|
||||||
Invalidate;
|
RequestInvalidate;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
end.
|
end.
|
||||||
|
|||||||
+5
-1
@@ -20,7 +20,7 @@ interface
|
|||||||
uses
|
uses
|
||||||
Classes, SysUtils, Graphics, ExtCtrls, Controls, Math,
|
Classes, SysUtils, Graphics, ExtCtrls, Controls, Math,
|
||||||
IntfGraphics, FPImage, LCLIntf, LCLType, GraphType, AppTheme,
|
IntfGraphics, FPImage, LCLIntf, LCLType, GraphType, AppTheme,
|
||||||
AlertOverlay,
|
AlertOverlay, SampleRateOverlay,
|
||||||
WaterfallView, SMeterView, RulerView;
|
WaterfallView, SMeterView, RulerView;
|
||||||
|
|
||||||
const
|
const
|
||||||
@@ -69,6 +69,7 @@ type
|
|||||||
FTXOverlay: Boolean;
|
FTXOverlay: Boolean;
|
||||||
// ── ADC overload overlay ─────────────────────────────────────────────────
|
// ── ADC overload overlay ─────────────────────────────────────────────────
|
||||||
FADCOverloadVisible: Boolean;
|
FADCOverloadVisible: Boolean;
|
||||||
|
FSampleRateOverlay: TSampleRateOverlay;
|
||||||
// ── Marker ────────────────────────────────────────────────────────────────
|
// ── Marker ────────────────────────────────────────────────────────────────
|
||||||
FMarkerActive: Boolean;
|
FMarkerActive: Boolean;
|
||||||
FMarkerX: Integer;
|
FMarkerX: Integer;
|
||||||
@@ -197,6 +198,7 @@ type
|
|||||||
property PbWaterfall: TPaintBox write SetPbWaterfall;
|
property PbWaterfall: TPaintBox write SetPbWaterfall;
|
||||||
property PbRuler: TPaintBox write SetPbRuler;
|
property PbRuler: TPaintBox write SetPbRuler;
|
||||||
property PbSMeterRight: TPaintBox write SetPbSMeterRight;
|
property PbSMeterRight: TPaintBox write SetPbSMeterRight;
|
||||||
|
property SampleRateOverlay: TSampleRateOverlay read FSampleRateOverlay write FSampleRateOverlay;
|
||||||
|
|
||||||
// ── Данные от DSP ─────────────────────────────────────────────────────────
|
// ── Данные от DSP ─────────────────────────────────────────────────────────
|
||||||
procedure SetSpectrumData(const Pixels: array of Single; Count: Integer);
|
procedure SetSpectrumData(const Pixels: array of Single; Count: Integer);
|
||||||
@@ -914,6 +916,8 @@ begin
|
|||||||
|
|
||||||
if FMarkerActive then DrawMarkerLine(C, W, H);
|
if FMarkerActive then DrawMarkerLine(C, W, H);
|
||||||
DrawADCOverloadOverlay(C, W, H);
|
DrawADCOverloadOverlay(C, W, H);
|
||||||
|
if Assigned(FSampleRateOverlay) then
|
||||||
|
FSampleRateOverlay.DrawOverlay(FSpectrumBitmap, C, W, H);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
// ────────────────────────────────────────────────────────────────────────────
|
// ────────────────────────────────────────────────────────────────────────────
|
||||||
|
|||||||
Reference in New Issue
Block a user