mirror of
https://git.vladimir.cc/vladimir/ewsdr.git
synced 2026-08-25 17:27:32 +00:00
add light/dark theme with global persistence
- AppTheme.pas: новый файл, TAppTheme record + DarkTheme()/LightTheme() - FlatSlider: цвета через SetThemeColors вместо хардкода - SpectrumView: все цвета из TAppTheme (SetTheme) - SettingsForm: ApplyTheme(T) с рекурсивным обходом контролов - DeviceForm: SetTheme(T) — перекраска всех элементов включая TEdit - Settings: SaveTheme/LoadTheme в корневой секции preferences (не привязано к устройству) - MainForm: тема загружается при старте, сохраняется при изменении Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+69
-44
@@ -18,7 +18,7 @@ interface
|
||||
|
||||
uses
|
||||
Classes, SysUtils, Graphics, ExtCtrls, Controls, Math,
|
||||
IntfGraphics, FPImage, LCLIntf, LCLType, GraphType;
|
||||
IntfGraphics, FPImage, LCLIntf, LCLType, GraphType, AppTheme;
|
||||
|
||||
const
|
||||
SV_CLR_BG = TColor($00101010);
|
||||
@@ -44,6 +44,9 @@ type
|
||||
FWfIntfImg: TLazIntfImage;
|
||||
FWfPixels: array of LongWord;
|
||||
FSpPts: array of TPoint;
|
||||
// ── Тема ─────────────────────────────────────────────────────────────────
|
||||
FTheme: TAppTheme;
|
||||
FLightTheme: Boolean;
|
||||
FSpPtsLen: Integer;
|
||||
FSmBitmap: TBitmap;
|
||||
FRulerLastFreq: Double;
|
||||
@@ -197,6 +200,8 @@ type
|
||||
|
||||
// ── Утилиты ───────────────────────────────────────────────────────────────
|
||||
procedure InvalidateGridCache;
|
||||
// ── Тема ─────────────────────────────────────────────────────────────────
|
||||
procedure SetTheme(const T: TAppTheme);
|
||||
procedure InvalidateRulerCache;
|
||||
procedure ResetSpectrumBuf;
|
||||
procedure ResetWfAvgBuf;
|
||||
@@ -360,6 +365,8 @@ begin
|
||||
FWaterfallDirty := True;
|
||||
FLastSMeter := -130; FSMeterPeak := -130; FSMeterMin := -130;
|
||||
FLastFwdW := 0; FLastSWR := 1;
|
||||
FLightTheme := False;
|
||||
FTheme := DarkTheme;
|
||||
ResetSpectrumBuf;
|
||||
end;
|
||||
|
||||
@@ -439,14 +446,17 @@ begin
|
||||
Alpha := Round((1.0 - Sqr((Row - YMin) / Max(1.0, H - YMin - 1.0))) * 200);
|
||||
if Alpha <= 0 then Continue;
|
||||
if Alpha > 200 then Alpha := 200;
|
||||
GG := Byte(Alpha shr 1); RG := Byte(Alpha);
|
||||
GG := Byte(FTheme.SpecGradB * Alpha div 200);
|
||||
RG := Byte(FTheme.SpecGradG * Alpha div 200);
|
||||
RowPtr := FSpecGradImg.GetDataLineStart(Row);
|
||||
if RowPtr = nil then Continue;
|
||||
for Col := 0 to W - 1 do
|
||||
begin
|
||||
if SpPts[Col].Y <= Row then
|
||||
begin
|
||||
RowPtr[0] := GG; RowPtr[1] := RG; RowPtr[2] := 0;
|
||||
RowPtr[0] := GG;
|
||||
RowPtr[1] := RG;
|
||||
RowPtr[2] := Byte(FTheme.SpecGradR * Alpha div 200);
|
||||
if BPPg >= 4 then RowPtr[3] := Byte(Alpha);
|
||||
end else begin
|
||||
RowPtr[0] := 0; RowPtr[1] := 0; RowPtr[2] := 0;
|
||||
@@ -495,7 +505,7 @@ procedure TSpectrumView.SetSpectrumBitmapSize(W, H: Integer);
|
||||
begin
|
||||
if (W <= 0) or (H <= 0) then Exit;
|
||||
FSpectrumBitmap.SetSize(W, H);
|
||||
FSpectrumBitmap.Canvas.Brush.Color := SV_CLR_BG;
|
||||
FSpectrumBitmap.Canvas.Brush.Color := FTheme.BG;
|
||||
FSpectrumBitmap.Canvas.FillRect(Rect(0, 0, W, H));
|
||||
FreeAndNil(FSpecGradImg);
|
||||
InvalidateGridCache;
|
||||
@@ -532,6 +542,16 @@ begin
|
||||
FSpectrumDirty := True;
|
||||
end;
|
||||
|
||||
procedure TSpectrumView.SetTheme(const T: TAppTheme);
|
||||
begin
|
||||
FTheme := T;
|
||||
FLightTheme := T.BG > TColor($00808080); // светлая тема если фон светлее 50%
|
||||
FreeAndNil(FSpecGradImg); // пересчитать градиент
|
||||
InvalidateGridCache;
|
||||
InvalidateRulerCache;
|
||||
FWaterfallDirty := True;
|
||||
end;
|
||||
|
||||
procedure TSpectrumView.InvalidateRulerCache;
|
||||
begin
|
||||
FRulerLastFreq := -1.0; FRulerLastVfo := -1.0; FRulerLastSpan := -1.0;
|
||||
@@ -606,20 +626,20 @@ begin
|
||||
FGridBitmapW := W; FGridBitmapH := H;
|
||||
FGridBitmap.SetSize(W, H);
|
||||
GC := FGridBitmap.Canvas;
|
||||
PaintVerticalGradient(GC, W, H, TColor($0020160B), TColor($006C4F1F));
|
||||
PaintVerticalGradient(GC, W, H, FTheme.SpecGradTop, FTheme.SpecGradBot);
|
||||
LabelBandW := 28;
|
||||
GC.Pen.Style := psClear; GC.Brush.Style := bsSolid;
|
||||
GC.Brush.Color := TColor($00543F18);
|
||||
GC.Brush.Color := FTheme.SpecLabelBand;
|
||||
GC.FillRect(Rect(0, 0, LabelBandW, H));
|
||||
GC.Pen.Style := psSolid;
|
||||
GC.Font.Size := 7; GC.Font.Name := 'Courier New';
|
||||
GC.Font.Color := TColor($00A8C9D9);
|
||||
GC.Font.Color := FTheme.SpecLabelText;
|
||||
GC.Brush.Style := bsClear;
|
||||
dB := DBmax - FSpecGridStep;
|
||||
while dB >= DBmin do
|
||||
begin
|
||||
Yp := Round((DBmax - dB) * InvRange * H);
|
||||
GC.Pen.Color := TColor($00685624); GC.Pen.Width := 1;
|
||||
GC.Pen.Color := FTheme.SpecGrid; GC.Pen.Width := 1;
|
||||
GC.MoveTo(0, Yp); GC.LineTo(W-1, Yp);
|
||||
GC.TextOut(1, Yp - 9, Format('%4.0f', [dB]));
|
||||
dB := dB - FSpecGridStep;
|
||||
@@ -627,7 +647,7 @@ begin
|
||||
for i := 0 to 8 do
|
||||
begin
|
||||
GX := ScaleX(i, 8, W);
|
||||
GC.Pen.Color := TColor($00685624);
|
||||
GC.Pen.Color := FTheme.SpecGrid;
|
||||
GC.MoveTo(GX, 0); GC.LineTo(GX, H);
|
||||
end;
|
||||
end;
|
||||
@@ -646,7 +666,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($006A4E22); C.Brush.Style := bsSolid; C.Pen.Style := psClear;
|
||||
C.Brush.Color := FTheme.SpecFilter; C.Brush.Style := bsSolid; C.Pen.Style := psClear;
|
||||
if X2 > X1 then C.FillRect(Rect(X1, 0, X2, H));
|
||||
C.Pen.Style := psSolid;
|
||||
|
||||
@@ -656,26 +676,26 @@ begin
|
||||
AGCy := Round((DBmax - FAGCThresh) * InvRange * H);
|
||||
if (AGCy >= 0) and (AGCy < H) then
|
||||
begin
|
||||
C.Pen.Color := TColor($0000AAFF); C.Pen.Width := 1; C.Pen.Style := psDash;
|
||||
C.Pen.Color := FTheme.SpecAgcColor; C.Pen.Width := 1; C.Pen.Style := psDash;
|
||||
C.MoveTo(0, AGCy); C.LineTo(W, AGCy);
|
||||
C.Pen.Style := psSolid; C.Font.Color := TColor($0000AAFF); C.Font.Size := 6;
|
||||
C.Pen.Style := psSolid; C.Font.Color := FTheme.SpecAgcColor; C.Font.Size := 6;
|
||||
C.TextOut(4, AGCy - 9, 'AGC T');
|
||||
end;
|
||||
AGCHangY := Round((DBmax - FAGCHangLevel) * InvRange * H);
|
||||
if (AGCHangY >= 0) and (AGCHangY < H) and (Abs(AGCHangY - AGCy) > 4) then
|
||||
begin
|
||||
C.Pen.Color := TColor($00FFCC00); C.Pen.Width := 1; C.Pen.Style := psDot;
|
||||
C.Pen.Color := FTheme.SpecAgcHangColor; C.Pen.Width := 1; C.Pen.Style := psDot;
|
||||
C.MoveTo(0, AGCHangY); C.LineTo(W, AGCHangY);
|
||||
C.Pen.Style := psSolid; C.Font.Color := TColor($00FFCC00); C.Font.Size := 6;
|
||||
C.Pen.Style := psSolid; C.Font.Color := FTheme.SpecAgcHangColor; C.Font.Size := 6;
|
||||
C.TextOut(4, AGCHangY + 2, 'AGC H');
|
||||
end;
|
||||
end else begin
|
||||
AGCy := Round((DBmax - (-FAGCTop)) * InvRange * H);
|
||||
if (AGCy >= 0) and (AGCy < H) then
|
||||
begin
|
||||
C.Pen.Color := TColor($0000AAFF); C.Pen.Width := 1; C.Pen.Style := psDash;
|
||||
C.Pen.Color := FTheme.SpecAgcColor; C.Pen.Width := 1; C.Pen.Style := psDash;
|
||||
C.MoveTo(0, AGCy); C.LineTo(W, AGCy);
|
||||
C.Pen.Style := psSolid; C.Font.Color := TColor($0000AAFF); C.Font.Size := 6;
|
||||
C.Pen.Style := psSolid; C.Font.Color := FTheme.SpecAgcColor; C.Font.Size := 6;
|
||||
C.TextOut(4, AGCy - 9, Format('AGC -%ddB', [FAGCTop]));
|
||||
end;
|
||||
end;
|
||||
@@ -700,16 +720,16 @@ begin
|
||||
DrawSpectrumGradient(FSpPts, W, H);
|
||||
|
||||
// ── 6. Линия спектра ──────────────────────────────────────────────────────
|
||||
C.Pen.Style := psSolid; C.Pen.Color := TColor($0040FF80); C.Pen.Width := 1;
|
||||
C.Pen.Style := psSolid; C.Pen.Color := FTheme.SpecLine; C.Pen.Width := 1;
|
||||
C.Polyline(Slice(FSpPts, W));
|
||||
|
||||
// ── 7. Края фильтра + VFO ─────────────────────────────────────────────────
|
||||
C.Pen.Color := TColor($0040FFCC); C.Pen.Width := 1; C.Pen.Style := psSolid;
|
||||
C.Pen.Color := FTheme.SpecFilterEdge; C.Pen.Width := 1; C.Pen.Style := psSolid;
|
||||
C.MoveTo(X1, 0); C.LineTo(X1, H);
|
||||
C.MoveTo(X2, 0); C.LineTo(X2, H);
|
||||
C.Pen.Color := TColor($0000AAFF); C.Pen.Width := 2;
|
||||
C.Pen.Color := FTheme.SpecVfoCursor; C.Pen.Width := 2;
|
||||
C.MoveTo(VfoX, 0); C.LineTo(VfoX, H - 12);
|
||||
C.Brush.Color := TColor($0000AAFF); C.Brush.Style := bsSolid; C.Pen.Width := 1;
|
||||
C.Brush.Color := FTheme.SpecVfoCursor; C.Brush.Style := bsSolid; C.Pen.Width := 1;
|
||||
C.Polygon([Point(VfoX-5,0), Point(VfoX+5,0), Point(VfoX,8)]);
|
||||
|
||||
if FMarkerActive then DrawMarkerLine(C, W, H);
|
||||
@@ -862,8 +882,8 @@ begin
|
||||
if (FRulerBitmap.Width <> W) or (FRulerBitmap.Height <> H) then
|
||||
FRulerBitmap.SetSize(W, H);
|
||||
C := FRulerBitmap.Canvas;
|
||||
PaintVerticalGradient(C, W, H, TColor($00372A12), TColor($00705A2A));
|
||||
C.Pen.Color := TColor($00685624); C.Pen.Width := 1;
|
||||
PaintVerticalGradient(C, W, H, FTheme.RulerGradTop, FTheme.RulerGradBot);
|
||||
C.Pen.Color := FTheme.RulerBorder; 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;
|
||||
@@ -872,18 +892,18 @@ begin
|
||||
for i := 0 to 8 do
|
||||
begin
|
||||
X := ScaleX(i, 8, W);
|
||||
C.Pen.Color := TColor($00685624);
|
||||
C.Pen.Color := FTheme.RulerBorder;
|
||||
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($00C8D8E2);
|
||||
C.Font.Color := FTheme.RulerText;
|
||||
C.TextOut(X - TW div 2, H div 2 - 1, Lbl);
|
||||
end;
|
||||
VfoX := Round((ActiveVfoFreq - FCenterFreq + FSpanHz/2) / FSpanHz * W);
|
||||
if (VfoX >= 0) and (VfoX < W) then
|
||||
begin
|
||||
C.Pen.Color := TColor($0000FF80); C.Pen.Width := 2;
|
||||
C.Pen.Color := FTheme.RulerVfo; C.Pen.Width := 2;
|
||||
C.MoveTo(VfoX, 0); C.LineTo(VfoX, H - 1); C.Pen.Width := 1;
|
||||
end;
|
||||
end;
|
||||
@@ -896,7 +916,7 @@ procedure TSpectrumView.DrawBarMeter(ACanvas: TCanvas; R: TRect;
|
||||
Value, MaxVal: Double; BarColor: TColor);
|
||||
var Pct, BarW: Integer;
|
||||
begin
|
||||
ACanvas.Brush.Color := TColor($00080808);
|
||||
ACanvas.Brush.Color := FTheme.SMeterBG;
|
||||
ACanvas.FillRect(R);
|
||||
if MaxVal > 0 then
|
||||
Pct := Round(Max(0.0, Min(1.0, Value / MaxVal)) * (R.Right - R.Left - 2))
|
||||
@@ -918,22 +938,13 @@ const
|
||||
DBM_LABELS: array[0..5] of string = ('-120','-100','-80','-60','-40','-20');
|
||||
S_MARKS_DBM: array[0..7] of Double = (-121,-109,-97,-85,-73,-53,-33,-13);
|
||||
S_MARKS_LBL: array[0..7] of string = ('S1','S3','S5','S7','S9','+20','+40','+60');
|
||||
CLR_SMETER_BG = TColor($00181818);
|
||||
CLR_BAR_GREEN = TColor($00091F00);
|
||||
CLR_BAR_OVER = TColor($00091528);
|
||||
CLR_PEAK_MARKER = TColor($00F8F8FF);
|
||||
CLR_TICK_GREEN = TColor($00708070);
|
||||
CLR_TICK_WHITE = TColor($00E0E0DC);
|
||||
CLR_TICK_BLUE = TColor($0070A090);
|
||||
CLR_LABEL_DBM = TColor($0096D8DC);
|
||||
CLR_LABEL_S = TColor($00F0F0F0);
|
||||
CLR_SCALE_DBM = TColor($00D0DCDC);
|
||||
CLR_SCALE_S_GREEN = TColor($00E0E0E0);
|
||||
CLR_SCALE_S_BLUE = TColor($0090D090);
|
||||
CLR_BDR = TColor($00303028);
|
||||
LEFT_INFO_MIN = 62; RIGHT_PAD = 8; LABEL_PAD = 4;
|
||||
TICK_LONG = 5; TICK_MED = 3;
|
||||
var
|
||||
CLR_SMETER_BG, CLR_BAR_GREEN, CLR_BAR_OVER: TColor;
|
||||
CLR_PEAK_MARKER, CLR_TICK_GREEN, CLR_TICK_WHITE, CLR_TICK_BLUE: TColor;
|
||||
CLR_LABEL_DBM, CLR_LABEL_S, CLR_SCALE_DBM: TColor;
|
||||
CLR_SCALE_S_GREEN, CLR_SCALE_S_BLUE, CLR_BDR: TColor;
|
||||
W, H, BX, BW, LeftInfo: Integer;
|
||||
Y_BAR_TOP, Y_BAR_BOT: Integer;
|
||||
BarEnd, PeakLeft, PeakRight, S9X, OvrX: Integer;
|
||||
@@ -960,6 +971,20 @@ var
|
||||
end;
|
||||
|
||||
begin
|
||||
CLR_SMETER_BG := FTheme.SMeterBG;
|
||||
CLR_BAR_GREEN := FTheme.SMeterBarGreen;
|
||||
CLR_BAR_OVER := FTheme.SMeterBarOver;
|
||||
CLR_PEAK_MARKER := FTheme.SMeterPeakMarker;
|
||||
CLR_TICK_GREEN := FTheme.SMeterTickGreen;
|
||||
CLR_TICK_WHITE := FTheme.SMeterTickWhite;
|
||||
CLR_TICK_BLUE := FTheme.SMeterTickBlue;
|
||||
CLR_LABEL_DBM := FTheme.SMeterLabelDbm;
|
||||
CLR_LABEL_S := FTheme.SMeterLabelS;
|
||||
CLR_SCALE_DBM := FTheme.SMeterScaleDbm;
|
||||
CLR_SCALE_S_GREEN:= FTheme.SMeterScaleSGreen;
|
||||
CLR_SCALE_S_BLUE := FTheme.SMeterScaleSBlue;
|
||||
CLR_BDR := FTheme.SMeterBdr;
|
||||
|
||||
W := R.Right - R.Left; H := R.Bottom - R.Top;
|
||||
ZX1 := 0; ZX2 := 0; ZY1 := 0; ZY2 := 0;
|
||||
if (W < 80) or (H < 16) then Exit;
|
||||
@@ -997,9 +1022,9 @@ begin
|
||||
|
||||
ACanvas.Brush.Color := CLR_SMETER_BG; ACanvas.Brush.Style := bsSolid;
|
||||
ACanvas.Pen.Style := psClear; ACanvas.FillRect(R); ACanvas.Pen.Style := psSolid;
|
||||
FillBar(BX, Y_BAR_TOP, S9X, Y_BAR_BOT, TColor($00061006));
|
||||
FillBar(S9X, Y_BAR_TOP, OvrX, Y_BAR_BOT, TColor($000A1006));
|
||||
FillBar(OvrX, Y_BAR_TOP, BX + BW, Y_BAR_BOT, TColor($00060A10));
|
||||
FillBar(BX, Y_BAR_TOP, S9X, Y_BAR_BOT, CLR_BAR_GREEN);
|
||||
FillBar(S9X, Y_BAR_TOP, OvrX, Y_BAR_BOT, CLR_BAR_GREEN);
|
||||
FillBar(OvrX, Y_BAR_TOP, BX + BW, Y_BAR_BOT, CLR_BAR_OVER);
|
||||
|
||||
BarEnd := DBtoX(Value);
|
||||
PeakLeft := DBtoX(MinVal);
|
||||
@@ -1029,7 +1054,7 @@ begin
|
||||
ACanvas.Brush.Style := bsClear; ACanvas.Pen.Color := CLR_BDR;
|
||||
ACanvas.Rectangle(LblZoneL, Y_BAR_TOP, BX + BW, Y_BAR_BOT);
|
||||
// разделитель между зоной надписей и шкалой
|
||||
ACanvas.Pen.Color := TColor($00404040);
|
||||
ACanvas.Pen.Color := FTheme.SMeterDivider;
|
||||
ACanvas.MoveTo(BX, Y_BAR_TOP + 1); ACanvas.LineTo(BX, Y_BAR_BOT - 1);
|
||||
|
||||
// --- Шкальные тики и надписи ---
|
||||
@@ -1066,7 +1091,7 @@ begin
|
||||
// "-85.3" над "dBm", снизу прижаты к верхнему краю полосы
|
||||
ACanvas.Font.Size := 10; ACanvas.Font.Style := [fsBold]; ACanvas.Font.Color := CLR_LABEL_DBM;
|
||||
ACanvas.TextOut(LblZoneL + 4, Y_BAR_TOP - TH10 - 2, LblDbm);
|
||||
ACanvas.Font.Size := 6; ACanvas.Font.Style := []; ACanvas.Font.Color := TColor($00507070);
|
||||
ACanvas.Font.Size := 6; ACanvas.Font.Style := []; ACanvas.Font.Color := CLR_TICK_BLUE;
|
||||
ACanvas.TextOut(LblZoneL + 4 + TWDbm + 2, Y_BAR_TOP - TH6 - 4, 'dBm');
|
||||
// S-значение под полосой, левый край у LblZoneL+4
|
||||
ACanvas.Font.Size := 10; ACanvas.Font.Style := [fsBold]; ACanvas.Font.Color := CLR_LABEL_S;
|
||||
|
||||
Reference in New Issue
Block a user