From e131af9ae5cef061c799710d6aad1e5dd67afd97 Mon Sep 17 00:00:00 2001 From: Uladzimir Karpenka Date: Mon, 9 Mar 2026 22:14:04 +0300 Subject: [PATCH] add gradient --- MainForm.pas | 135 ++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 102 insertions(+), 33 deletions(-) diff --git a/MainForm.pas b/MainForm.pas index b11fe1d..d3d6d8c 100644 --- a/MainForm.pas +++ b/MainForm.pas @@ -318,6 +318,8 @@ type BtnSNB: TFlatButton; BtnANF: TFlatButton; BtnMute: TFlatButton; + BtnWfAGC: TFlatButton; + BtnWfNF: TFlatButton; PanelSMeterRight: TPanel; // контейнер S-метра справа PbSMeterRight: TPaintBox;// новый большой S-метр справа @@ -342,8 +344,6 @@ type BtnSpan384k: TFlatButton; BtnSpan768k: TFlatButton; BtnSpan1536k: TFlatButton; - BtnWfAGC: TFlatButton; - BtnWfNF: TFlatButton; BtnHidePanel: TFlatButton; // скрыть/показать левую панель PbSpectrum: TPaintBox; PbRuler: TPaintBox; // полоса частотных меток между спектром и водопадом @@ -1161,7 +1161,7 @@ begin // RX controls PanelRX := TPanel.Create(Self); PanelRX.Parent := PanelLeft; - PanelRX.SetBounds(0, Y, LEFT_W, 120); + PanelRX.SetBounds(0, Y, LEFT_W, 150); PanelRX.BevelOuter := bvNone; // AGC mode — 5 кнопок в ряд @@ -1207,8 +1207,10 @@ begin BtnSNB := MakeBtn(PanelRX, 'SNB', 2*W+6, 96, W, BTN_SM, BtnSNBClick); BtnANF := MakeBtn(PanelRX, 'ANF', 3*W+8, 96, W, BTN_SM, BtnANFClick); BtnMute:= MakeBtn(PanelRX, 'MUTE', 4*W+10, 96, W, BTN_SM, BtnMuteClick); + BtnWfAGC := MakeBtn(PanelRX, 'WF AGC', 2, 122, (LEFT_W - 6) div 2, BTN_SM, BtnWfAGCClick); + BtnWfNF := MakeBtn(PanelRX, 'WF NF', 4 + (LEFT_W - 6) div 2, 122, (LEFT_W - 6) div 2, BTN_SM, BtnWfNFClick); - Inc(Y, 124); + Inc(Y, 154); // S-Meter @@ -1294,10 +1296,15 @@ begin StyleSpanButton(BtnSpan768k, False); StyleSpanButton(BtnSpan1536k, False); - // Waterfall AGC + Noise Floor кнопки справа в той же панели - BtnWfAGC := MakeBtn(PanelSpanButtons, 'WF AGC', 379, 3, 62, BTN_SM, BtnWfAGCClick); - BtnWfNF := MakeBtn(PanelSpanButtons, 'WF NF', 445, 3, 58, BTN_SM, BtnWfNFClick); - BtnHidePanel := MakeBtn(PanelSpanButtons, '◀ HIDE', 510, 3, 64, BTN_SM, BtnHidePanelClick); + BtnHidePanel := MakeBtn(PanelRight, '◀', 0, 0, 28, 28, BtnHidePanelClick); + BtnHidePanel.Font.Size := 10; + BtnHidePanel.ClrNorm := TColor($00543F18); + BtnHidePanel.ClrHot := TColor($00624B1D); + BtnHidePanel.ClrActive := TColor($00624B1D); + BtnHidePanel.ClrBorder := TColor($00685624); + BtnHidePanel.ClrText := TColor($00D8E6EE); + BtnHidePanel.ClrTextAct := TColor($00F0FAFF); + BtnHidePanel.BringToFront; // Большой S-метр — на главной форме поверх двух панелей // S-метр: Parent=Self, перекрывает Toolbar+SpanButtons вертикально @@ -1359,7 +1366,6 @@ procedure TMainForm.ResizeSMeter; // По Y: от самого верха формы до низа PanelSpanButtons (Toolbar+Span = 66px). // По X: 40% ширины PanelRight, прижат к правому краю. const - LEFT_PANEL_W = 232; // ширина левой панели (LEFT_W) MARGIN = 3; var FW, FH: Integer; @@ -1370,11 +1376,11 @@ var SMBot: Integer; begin if (PanelSMeterRight = nil) or (PanelToolbar = nil) or - (PanelSpanButtons = nil) then Exit; + (PanelSpanButtons = nil) or (PanelLeft = nil) then Exit; FW := ClientWidth; // Ширина зоны спектра (PanelRight) - RightW := FW - LEFT_PANEL_W; + RightW := FW - PanelLeft.Width; if RightW < 200 then Exit; // Ширина S-метра = 40% от PanelRight @@ -1499,12 +1505,12 @@ end; procedure TMainForm.StyleSpanButton(B: TFlatButton; Active: Boolean); begin B.Active := Active; - B.ClrNorm := TColor($0013191B); - B.ClrActive := TColor($001E3C1E); - B.ClrHot := TColor($001E2D1E); - B.ClrBorder := TColor($005A7A5A); - B.ClrText := TColor($0090B890); - B.ClrTextAct := TColor($00AAFFAA); + B.ClrNorm := TColor($00543F18); + B.ClrActive := TColor($00624B1D); + B.ClrHot := TColor($00624B1D); + B.ClrBorder := TColor($00685624); + B.ClrText := TColor($00D8E6EE); + B.ClrTextAct := TColor($00F0FAFF); B.Font.Name := 'Courier New'; B.Font.Size := 7; end; @@ -1545,6 +1551,8 @@ begin BtnSpan384k.SetBounds(X, BaseY + BTN_TOP, W384, HBTN); Inc(X, W384 + GAP); BtnSpan768k.SetBounds(X, BaseY + BTN_TOP, W768, HBTN); Inc(X, W768 + GAP); BtnSpan1536k.SetBounds(X, BaseY + BTN_TOP, W1536, HBTN); + if BtnHidePanel <> nil then + BtnHidePanel.SetBounds(BaseX, BaseY + BTN_TOP, 28, HBTN); BtnSpan48k.Visible := True; BtnSpan96k.Visible := True; @@ -1559,6 +1567,7 @@ begin BtnSpan384k.BringToFront; BtnSpan768k.BringToFront; BtnSpan1536k.BringToFront; + if BtnHidePanel <> nil then BtnHidePanel.BringToFront; end; // =========================================================================== @@ -1728,6 +1737,58 @@ begin Desc.AlphaShift := 24; Desc.AlphaPrec := 8; end; +function MixColorBGR(C1, C2: TColor; T: Double): TColor; +var + B1, G1, R1: Integer; + B2, G2, R2: Integer; + B, G, R: Integer; +begin + if T < 0.0 then T := 0.0 else + if T > 1.0 then T := 1.0; + + B1 := (C1 shr 16) and $FF; G1 := (C1 shr 8) and $FF; R1 := C1 and $FF; + B2 := (C2 shr 16) and $FF; G2 := (C2 shr 8) and $FF; R2 := C2 and $FF; + + B := Round(B1 + (B2 - B1) * T); + G := Round(G1 + (G2 - G1) * T); + R := Round(R1 + (R2 - R1) * T); + Result := TColor((B shl 16) or (G shl 8) or R); +end; + +procedure PaintVerticalGradient(C: TCanvas; W, H: Integer; TopColor, BottomColor: TColor); +var + Y: Integer; + T: Double; +begin + if (W <= 0) or (H <= 0) then Exit; + C.Pen.Style := psClear; + C.Brush.Style := bsSolid; + for Y := 0 to H - 1 do + begin + T := Y / Max(1, H - 1); + C.Brush.Color := MixColorBGR(TopColor, BottomColor, T); + C.FillRect(Rect(0, Y, W, Y + 1)); + end; + C.Pen.Style := psSolid; +end; + +procedure PaintHorizontalFade(C: TCanvas; X1, X2, H: Integer; LeftColor, RightColor: TColor); +var + X: Integer; + T: Double; +begin + if (H <= 0) or (X2 <= X1) then Exit; + C.Pen.Style := psClear; + C.Brush.Style := bsSolid; + for X := X1 to X2 - 1 do + begin + T := (X - X1) / Max(1, X2 - X1 - 1); + C.Brush.Color := MixColorBGR(LeftColor, RightColor, T); + C.FillRect(Rect(X, 0, X + 1, H)); + end; + C.Pen.Style := psSolid; +end; + procedure TMainForm.DrawSpectrumGradient(const SpPts: array of TPoint; W, H: Integer); // ═══════════════════════════════════════════════════════════════════════ // Попиксельный градиент через кэшированный FSpecGradImg. @@ -1815,6 +1876,7 @@ var SrcF, Frac, dBv: Double; S0, S1: Integer; InvRange: Double; + LabelBandW: Integer; begin if FSpectrumBitmap = nil then Exit; W := FSpectrumBitmap.Width; @@ -1832,16 +1894,22 @@ begin FGridBitmapH := H; FGridBitmap.SetSize(W, H); GC := FGridBitmap.Canvas; - GC.Brush.Color := TColor($00050505); - GC.FillRect(Rect(0, 0, W, H)); + PaintVerticalGradient(GC, W, H, TColor($0020160B), TColor($006C4F1F)); + LabelBandW := 28; + GC.Pen.Style := psClear; + GC.Brush.Style := bsSolid; + GC.Brush.Color := TColor($00543F18); + GC.FillRect(Rect(0, 0, LabelBandW, H)); + GC.Pen.Style := psSolid; GC.Font.Size := 7; GC.Font.Name := 'Courier New'; - GC.Font.Color := CLR_TEXTDIM; - dB := -30.0; + GC.Font.Color := TColor($00A8C9D9); + GC.Brush.Style := bsClear; + dB := -40.0; while dB >= DBmin do begin Yp := Round((DBmax - dB) * InvRange * H); - GC.Pen.Color := CLR_BORDER; + GC.Pen.Color := TColor($00685624); GC.Pen.Width := 1; GC.MoveTo(0, Yp); GC.LineTo(W-1, Yp); GC.TextOut(1, Yp - 9, Format('%4.0f', [dB])); @@ -1850,7 +1918,7 @@ begin for i := 0 to 8 do begin GX := ScaleX(i, 8, W); - GC.Pen.Color := CLR_BORDER; + GC.Pen.Color := TColor($00685624); GC.MoveTo(GX, 0); GC.LineTo(GX, H); end; end; @@ -1866,7 +1934,7 @@ begin end; X1 := Max(0, Min(W-1, VfoX + Round(Lo_Hz / FSpanHz * W))); X2 := Max(0, Min(W-1, VfoX + Round(Hi_Hz / FSpanHz * W))); - C.Brush.Color := TColor($00102828); + C.Brush.Color := TColor($006A4E22); C.Brush.Style := bsSolid; C.Pen.Style := psClear; if X2 > X1 then C.FillRect(Rect(X1, 0, X2, H)); @@ -2457,24 +2525,24 @@ begin FRulerBitmap.SetSize(W, H); C := FRulerBitmap.Canvas; - C.Brush.Color := TColor($00141414); - C.FillRect(Rect(0, 0, W, H)); - C.Pen.Color := CLR_BORDER; + PaintVerticalGradient(C, W, H, TColor($00372A12), TColor($00705A2A)); + C.Pen.Color := TColor($00685624); C.Pen.Width := 1; C.MoveTo(0, 0); C.LineTo(W, 0); C.MoveTo(0, H-1); C.LineTo(W, H-1); C.Font.Name := 'Courier New'; C.Font.Size := 7; + C.Brush.Style := bsClear; FreqStart := FCenterFreq - FSpanHz / 2; for i := 0 to 8 do begin X := ScaleX(i, 8, W); - C.Pen.Color := CLR_BORDER; + C.Pen.Color := TColor($00685624); C.MoveTo(X, 0); C.LineTo(X, H div 2); FreqHz := FreqStart + i * FSpanHz / 8; Lbl := Format('%.3f', [FreqHz / 1e6]); TW := C.TextWidth(Lbl); - C.Font.Color := TColor($00888888); + C.Font.Color := TColor($00C8D8E2); C.TextOut(X - TW div 2, H div 2 - 1, Lbl); end; VfoX := Round((FVfoA - FCenterFreq + FSpanHz / 2) / FSpanHz * W); @@ -3746,11 +3814,10 @@ end; procedure TMainForm.BtnHidePanelClick(Sender: TObject); begin FPanelHidden := not FPanelHidden; - PanelLeft.Visible := not FPanelHidden; if FPanelHidden then begin - BtnHidePanel.Caption := '▶ SHOW'; - // PbSpectrum — TGraphicControl, не TWinControl, используем PanelRight + PanelLeft.Width := 0; + BtnHidePanel.Caption := '▶'; FVfoOverlay.Parent := PanelRight; FVfoOverlay.Width := 260; FVfoOverlay.Height := 130; @@ -3761,9 +3828,11 @@ begin end else begin - BtnHidePanel.Caption := '◀ HIDE'; + PanelLeft.Width := 232; + BtnHidePanel.Caption := '◀'; FVfoOverlay.Visible := False; end; + ResizeSpectrumPanels; ResizeSMeter; end;