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:
+303
@@ -0,0 +1,303 @@
|
||||
unit AppTheme;
|
||||
|
||||
{ Цветовые темы приложения EWSDR.
|
||||
Определяет TAppTheme — запись со всеми цветами интерфейса.
|
||||
Два конструктора: DarkTheme() и LightTheme(). }
|
||||
|
||||
{$mode objfpc}{$H+}
|
||||
|
||||
interface
|
||||
|
||||
uses Graphics;
|
||||
|
||||
type
|
||||
TAppTheme = record
|
||||
// Форма / панели
|
||||
BG, Panel, Border: TColor;
|
||||
// Текст
|
||||
Text, TextDim: TColor;
|
||||
// Обычные кнопки (StyleButton)
|
||||
BtnNorm, BtnActive, BtnHot: TColor;
|
||||
BtnBorderNorm, BtnBorderActive: TColor;
|
||||
BtnText, BtnTextActive: TColor;
|
||||
// Кнопки SPAN / VFO-copy / HidePanel (StyleSpanButton)
|
||||
SpanNorm, SpanActive: TColor;
|
||||
SpanBorder: TColor;
|
||||
SpanText: TColor;
|
||||
// Кнопки тулбара (Discover / StartStop)
|
||||
TbDiscoverNorm, TbDiscoverHot, TbDiscoverBorder: TColor;
|
||||
TbDiscoverText: TColor;
|
||||
TbStartNorm, TbStartBorder, TbStartText: TColor;
|
||||
// VFO-select кнопка — активный VFO
|
||||
VfoSelActNorm, VfoSelActBorder: TColor;
|
||||
VfoSelActText, VfoSelActTextAct: TColor;
|
||||
// VFO-select кнопка — неактивный VFO
|
||||
VfoSelInaNorm, VfoSelInaBorder: TColor;
|
||||
VfoSelInaText, VfoSelInaTextAct: TColor;
|
||||
// VFO-TX кнопка — активна (TX на этом VFO)
|
||||
VfoTxActNorm, VfoTxActBorder: TColor;
|
||||
VfoTxActText: TColor;
|
||||
// VFO-TX кнопка — неактивна
|
||||
VfoTxInaNorm, VfoTxInaBorder: TColor;
|
||||
VfoTxInaText: TColor;
|
||||
// Дисплей частоты
|
||||
FreqActive, FreqDim: TColor;
|
||||
// Индикаторы (S-meter, мощность)
|
||||
MeterOn, MeterOvr: TColor;
|
||||
Amber: TColor;
|
||||
// Спектр — фон и сетка
|
||||
SpecGradTop, SpecGradBot: TColor;
|
||||
SpecLabelBand: TColor;
|
||||
SpecLabelText: TColor;
|
||||
SpecGrid: TColor;
|
||||
// Спектр — сигнал и маркеры
|
||||
SpecLine: TColor;
|
||||
SpecFilter: TColor;
|
||||
SpecFilterEdge: TColor;
|
||||
SpecAgcColor: TColor;
|
||||
SpecAgcHangColor: TColor;
|
||||
SpecVfoCursor: TColor;
|
||||
// Спектр — градиент заливки (каналы Blue/Green/Red в сыром пикселе)
|
||||
SpecGradB, SpecGradG, SpecGradR: Byte; // при Alpha=200
|
||||
// Линейка частот
|
||||
RulerGradTop, RulerGradBot: TColor;
|
||||
RulerBorder: TColor;
|
||||
RulerText: TColor;
|
||||
RulerVfo: TColor;
|
||||
// Ползунки
|
||||
SliderBG: TColor;
|
||||
SliderTrackEmpty: TColor;
|
||||
SliderTrackFill: TColor;
|
||||
SliderThumbNorm: TColor;
|
||||
SliderThumbHot: TColor;
|
||||
SliderThumbDrag: TColor;
|
||||
SliderThumbBdr: TColor;
|
||||
// S-метр (широкий)
|
||||
SMeterBG: TColor;
|
||||
SMeterBarGreen: TColor;
|
||||
SMeterBarOver: TColor;
|
||||
SMeterPeakMarker: TColor;
|
||||
SMeterTickGreen: TColor;
|
||||
SMeterTickWhite: TColor;
|
||||
SMeterTickBlue: TColor;
|
||||
SMeterLabelDbm: TColor;
|
||||
SMeterLabelS: TColor;
|
||||
SMeterScaleDbm: TColor;
|
||||
SMeterScaleSGreen: TColor;
|
||||
SMeterScaleSBlue: TColor;
|
||||
SMeterBdr: TColor;
|
||||
SMeterDivider: TColor;
|
||||
end;
|
||||
|
||||
function DarkTheme: TAppTheme;
|
||||
function LightTheme: TAppTheme;
|
||||
|
||||
implementation
|
||||
|
||||
function DarkTheme: TAppTheme;
|
||||
begin
|
||||
FillChar(Result, SizeOf(Result), 0);
|
||||
Result.BG := TColor($00101010);
|
||||
Result.Panel := TColor($00181818);
|
||||
Result.Border := TColor($00303030);
|
||||
Result.Text := TColor($00CCCCCC);
|
||||
Result.TextDim := TColor($00666666);
|
||||
|
||||
Result.BtnNorm := TColor($001A1A1A);
|
||||
Result.BtnActive := TColor($000C3010);
|
||||
Result.BtnHot := TColor($00282828);
|
||||
Result.BtnBorderNorm := TColor($00444444);
|
||||
Result.BtnBorderActive := TColor($00387838);
|
||||
Result.BtnText := TColor($00CCCCCC);
|
||||
Result.BtnTextActive := TColor($0000FF88);
|
||||
|
||||
Result.SpanNorm := TColor($00543F18);
|
||||
Result.SpanActive := TColor($00624B1D);
|
||||
Result.SpanBorder := TColor($00685624);
|
||||
Result.SpanText := TColor($00D8E6EE);
|
||||
|
||||
Result.TbDiscoverNorm := TColor($00101828);
|
||||
Result.TbDiscoverHot := TColor($00182038);
|
||||
Result.TbDiscoverBorder := TColor($00304860);
|
||||
Result.TbDiscoverText := TColor($0090B8D0);
|
||||
Result.TbStartNorm := TColor($00101810);
|
||||
Result.TbStartBorder := TColor($00304830);
|
||||
Result.TbStartText := TColor($0090C090);
|
||||
|
||||
Result.VfoSelActNorm := TColor($00101C10);
|
||||
Result.VfoSelActBorder := TColor($004A7A4A);
|
||||
Result.VfoSelActText := TColor($0060B860);
|
||||
Result.VfoSelActTextAct := TColor($0040FF60);
|
||||
Result.VfoSelInaNorm := TColor($001A1A1A);
|
||||
Result.VfoSelInaBorder := TColor($00404040);
|
||||
Result.VfoSelInaText := TColor($00666666);
|
||||
Result.VfoSelInaTextAct := TColor($00CCCCCC);
|
||||
|
||||
Result.VfoTxActNorm := TColor($00000060);
|
||||
Result.VfoTxActBorder := TColor($001010B0);
|
||||
Result.VfoTxActText := TColor($00C8D8FF);
|
||||
Result.VfoTxInaNorm := TColor($00303030);
|
||||
Result.VfoTxInaBorder := TColor($00404040);
|
||||
Result.VfoTxInaText := TColor($00808080);
|
||||
|
||||
Result.FreqActive := TColor($0000FF00);
|
||||
Result.FreqDim := TColor($00007800);
|
||||
|
||||
Result.MeterOn := TColor($0000CC44);
|
||||
Result.MeterOvr := TColor($000000CC);
|
||||
Result.Amber := TColor($0000AAFF);
|
||||
|
||||
Result.SpecGradTop := TColor($0020160B);
|
||||
Result.SpecGradBot := TColor($006C4F1F);
|
||||
Result.SpecLabelBand := TColor($00543F18);
|
||||
Result.SpecLabelText := TColor($00A8C9D9);
|
||||
Result.SpecGrid := TColor($00685624);
|
||||
Result.SpecLine := TColor($0040FF80);
|
||||
Result.SpecFilter := TColor($006A4E22);
|
||||
Result.SpecFilterEdge := TColor($0040FFCC);
|
||||
Result.SpecAgcColor := TColor($0000AAFF);
|
||||
Result.SpecAgcHangColor := TColor($00FFCC00);
|
||||
Result.SpecVfoCursor := TColor($0000AAFF);
|
||||
Result.SpecGradB := 100; Result.SpecGradG := 200; Result.SpecGradR := 0;
|
||||
|
||||
Result.RulerGradTop := TColor($00372A12);
|
||||
Result.RulerGradBot := TColor($00705A2A);
|
||||
Result.RulerBorder := TColor($00685624);
|
||||
Result.RulerText := TColor($00C8D8E2);
|
||||
Result.RulerVfo := TColor($0000FF80);
|
||||
|
||||
Result.SliderBG := TColor($00181818);
|
||||
Result.SliderTrackEmpty := TColor($00323232);
|
||||
Result.SliderTrackFill := TColor($00205018);
|
||||
Result.SliderThumbNorm := TColor($00407840);
|
||||
Result.SliderThumbHot := TColor($0066BB66);
|
||||
Result.SliderThumbDrag := TColor($0099EE99);
|
||||
Result.SliderThumbBdr := TColor($00122012);
|
||||
|
||||
Result.SMeterBG := TColor($00181818);
|
||||
Result.SMeterBarGreen := TColor($00091F00);
|
||||
Result.SMeterBarOver := TColor($00091528);
|
||||
Result.SMeterPeakMarker := TColor($00F8F8FF);
|
||||
Result.SMeterTickGreen := TColor($00708070);
|
||||
Result.SMeterTickWhite := TColor($00E0E0DC);
|
||||
Result.SMeterTickBlue := TColor($0070A090);
|
||||
Result.SMeterLabelDbm := TColor($0096D8DC);
|
||||
Result.SMeterLabelS := TColor($00F0F0F0);
|
||||
Result.SMeterScaleDbm := TColor($00D0DCDC);
|
||||
Result.SMeterScaleSGreen:= TColor($00E0E0E0);
|
||||
Result.SMeterScaleSBlue := TColor($0090D090);
|
||||
Result.SMeterBdr := TColor($00303028);
|
||||
Result.SMeterDivider := TColor($00404040);
|
||||
end;
|
||||
|
||||
function LightTheme: TAppTheme;
|
||||
begin
|
||||
FillChar(Result, SizeOf(Result), 0);
|
||||
// Фон — тёплый белый, панели — тёплый светло-серый
|
||||
Result.BG := TColor($00F0EEE8);
|
||||
Result.Panel := TColor($00E8E6E0);
|
||||
Result.Border := TColor($00B8B4AC);
|
||||
Result.Text := TColor($00181410);
|
||||
Result.TextDim := TColor($00706860);
|
||||
|
||||
// Обычные кнопки — светло-серые, активная — светло-зелёная
|
||||
Result.BtnNorm := TColor($00DCDAD4);
|
||||
Result.BtnActive := TColor($00B8D8B8);
|
||||
Result.BtnHot := TColor($00CFCDC6);
|
||||
Result.BtnBorderNorm := TColor($00A8A4A0);
|
||||
Result.BtnBorderActive := TColor($00407840);
|
||||
Result.BtnText := TColor($00181410);
|
||||
Result.BtnTextActive := TColor($00004800);
|
||||
|
||||
// Span/VFO-copy — тёплый тан/пергамент
|
||||
Result.SpanNorm := TColor($00D4C8A0);
|
||||
Result.SpanActive := TColor($00C0B488);
|
||||
Result.SpanBorder := TColor($00A09070);
|
||||
Result.SpanText := TColor($00302010);
|
||||
|
||||
// Тулбар — более насыщенные оттенки
|
||||
Result.TbDiscoverNorm := TColor($00C8D8E8);
|
||||
Result.TbDiscoverHot := TColor($00B8CCE0);
|
||||
Result.TbDiscoverBorder := TColor($00789CB8);
|
||||
Result.TbDiscoverText := TColor($00284860);
|
||||
Result.TbStartNorm := TColor($00C8E0C8);
|
||||
Result.TbStartBorder := TColor($00609860);
|
||||
Result.TbStartText := TColor($00204820);
|
||||
|
||||
// VFO-select активный — зелёная подсветка
|
||||
Result.VfoSelActNorm := TColor($00C0E0C0);
|
||||
Result.VfoSelActBorder := TColor($00508050);
|
||||
Result.VfoSelActText := TColor($00205020);
|
||||
Result.VfoSelActTextAct := TColor($00006600);
|
||||
// VFO-select неактивный — нейтральный
|
||||
Result.VfoSelInaNorm := TColor($00DCDAD4);
|
||||
Result.VfoSelInaBorder := TColor($00A8A4A0);
|
||||
Result.VfoSelInaText := TColor($00706860);
|
||||
Result.VfoSelInaTextAct := TColor($00181410);
|
||||
|
||||
// VFO-TX — синеватый акцент
|
||||
Result.VfoTxActNorm := TColor($00C0CCEC);
|
||||
Result.VfoTxActBorder := TColor($006070C0);
|
||||
Result.VfoTxActText := TColor($00102060);
|
||||
Result.VfoTxInaNorm := TColor($00DCDAD4);
|
||||
Result.VfoTxInaBorder := TColor($00A8A4A0);
|
||||
Result.VfoTxInaText := TColor($00706860);
|
||||
|
||||
// Дисплей частоты — насыщенный зелёный
|
||||
Result.FreqActive := TColor($00005500);
|
||||
Result.FreqDim := TColor($00447044);
|
||||
|
||||
// Индикаторы
|
||||
Result.MeterOn := TColor($00006820);
|
||||
Result.MeterOvr := TColor($00AA1010);
|
||||
Result.Amber := TColor($00904000);
|
||||
|
||||
// Спектр — светлый пергаментный фон, тёмно-зелёный сигнал
|
||||
Result.SpecGradTop := TColor($00E8E4D8);
|
||||
Result.SpecGradBot := TColor($00D0CCBE);
|
||||
Result.SpecLabelBand := TColor($00D8D4C8);
|
||||
Result.SpecLabelText := TColor($00404038);
|
||||
Result.SpecGrid := TColor($00B8B4A4);
|
||||
Result.SpecLine := TColor($00006400); // тёмно-зелёная линия
|
||||
Result.SpecFilter := TColor($00C8DCC8); // светло-зелёная полоса фильтра
|
||||
Result.SpecFilterEdge := TColor($00407840);
|
||||
Result.SpecAgcColor := TColor($00904000); // тёмно-оранжевый AGC
|
||||
Result.SpecAgcHangColor := TColor($00802000);
|
||||
Result.SpecVfoCursor := TColor($00004800);
|
||||
// Пиксели градиента: тёмно-зелёный (B=0, G=80, R=10 при Alpha=200)
|
||||
Result.SpecGradB := 0; Result.SpecGradG := 80; Result.SpecGradR := 10;
|
||||
|
||||
// Линейка — тёплый пергамент
|
||||
Result.RulerGradTop := TColor($00DDD8CC);
|
||||
Result.RulerGradBot := TColor($00C8C2B4);
|
||||
Result.RulerBorder := TColor($00A09878);
|
||||
Result.RulerText := TColor($00302820);
|
||||
Result.RulerVfo := TColor($00006800);
|
||||
|
||||
// Ползунки — светлые с зелёным акцентом
|
||||
Result.SliderBG := TColor($00E8E6E0);
|
||||
Result.SliderTrackEmpty := TColor($00CCCAC4);
|
||||
Result.SliderTrackFill := TColor($0080AA80);
|
||||
Result.SliderThumbNorm := TColor($00508050);
|
||||
Result.SliderThumbHot := TColor($003D9040);
|
||||
Result.SliderThumbDrag := TColor($0028A830);
|
||||
Result.SliderThumbBdr := TColor($00183018);
|
||||
|
||||
// S-метр — светлый
|
||||
Result.SMeterBG := TColor($00ECE8E0);
|
||||
Result.SMeterBarGreen := TColor($00C0EAC0);
|
||||
Result.SMeterBarOver := TColor($00EAC0C0);
|
||||
Result.SMeterPeakMarker := TColor($00181010);
|
||||
Result.SMeterTickGreen := TColor($00508050);
|
||||
Result.SMeterTickWhite := TColor($00282018);
|
||||
Result.SMeterTickBlue := TColor($00406880);
|
||||
Result.SMeterLabelDbm := TColor($00204860);
|
||||
Result.SMeterLabelS := TColor($00201810);
|
||||
Result.SMeterScaleDbm := TColor($00304848);
|
||||
Result.SMeterScaleSGreen:= TColor($00204020);
|
||||
Result.SMeterScaleSBlue := TColor($00204050);
|
||||
Result.SMeterBdr := TColor($00A0A090);
|
||||
Result.SMeterDivider := TColor($00B0ACA0);
|
||||
end;
|
||||
|
||||
end.
|
||||
+66
-2
@@ -5,7 +5,7 @@ unit DeviceForm;
|
||||
interface
|
||||
|
||||
uses
|
||||
Classes, SysUtils, FlatButton, Forms, Controls, Graphics, Dialogs,
|
||||
Classes, SysUtils, FlatButton, AppTheme, Forms, Controls, Graphics, Dialogs,
|
||||
StdCtrls, ExtCtrls, ComCtrls, IniFiles;
|
||||
|
||||
// Декодирование типа платы (совпадает с MainForm.BoardTypeName)
|
||||
@@ -94,6 +94,8 @@ type
|
||||
public
|
||||
constructor Create(AOwner: TComponent); override;
|
||||
|
||||
procedure SetTheme(const T: TAppTheme);
|
||||
|
||||
// Добавить найденное устройство (вызывается из MainForm при discovery)
|
||||
procedure AddDiscovered(const IP, DisplayName: string; BoardType: Integer = 0);
|
||||
procedure ClearDiscovered;
|
||||
@@ -271,7 +273,69 @@ end;
|
||||
|
||||
procedure TDeviceDialog.ApplyTheme;
|
||||
begin
|
||||
// уже задано в BuildUI
|
||||
end;
|
||||
|
||||
procedure TDeviceDialog.SetTheme(const T: TAppTheme);
|
||||
|
||||
procedure StyleBtn(B: TFlatButton; Accent: Boolean);
|
||||
begin
|
||||
B.ClrNorm := T.Panel;
|
||||
B.ClrBorder := T.Border;
|
||||
B.ClrHot := T.BG;
|
||||
B.ClrActive := T.Panel;
|
||||
B.ClrText := T.Text;
|
||||
if Accent then B.ClrTextAct := T.FreqActive
|
||||
else B.ClrTextAct := T.Text;
|
||||
B.Font.Color := T.Text;
|
||||
B.Invalidate;
|
||||
end;
|
||||
|
||||
procedure WalkLabels(C: TWinControl);
|
||||
var i: Integer; Ctrl: TControl;
|
||||
begin
|
||||
for i := 0 to C.ControlCount - 1 do
|
||||
begin
|
||||
Ctrl := C.Controls[i];
|
||||
if Ctrl is TLabel then
|
||||
TLabel(Ctrl).Font.Color := T.TextDim
|
||||
else if Ctrl is TWinControl then
|
||||
WalkLabels(TWinControl(Ctrl));
|
||||
end;
|
||||
end;
|
||||
|
||||
begin
|
||||
Color := T.BG;
|
||||
Font.Color := T.Text;
|
||||
if PanelLeft <> nil then PanelLeft.Color := T.Panel;
|
||||
if PanelRight <> nil then PanelRight.Color := T.Panel;
|
||||
if LstSaved <> nil then begin
|
||||
LstSaved.Color := T.BG;
|
||||
LstSaved.Font.Color := T.Text;
|
||||
end;
|
||||
if LstFound <> nil then begin
|
||||
LstFound.Color := T.BG;
|
||||
LstFound.Font.Color := T.Text;
|
||||
end;
|
||||
if BtnAdd <> nil then StyleBtn(BtnAdd, False);
|
||||
if BtnRemove <> nil then StyleBtn(BtnRemove, False);
|
||||
if BtnSetAuto <> nil then StyleBtn(BtnSetAuto, False);
|
||||
if BtnDiscover <> nil then StyleBtn(BtnDiscover, False);
|
||||
if BtnAddFound <> nil then StyleBtn(BtnAddFound, False);
|
||||
if BtnCancel <> nil then StyleBtn(BtnCancel, False);
|
||||
if BtnConnect <> nil then StyleBtn(BtnConnect, True);
|
||||
// Edit fields: white bg for light theme, dark bg for dark theme
|
||||
if Integer(T.BG) > Integer(TColor($00808080)) then
|
||||
begin
|
||||
if EdName <> nil then begin EdName.Color := TColor($00FFFFFF); EdName.Font.Color := TColor($00202020); end;
|
||||
if EdIP <> nil then begin EdIP.Color := TColor($00FFFFFF); EdIP.Font.Color := TColor($00202020); end;
|
||||
end
|
||||
else
|
||||
begin
|
||||
if EdName <> nil then begin EdName.Color := TColor($00121212); EdName.Font.Color := TColor($00E0E0E0); end;
|
||||
if EdIP <> nil then begin EdIP.Color := TColor($00121212); EdIP.Font.Color := TColor($00E0E0E0); end;
|
||||
end;
|
||||
WalkLabels(Self);
|
||||
Invalidate;
|
||||
end;
|
||||
|
||||
procedure TDeviceDialog.LoadSaved;
|
||||
|
||||
+51
-31
@@ -1,7 +1,8 @@
|
||||
unit FlatSlider;
|
||||
|
||||
{ Кастомный горизонтальный ползунок с тёмной темой — замена TTrackBar.
|
||||
Полностью отрисован через Canvas, одинаково выглядит на Windows и Linux. }
|
||||
{ Кастомный горизонтальный ползунок — замена TTrackBar.
|
||||
Полностью отрисован через Canvas, одинаково выглядит на Windows и Linux.
|
||||
Цвета настраиваемые: применяй SetThemeColors(T: TAppTheme) для смены темы. }
|
||||
|
||||
{$mode objfpc}{$H+}
|
||||
|
||||
@@ -20,6 +21,14 @@ type
|
||||
FDragging: Boolean;
|
||||
FHot: Boolean;
|
||||
FOnChange: TNotifyEvent;
|
||||
// Цвета (настраиваются через SetThemeColors или напрямую)
|
||||
FClrBG: TColor;
|
||||
FClrTrackEmpty: TColor;
|
||||
FClrTrackFill: TColor;
|
||||
FClrThumbNorm: TColor;
|
||||
FClrThumbHot: TColor;
|
||||
FClrThumbDrag: TColor;
|
||||
FClrThumbBdr: TColor;
|
||||
procedure SetPosition(V: Integer);
|
||||
procedure SetMin(V: Integer);
|
||||
procedure SetMax(V: Integer);
|
||||
@@ -39,6 +48,11 @@ type
|
||||
public
|
||||
constructor Create(AOwner: TComponent); override;
|
||||
|
||||
// Применить набор цветов из темы
|
||||
procedure SetThemeColors(
|
||||
ABG, ATrackEmpty, ATrackFill,
|
||||
AThumbNorm, AThumbHot, AThumbDrag, AThumbBdr: TColor);
|
||||
|
||||
property Min: Integer read FMin write SetMin;
|
||||
property Max: Integer read FMax write SetMax;
|
||||
property Position: Integer read FPosition write SetPosition;
|
||||
@@ -51,17 +65,9 @@ type
|
||||
implementation
|
||||
|
||||
const
|
||||
THUMB_R = 6; // радиус ручки (px)
|
||||
TRACK_H = 4; // высота рельса (px)
|
||||
PAD = THUMB_R + 2; // боковой отступ чтобы ручка не вылезала
|
||||
|
||||
CLR_BG = TColor($00181818); // совпадает с CLR_PANEL
|
||||
CLR_TRACK_EMPTY = TColor($00323232);
|
||||
CLR_TRACK_FILL = TColor($00205018); // тёмно-зелёный, как тема
|
||||
CLR_THUMB_NORM = TColor($00407840);
|
||||
CLR_THUMB_HOT = TColor($0066BB66);
|
||||
CLR_THUMB_DRAG = TColor($0099EE99);
|
||||
CLR_THUMB_BDR = TColor($00122012);
|
||||
THUMB_R = 6;
|
||||
TRACK_H = 4;
|
||||
PAD = THUMB_R + 2;
|
||||
|
||||
constructor TFlatSlider.Create(AOwner: TComponent);
|
||||
begin
|
||||
@@ -73,11 +79,32 @@ begin
|
||||
FDragging := False;
|
||||
FHot := False;
|
||||
Cursor := crHandPoint;
|
||||
// Тёмная тема по умолчанию
|
||||
FClrBG := TColor($00181818);
|
||||
FClrTrackEmpty := TColor($00323232);
|
||||
FClrTrackFill := TColor($00205018);
|
||||
FClrThumbNorm := TColor($00407840);
|
||||
FClrThumbHot := TColor($0066BB66);
|
||||
FClrThumbDrag := TColor($0099EE99);
|
||||
FClrThumbBdr := TColor($00122012);
|
||||
end;
|
||||
|
||||
procedure TFlatSlider.SetThemeColors(
|
||||
ABG, ATrackEmpty, ATrackFill,
|
||||
AThumbNorm, AThumbHot, AThumbDrag, AThumbBdr: TColor);
|
||||
begin
|
||||
FClrBG := ABG;
|
||||
FClrTrackEmpty := ATrackEmpty;
|
||||
FClrTrackFill := ATrackFill;
|
||||
FClrThumbNorm := AThumbNorm;
|
||||
FClrThumbHot := AThumbHot;
|
||||
FClrThumbDrag := AThumbDrag;
|
||||
FClrThumbBdr := AThumbBdr;
|
||||
Invalidate;
|
||||
end;
|
||||
|
||||
procedure TFlatSlider.SetPosition(V: Integer);
|
||||
var
|
||||
C: Integer;
|
||||
var C: Integer;
|
||||
begin
|
||||
C := EnsureRange(V, FMin, FMax);
|
||||
if FPosition = C then Exit;
|
||||
@@ -102,10 +129,8 @@ begin
|
||||
Invalidate;
|
||||
end;
|
||||
|
||||
// Позиция значения → пиксельная X-координата ручки
|
||||
function TFlatSlider.PosToX(APos: Integer): Integer;
|
||||
var
|
||||
Range, TrackW: Integer;
|
||||
var Range, TrackW: Integer;
|
||||
begin
|
||||
Range := FMax - FMin;
|
||||
TrackW := Width - PAD * 2;
|
||||
@@ -116,10 +141,8 @@ begin
|
||||
Result := PAD + Round((APos - FMin) / Range * TrackW);
|
||||
end;
|
||||
|
||||
// Пиксельная X-координата → значение позиции
|
||||
function TFlatSlider.XToPos(AX: Integer): Integer;
|
||||
var
|
||||
Range, TrackW, RawX: Integer;
|
||||
var Range, TrackW, RawX: Integer;
|
||||
begin
|
||||
Range := FMax - FMin;
|
||||
TrackW := Width - PAD * 2;
|
||||
@@ -141,32 +164,29 @@ begin
|
||||
CY := Height div 2;
|
||||
TX := PosToX(FPosition);
|
||||
|
||||
Canvas.Brush.Color := CLR_BG;
|
||||
Canvas.Brush.Color := FClrBG;
|
||||
Canvas.Brush.Style := bsSolid;
|
||||
Canvas.Pen.Style := psClear;
|
||||
Canvas.FillRect(ClientRect);
|
||||
|
||||
// Рельс — весь
|
||||
TR := Rect(PAD, CY - TRACK_H div 2,
|
||||
Width - PAD, CY + (TRACK_H + 1) div 2);
|
||||
Canvas.Brush.Color := CLR_TRACK_EMPTY;
|
||||
Canvas.Brush.Color := FClrTrackEmpty;
|
||||
Canvas.RoundRect(TR.Left, TR.Top, TR.Right, TR.Bottom, TRACK_H, TRACK_H);
|
||||
|
||||
// Рельс — заполненная часть
|
||||
if not FReversed then begin FL := PAD; FR := TX; end
|
||||
else begin FL := TX; FR := Width - PAD; end;
|
||||
if FR > FL then
|
||||
begin
|
||||
Canvas.Brush.Color := CLR_TRACK_FILL;
|
||||
Canvas.Brush.Color := FClrTrackFill;
|
||||
Canvas.RoundRect(FL, TR.Top, FR, TR.Bottom, TRACK_H, TRACK_H);
|
||||
end;
|
||||
|
||||
// Ручка — круг
|
||||
if FDragging then ThC := CLR_THUMB_DRAG
|
||||
else if FHot then ThC := CLR_THUMB_HOT
|
||||
else ThC := CLR_THUMB_NORM;
|
||||
if FDragging then ThC := FClrThumbDrag
|
||||
else if FHot then ThC := FClrThumbHot
|
||||
else ThC := FClrThumbNorm;
|
||||
Canvas.Brush.Color := ThC;
|
||||
Canvas.Pen.Color := CLR_THUMB_BDR;
|
||||
Canvas.Pen.Color := FClrThumbBdr;
|
||||
Canvas.Pen.Style := psSolid;
|
||||
Canvas.Pen.Width := 1;
|
||||
Canvas.Ellipse(TX - THUMB_R, CY - THUMB_R,
|
||||
|
||||
+168
-82
@@ -22,7 +22,7 @@ unit MainForm;
|
||||
interface
|
||||
|
||||
uses
|
||||
Classes, SysUtils, StrUtils, FreqDisplay, DeviceForm, VfoOverlay, FlatButton, FlatSlider, Forms, Controls, Graphics, Dialogs,
|
||||
Classes, SysUtils, StrUtils, FreqDisplay, DeviceForm, VfoOverlay, FlatButton, FlatSlider, AppTheme, Forms, Controls, Graphics, Dialogs,
|
||||
StdCtrls, ExtCtrls, ComCtrls, Buttons, Menus, Math, Types,
|
||||
LCLIntf, LCLType, GraphType,
|
||||
HPSDRProtocol, HPSDRNetwork,
|
||||
@@ -249,6 +249,7 @@ type
|
||||
FPendingDev: THPSDRDevice; // устройство ожидающее открытия WDSP
|
||||
FBandCache: array[0..CFG_BAND_COUNT-1] of TBandSettings; // кэш диапазонов
|
||||
FDevConnected: Boolean; // True после первого подключения
|
||||
FLightTheme: Boolean; // True = светлая тема
|
||||
FCenterFreq: Double; // центр спектра = LO (DDC). При CTUN ON не меняется при кручении VFO
|
||||
FSpanHz: Double;
|
||||
FSampleRate: Integer; // текущий DDC sample rate (Hz)
|
||||
@@ -345,6 +346,7 @@ type
|
||||
// ---- Helpers ----
|
||||
procedure BuildUI;
|
||||
procedure ApplyDarkTheme;
|
||||
procedure SetLightTheme(V: Boolean);
|
||||
procedure StyleButton(B: TFlatButton; Active: Boolean = False);
|
||||
procedure StyleSpanButton(B: TFlatButton; Active: Boolean = False);
|
||||
procedure PositionSpanOverlayButtons;
|
||||
@@ -845,6 +847,7 @@ begin
|
||||
Result.ShowSpectrum := FShowSpectrum;
|
||||
Result.ShowWaterfall := FShowWaterfall;
|
||||
Result.DisplayFPS := FDisplayFPS;
|
||||
Result.LightTheme := FLightTheme;
|
||||
// Audio device names
|
||||
Result.AudioOutDevice := FAudioOutDevName;
|
||||
Result.AudioInDevice := FAudioInDevName;
|
||||
@@ -1053,6 +1056,7 @@ begin
|
||||
{$ENDIF}
|
||||
end;
|
||||
FSettings.LoadCATSettings(FCATLastGlobal);
|
||||
FLightTheme := FSettings.LoadTheme;
|
||||
InitCATEngine;
|
||||
CATApplySettings(FCATLastGlobal);
|
||||
FSMeterPeak := -130;
|
||||
@@ -2125,12 +2129,20 @@ end;
|
||||
// ===========================================================================
|
||||
|
||||
procedure TMainForm.ApplyDarkTheme;
|
||||
var T: TAppTheme; i: Integer;
|
||||
procedure DP(P: TPanel);
|
||||
begin P.Color := T.Panel; P.Font.Color := T.Text; end;
|
||||
procedure DL(L: TLabel; Dim: Boolean = True);
|
||||
begin if Dim then L.Font.Color := T.TextDim else L.Font.Color := T.Text; end;
|
||||
procedure DS(S: TFlatSlider);
|
||||
begin
|
||||
P.Color := CLR_PANEL; P.Font.Color := CLR_TEXT;
|
||||
S.SetThemeColors(T.SliderBG, T.SliderTrackEmpty, T.SliderTrackFill,
|
||||
T.SliderThumbNorm, T.SliderThumbHot, T.SliderThumbDrag, T.SliderThumbBdr);
|
||||
end;
|
||||
begin
|
||||
Color := CLR_BG; Font.Color := CLR_TEXT;
|
||||
if FLightTheme then T := LightTheme else T := DarkTheme;
|
||||
|
||||
Color := T.BG; Font.Color := T.Text;
|
||||
DP(PanelToolbar); DP(PanelLeft);
|
||||
DP(PanelTopVfoGroup); DP(PanelTopVfoA); DP(PanelTopVfoB);
|
||||
DP(PanelVfoA); DP(PanelVfoB); DP(PanelVfoButtons);
|
||||
@@ -2138,38 +2150,107 @@ begin
|
||||
DP(PanelRX); DP(PanelTX);
|
||||
DP(PanelRight); DP(PanelSpanButtons);
|
||||
|
||||
PbSpectrum.Color := T.BG;
|
||||
PbWaterfall.Color := T.BG;
|
||||
if PanelSMeterRight <> nil then PanelSMeterRight.Color := T.Panel;
|
||||
if PbSMeterRight <> nil then PbSMeterRight.Color := T.Panel;
|
||||
PbFwdPower.Color := T.BG;
|
||||
PbSWR.Color := T.BG;
|
||||
|
||||
PbSpectrum.Color := CLR_BG;
|
||||
PbWaterfall.Color := CLR_BG;
|
||||
if PanelSMeterRight <> nil then PanelSMeterRight.Color := CLR_PANEL;
|
||||
if PbSMeterRight <> nil then PbSMeterRight.Color := CLR_PANEL;
|
||||
PbFwdPower.Color := CLR_BG;
|
||||
PbSWR.Color := CLR_BG;
|
||||
// Метки
|
||||
DL(LblAGCTop, False);
|
||||
DL(LblDrv); DL(LblSpan);
|
||||
LblFwdPwr.Font.Color := T.MeterOn;
|
||||
LblSWRVal.Font.Color := T.TextDim;
|
||||
|
||||
// Кнопки тулбара
|
||||
BtnDiscover.ClrNorm := T.TbDiscoverNorm;
|
||||
BtnDiscover.ClrHot := T.TbDiscoverHot;
|
||||
BtnDiscover.ClrActive := T.TbDiscoverHot;
|
||||
BtnDiscover.ClrBorder := T.TbDiscoverBorder;
|
||||
BtnDiscover.ClrText := T.TbDiscoverText;
|
||||
BtnDiscover.ClrTextAct := T.TbDiscoverText;
|
||||
BtnDiscover.Invalidate;
|
||||
BtnStartStop.ClrNorm := T.TbStartNorm;
|
||||
BtnStartStop.ClrBorder := T.TbStartBorder;
|
||||
BtnStartStop.ClrText := T.TbStartText;
|
||||
BtnStartStop.Invalidate;
|
||||
|
||||
// Обычные кнопки — все массивы
|
||||
for i := 0 to BAND_COUNT-1 do StyleButton(BtnBand[i], BtnBand[i].Active);
|
||||
for i := 0 to MODE_COUNT-1 do StyleButton(BtnMode[i], BtnMode[i].Active);
|
||||
for i := 0 to FILT_COUNT-1 do StyleButton(BtnFilter[i], BtnFilter[i].Active);
|
||||
for i := 0 to 4 do StyleButton(BtnAGCMode[i],BtnAGCMode[i].Active);
|
||||
StyleButton(BtnCTun, BtnCTun.Active);
|
||||
StyleButton(BtnNR, BtnNR.Active);
|
||||
StyleButton(BtnNB, BtnNB.Active);
|
||||
StyleButton(BtnSNB, BtnSNB.Active);
|
||||
StyleButton(BtnANF, BtnANF.Active);
|
||||
StyleButton(BtnMute, BtnMute.Active);
|
||||
StyleButton(BtnWfAGC, BtnWfAGC.Active);
|
||||
StyleButton(BtnWfNF, BtnWfNF.Active);
|
||||
StyleButton(BtnMOX, BtnMOX.Active);
|
||||
|
||||
// Span / VFO-copy / HidePanel
|
||||
StyleSpanButton(BtnSpan48k, BtnSpan48k.Active);
|
||||
StyleSpanButton(BtnSpan96k, BtnSpan96k.Active);
|
||||
StyleSpanButton(BtnSpan192k, BtnSpan192k.Active);
|
||||
StyleSpanButton(BtnSpan384k, BtnSpan384k.Active);
|
||||
StyleSpanButton(BtnSpan768k, BtnSpan768k.Active);
|
||||
StyleSpanButton(BtnSpan1536k, BtnSpan1536k.Active);
|
||||
StyleSpanButton(BtnVfoACopyB, False);
|
||||
StyleSpanButton(BtnVfoSwap, False);
|
||||
StyleSpanButton(BtnVfoBCopyA, False);
|
||||
if BtnHidePanel <> nil then StyleSpanButton(BtnHidePanel, False);
|
||||
|
||||
// Ползунки
|
||||
DS(TrkAGC); DS(TrkVolume); DS(TrkDrive);
|
||||
|
||||
// Спектр
|
||||
FSpecView.SetTheme(T);
|
||||
|
||||
// VFO display — вызывает UpdateVfoDisplay, который использует тему
|
||||
UpdateVfoDisplay;
|
||||
end;
|
||||
|
||||
procedure TMainForm.SetLightTheme(V: Boolean);
|
||||
begin
|
||||
if FLightTheme = V then Exit;
|
||||
FLightTheme := V;
|
||||
ApplyDarkTheme;
|
||||
FSettings.SaveTheme(V);
|
||||
FSettings.Save;
|
||||
if (FSettingsForm <> nil) and TSettingsForm(FSettingsForm).Visible then
|
||||
TSettingsForm(FSettingsForm).LoadLightTheme(V);
|
||||
end;
|
||||
|
||||
procedure TMainForm.StyleButton(B: TFlatButton; Active: Boolean);
|
||||
var T: TAppTheme;
|
||||
begin
|
||||
if FLightTheme then T := LightTheme else T := DarkTheme;
|
||||
B.Active := Active;
|
||||
B.ClrNorm := CLR_INACTIVE;
|
||||
B.ClrActive := CLR_ACTIVE;
|
||||
B.ClrHot := TColor($00282828);
|
||||
if Active then B.ClrBorder := CLR_ACTIVE_BDR
|
||||
else B.ClrBorder := TColor($00444444);
|
||||
B.ClrText := CLR_TEXT;
|
||||
B.ClrTextAct := TColor($0000FF88);
|
||||
B.ClrNorm := T.BtnNorm;
|
||||
B.ClrActive := T.BtnActive;
|
||||
B.ClrHot := T.BtnHot;
|
||||
if Active then B.ClrBorder := T.BtnBorderActive
|
||||
else B.ClrBorder := T.BtnBorderNorm;
|
||||
B.ClrText := T.BtnText;
|
||||
B.ClrTextAct := T.BtnTextActive;
|
||||
B.Font.Name := 'Courier New';
|
||||
B.Font.Size := 8;
|
||||
end;
|
||||
|
||||
procedure TMainForm.StyleSpanButton(B: TFlatButton; Active: Boolean);
|
||||
var T: TAppTheme;
|
||||
begin
|
||||
if FLightTheme then T := LightTheme else T := DarkTheme;
|
||||
B.Active := Active;
|
||||
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.ClrNorm := T.SpanNorm;
|
||||
B.ClrActive := T.SpanActive;
|
||||
B.ClrHot := T.SpanActive;
|
||||
B.ClrBorder := T.SpanBorder;
|
||||
B.ClrText := T.SpanText;
|
||||
B.ClrTextAct := T.SpanText;
|
||||
B.Font.Name := 'Courier New';
|
||||
B.Font.Size := 7;
|
||||
end;
|
||||
@@ -2246,7 +2327,10 @@ end;
|
||||
procedure TMainForm.UpdateVfoDisplay;
|
||||
const
|
||||
TOP_VFO_FREQ_H = 47;
|
||||
var T: TAppTheme;
|
||||
begin
|
||||
if FLightTheme then T := LightTheme else T := DarkTheme;
|
||||
|
||||
FreqDispA.Frequency := Round(FVfoA);
|
||||
FreqDispB.Frequency := Round(FVfoB);
|
||||
FreqDispA.FontSize := TOP_VFO_FONT;
|
||||
@@ -2256,56 +2340,52 @@ begin
|
||||
|
||||
if FActiveVfo = 0 then
|
||||
begin
|
||||
FreqDispA.ColorNormal := CLR_FREQ;
|
||||
FreqDispB.ColorNormal := CLR_FREQ_DIM;
|
||||
LblVfoALabel.Font.Color := CLR_FREQ;
|
||||
LblVfoBLabel.Font.Color := CLR_TEXTDIM;
|
||||
// Активный VFO-A: зелёный акцент
|
||||
FreqDispA.ColorNormal := T.FreqActive;
|
||||
FreqDispB.ColorNormal := T.FreqDim;
|
||||
LblVfoALabel.Font.Color := T.FreqActive;
|
||||
LblVfoBLabel.Font.Color := T.TextDim;
|
||||
if BtnTopVfoASelect <> nil then begin
|
||||
BtnTopVfoASelect.ClrNorm := TColor($00101C10);
|
||||
BtnTopVfoASelect.ClrActive := TColor($00182818);
|
||||
BtnTopVfoASelect.ClrHot := TColor($00182818);
|
||||
BtnTopVfoASelect.ClrBorder := TColor($004A7A4A);
|
||||
BtnTopVfoASelect.ClrText := TColor($0060B860);
|
||||
BtnTopVfoASelect.ClrTextAct := TColor($0040FF60);
|
||||
BtnTopVfoASelect.ClrNorm := T.VfoSelActNorm;
|
||||
BtnTopVfoASelect.ClrActive := T.VfoSelActNorm;
|
||||
BtnTopVfoASelect.ClrHot := T.VfoSelActNorm;
|
||||
BtnTopVfoASelect.ClrBorder := T.VfoSelActBorder;
|
||||
BtnTopVfoASelect.ClrText := T.VfoSelActText;
|
||||
BtnTopVfoASelect.ClrTextAct := T.VfoSelActTextAct;
|
||||
BtnTopVfoASelect.Active := True;
|
||||
BtnTopVfoASelect.Invalidate;
|
||||
end;
|
||||
// Неактивный VFO-B: приглушённый
|
||||
if BtnTopVfoBSelect <> nil then begin
|
||||
BtnTopVfoBSelect.ClrNorm := TColor($001A1A1A);
|
||||
BtnTopVfoBSelect.ClrActive := TColor($00282828);
|
||||
BtnTopVfoBSelect.ClrHot := TColor($00252525);
|
||||
BtnTopVfoBSelect.ClrBorder := TColor($00404040);
|
||||
BtnTopVfoBSelect.ClrText := CLR_TEXTDIM;
|
||||
BtnTopVfoBSelect.ClrTextAct := CLR_TEXT;
|
||||
BtnTopVfoBSelect.ClrNorm := T.VfoSelInaNorm;
|
||||
BtnTopVfoBSelect.ClrActive := T.VfoSelInaNorm;
|
||||
BtnTopVfoBSelect.ClrHot := T.VfoSelInaNorm;
|
||||
BtnTopVfoBSelect.ClrBorder := T.VfoSelInaBorder;
|
||||
BtnTopVfoBSelect.ClrText := T.VfoSelInaText;
|
||||
BtnTopVfoBSelect.ClrTextAct := T.VfoSelInaTextAct;
|
||||
BtnTopVfoBSelect.Active := False;
|
||||
BtnTopVfoBSelect.Invalidate;
|
||||
end;
|
||||
end else begin
|
||||
FreqDispA.ColorNormal := CLR_FREQ_DIM;
|
||||
FreqDispB.ColorNormal := CLR_FREQ;
|
||||
LblVfoALabel.Font.Color := CLR_TEXTDIM;
|
||||
LblVfoBLabel.Font.Color := CLR_FREQ;
|
||||
// Неактивный VFO-A: приглушённый
|
||||
FreqDispA.ColorNormal := T.FreqDim;
|
||||
FreqDispB.ColorNormal := T.FreqActive;
|
||||
LblVfoALabel.Font.Color := T.TextDim;
|
||||
LblVfoBLabel.Font.Color := T.FreqActive;
|
||||
if BtnTopVfoASelect <> nil then begin
|
||||
BtnTopVfoASelect.ClrNorm := TColor($001A1A1A);
|
||||
BtnTopVfoASelect.ClrActive := TColor($00282828);
|
||||
BtnTopVfoASelect.ClrHot := TColor($00252525);
|
||||
BtnTopVfoASelect.ClrBorder := TColor($00404040);
|
||||
BtnTopVfoASelect.ClrText := CLR_TEXTDIM;
|
||||
BtnTopVfoASelect.ClrTextAct := CLR_TEXT;
|
||||
BtnTopVfoASelect.ClrNorm := T.VfoSelInaNorm;
|
||||
BtnTopVfoASelect.ClrActive := T.VfoSelInaNorm;
|
||||
BtnTopVfoASelect.ClrHot := T.VfoSelInaNorm;
|
||||
BtnTopVfoASelect.ClrBorder := T.VfoSelInaBorder;
|
||||
BtnTopVfoASelect.ClrText := T.VfoSelInaText;
|
||||
BtnTopVfoASelect.ClrTextAct := T.VfoSelInaTextAct;
|
||||
BtnTopVfoASelect.Active := False;
|
||||
BtnTopVfoASelect.Invalidate;
|
||||
end;
|
||||
// Активный VFO-B: зелёный акцент
|
||||
if BtnTopVfoBSelect <> nil then begin
|
||||
BtnTopVfoBSelect.ClrNorm := TColor($00101C10);
|
||||
BtnTopVfoBSelect.ClrActive := TColor($00182818);
|
||||
BtnTopVfoBSelect.ClrHot := TColor($00182818);
|
||||
BtnTopVfoBSelect.ClrBorder := TColor($004A7A4A);
|
||||
BtnTopVfoBSelect.ClrText := TColor($0060B860);
|
||||
BtnTopVfoBSelect.ClrTextAct := TColor($0040FF60);
|
||||
BtnTopVfoBSelect.ClrNorm := T.VfoSelActNorm;
|
||||
BtnTopVfoBSelect.ClrActive := T.VfoSelActNorm;
|
||||
BtnTopVfoBSelect.ClrHot := T.VfoSelActNorm;
|
||||
BtnTopVfoBSelect.ClrBorder := T.VfoSelActBorder;
|
||||
BtnTopVfoBSelect.ClrText := T.VfoSelActText;
|
||||
BtnTopVfoBSelect.ClrTextAct := T.VfoSelActTextAct;
|
||||
BtnTopVfoBSelect.Active := True;
|
||||
BtnTopVfoBSelect.Invalidate;
|
||||
end;
|
||||
@@ -2314,22 +2394,22 @@ begin
|
||||
begin
|
||||
if not FSplitTxB then
|
||||
begin
|
||||
BtnTopVfoATX.ClrNorm := TColor($00000060);
|
||||
BtnTopVfoATX.ClrHot := TColor($00000090);
|
||||
BtnTopVfoATX.ClrActive := TColor($00000090);
|
||||
BtnTopVfoATX.ClrBorder := TColor($001010B0);
|
||||
BtnTopVfoATX.ClrText := TColor($00C8D8FF);
|
||||
BtnTopVfoATX.ClrTextAct := TColor($00E8F0FF);
|
||||
BtnTopVfoATX.ClrNorm := T.VfoTxActNorm;
|
||||
BtnTopVfoATX.ClrHot := T.VfoTxActNorm;
|
||||
BtnTopVfoATX.ClrActive := T.VfoTxActNorm;
|
||||
BtnTopVfoATX.ClrBorder := T.VfoTxActBorder;
|
||||
BtnTopVfoATX.ClrText := T.VfoTxActText;
|
||||
BtnTopVfoATX.ClrTextAct := T.VfoTxActText;
|
||||
BtnTopVfoATX.Active := True;
|
||||
end
|
||||
else
|
||||
begin
|
||||
BtnTopVfoATX.ClrNorm := TColor($00303030);
|
||||
BtnTopVfoATX.ClrHot := TColor($00404040);
|
||||
BtnTopVfoATX.ClrActive := TColor($00404040);
|
||||
BtnTopVfoATX.ClrBorder := TColor($00505050);
|
||||
BtnTopVfoATX.ClrText := CLR_TEXTDIM;
|
||||
BtnTopVfoATX.ClrTextAct := CLR_TEXT;
|
||||
BtnTopVfoATX.ClrNorm := T.VfoTxInaNorm;
|
||||
BtnTopVfoATX.ClrHot := T.VfoTxInaNorm;
|
||||
BtnTopVfoATX.ClrActive := T.VfoTxInaNorm;
|
||||
BtnTopVfoATX.ClrBorder := T.VfoTxInaBorder;
|
||||
BtnTopVfoATX.ClrText := T.VfoTxInaText;
|
||||
BtnTopVfoATX.ClrTextAct := T.VfoTxInaText;
|
||||
BtnTopVfoATX.Active := False;
|
||||
end;
|
||||
BtnTopVfoATX.Invalidate;
|
||||
@@ -2338,22 +2418,22 @@ begin
|
||||
begin
|
||||
if FSplitTxB then
|
||||
begin
|
||||
BtnTopVfoBTX.ClrNorm := TColor($00000060);
|
||||
BtnTopVfoBTX.ClrHot := TColor($00000090);
|
||||
BtnTopVfoBTX.ClrActive := TColor($00000090);
|
||||
BtnTopVfoBTX.ClrBorder := TColor($001010B0);
|
||||
BtnTopVfoBTX.ClrText := TColor($00C8D8FF);
|
||||
BtnTopVfoBTX.ClrTextAct := TColor($00E8F0FF);
|
||||
BtnTopVfoBTX.ClrNorm := T.VfoTxActNorm;
|
||||
BtnTopVfoBTX.ClrHot := T.VfoTxActNorm;
|
||||
BtnTopVfoBTX.ClrActive := T.VfoTxActNorm;
|
||||
BtnTopVfoBTX.ClrBorder := T.VfoTxActBorder;
|
||||
BtnTopVfoBTX.ClrText := T.VfoTxActText;
|
||||
BtnTopVfoBTX.ClrTextAct := T.VfoTxActText;
|
||||
BtnTopVfoBTX.Active := True;
|
||||
end
|
||||
else
|
||||
begin
|
||||
BtnTopVfoBTX.ClrNorm := TColor($00303030);
|
||||
BtnTopVfoBTX.ClrHot := TColor($00404040);
|
||||
BtnTopVfoBTX.ClrActive := TColor($00404040);
|
||||
BtnTopVfoBTX.ClrBorder := TColor($00505050);
|
||||
BtnTopVfoBTX.ClrText := CLR_TEXTDIM;
|
||||
BtnTopVfoBTX.ClrTextAct := CLR_TEXT;
|
||||
BtnTopVfoBTX.ClrNorm := T.VfoTxInaNorm;
|
||||
BtnTopVfoBTX.ClrHot := T.VfoTxInaNorm;
|
||||
BtnTopVfoBTX.ClrActive := T.VfoTxInaNorm;
|
||||
BtnTopVfoBTX.ClrBorder := T.VfoTxInaBorder;
|
||||
BtnTopVfoBTX.ClrText := T.VfoTxInaText;
|
||||
BtnTopVfoBTX.ClrTextAct := T.VfoTxInaText;
|
||||
BtnTopVfoBTX.Active := False;
|
||||
end;
|
||||
BtnTopVfoBTX.Invalidate;
|
||||
@@ -2907,6 +2987,8 @@ begin
|
||||
if not Assigned(FDeviceDialog) then
|
||||
FDeviceDialog := TDeviceDialog.Create(Self);
|
||||
FDeviceDialog.OnDiscover := BtnDiscoverFromDialog;
|
||||
if FLightTheme then FDeviceDialog.SetTheme(LightTheme)
|
||||
else FDeviceDialog.SetTheme(DarkTheme);
|
||||
FDeviceDialog.ShowModal;
|
||||
// Результат диалога не обрабатываем здесь — START сам спросит если нужно
|
||||
end;
|
||||
@@ -2986,6 +3068,8 @@ begin
|
||||
if not Assigned(FDeviceDialog) then
|
||||
FDeviceDialog := TDeviceDialog.Create(Self);
|
||||
FDeviceDialog.OnDiscover := BtnDiscoverFromDialog;
|
||||
if FLightTheme then FDeviceDialog.SetTheme(LightTheme)
|
||||
else FDeviceDialog.SetTheme(DarkTheme);
|
||||
if FDeviceDialog.ShowModal <> mrOk then Exit;
|
||||
if not FDeviceDialog.DialogResult.Accepted then Exit;
|
||||
FPendingIP := FDeviceDialog.DialogResult.IPAddress;
|
||||
@@ -5016,6 +5100,7 @@ begin
|
||||
SF.OnFPSChange := ApplyFPS;
|
||||
SF.OnPAChange := OnPASettingsChange;
|
||||
SF.OnCATChange := OnCATSettingsChange;
|
||||
SF.OnThemeChange := SetLightTheme;
|
||||
end;
|
||||
SF := TSettingsForm(FSettingsForm);
|
||||
SF.LoadPASettings(FPAMaxPower, FPABandCal);
|
||||
@@ -5032,6 +5117,7 @@ begin
|
||||
SF.RefreshAudioDevices(FAudioOut, FAudioIn);
|
||||
SF.LoadVisibility(FShowSpectrum, FShowWaterfall);
|
||||
SF.LoadFPS(FDisplayFPS);
|
||||
SF.LoadLightTheme(FLightTheme);
|
||||
// Загружаем текущие значения
|
||||
if FWDSPReady then
|
||||
SF.LoadValues(
|
||||
|
||||
@@ -72,6 +72,8 @@ type
|
||||
ShowWaterfall: Boolean; // True = draw waterfall on main form
|
||||
// --- Display FPS ---
|
||||
DisplayFPS: Integer; // spectrum/waterfall timer fps (1..100)
|
||||
// --- Theme ---
|
||||
LightTheme: Boolean; // False = dark (default), True = light
|
||||
// --- PA (Power Amplifier) settings ---
|
||||
PAMaxPower: Double; // максимальная выходная мощность, Вт (5..200)
|
||||
PABandCal: array[0..CFG_BAND_COUNT-1] of Double; // калибровка на диапазон 38.8..100.0
|
||||
@@ -128,6 +130,9 @@ type
|
||||
// CAT settings — не привязаны к MAC, хранятся в корне JSON (доступны до подключения)
|
||||
procedure SaveCATSettings(const G: TGlobalSettings);
|
||||
procedure LoadCATSettings(var G: TGlobalSettings);
|
||||
// Тема — глобальная настройка, не привязана к устройству
|
||||
procedure SaveTheme(ALightTheme: Boolean);
|
||||
function LoadTheme: Boolean;
|
||||
end;
|
||||
|
||||
implementation
|
||||
@@ -413,6 +418,7 @@ begin
|
||||
G.ShowSpectrum := JB(GObj,'show_spectrum',True);
|
||||
G.ShowWaterfall := JB(GObj,'show_waterfall',True);
|
||||
G.DisplayFPS := JI(GObj,'display_fps',60);
|
||||
G.LightTheme := JB(GObj,'light_theme',False);
|
||||
// PA settings
|
||||
G.PAMaxPower := JD(GObj,'pa_max_power',100.0);
|
||||
for i := 0 to CFG_BAND_COUNT-1 do
|
||||
@@ -489,6 +495,7 @@ begin
|
||||
JW(O,'show_spectrum',G.ShowSpectrum);
|
||||
JW(O,'show_waterfall',G.ShowWaterfall);
|
||||
JW(O,'display_fps',G.DisplayFPS);
|
||||
JW(O,'light_theme',G.LightTheme);
|
||||
// PA settings
|
||||
JW(O,'pa_max_power',G.PAMaxPower);
|
||||
for i := 0 to CFG_BAND_COUNT-1 do
|
||||
@@ -621,4 +628,20 @@ begin
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TSettingsManager.SaveTheme(ALightTheme: Boolean);
|
||||
var O: TJSONObject;
|
||||
begin
|
||||
O := EnsureObj(FRoot, 'preferences');
|
||||
JW(O, 'light_theme', ALightTheme);
|
||||
end;
|
||||
|
||||
function TSettingsManager.LoadTheme: Boolean;
|
||||
var O: TJSONObject;
|
||||
begin
|
||||
Result := False;
|
||||
if FRoot.Find('preferences') = nil then Exit;
|
||||
O := EnsureObj(FRoot, 'preferences');
|
||||
Result := JB(O, 'light_theme', False);
|
||||
end;
|
||||
|
||||
end.
|
||||
|
||||
+101
-14
@@ -27,7 +27,7 @@ uses
|
||||
Classes, SysUtils, Math,
|
||||
Forms, Controls, Graphics, Dialogs,
|
||||
StdCtrls, ExtCtrls, Spin,
|
||||
FlatButton, AudioOutput, AudioInput;
|
||||
FlatButton, AudioOutput, AudioInput, AppTheme;
|
||||
|
||||
type
|
||||
// PA: MaxPower в Вт, BandCal[0..10] — калибровка 38.8..100.0 на диапазон
|
||||
@@ -43,6 +43,7 @@ type
|
||||
TOnAudioInDevChange = procedure(DevIndex: Integer; const DevName: string) of object;
|
||||
TOnVisibilityChange = procedure(ShowSpectrum, ShowWaterfall: Boolean) of object;
|
||||
TOnFPSChange = procedure(FPS: Integer) of object;
|
||||
TOnThemeChange = procedure(LightTheme: Boolean) of object;
|
||||
TOnCATChange = procedure(
|
||||
const SerEnabled: array of Boolean;
|
||||
const SerPort: array of string;
|
||||
@@ -101,6 +102,7 @@ type
|
||||
FChkShowSpectrum: TCheckBox;
|
||||
FChkShowWaterfall: TCheckBox;
|
||||
FCmbFPS: TComboBox;
|
||||
FChkLightTheme: TCheckBox;
|
||||
|
||||
// ---- PA Settings tab controls ----
|
||||
FEdMaxPower: TSpinEdit;
|
||||
@@ -129,6 +131,7 @@ type
|
||||
FOnAudioInDevChange: TOnAudioInDevChange;
|
||||
FOnVisibilityChange: TOnVisibilityChange;
|
||||
FOnFPSChange: TOnFPSChange;
|
||||
FOnThemeChange: TOnThemeChange;
|
||||
|
||||
FLoading: Boolean;
|
||||
|
||||
@@ -138,7 +141,7 @@ type
|
||||
procedure BuildRX1Tab;
|
||||
procedure BuildPATab;
|
||||
procedure BuildCATTab;
|
||||
procedure ApplyTheme;
|
||||
procedure ApplyTheme(const T: TAppTheme);
|
||||
procedure NavClick(Sender: TObject);
|
||||
procedure SelectPage(APage: TWinControl; ANav: TFlatButton);
|
||||
|
||||
@@ -167,6 +170,7 @@ type
|
||||
procedure OnTXDevChange(Sender: TObject);
|
||||
procedure OnVisibilityChkChange(Sender: TObject);
|
||||
procedure OnFPSCmbChange(Sender: TObject);
|
||||
procedure OnLightThemeChkChange(Sender: TObject);
|
||||
procedure OnMaxPowerChange(Sender: TObject);
|
||||
procedure OnBandCalChange(Sender: TObject);
|
||||
procedure OnCATSerialChange(Sender: TObject);
|
||||
@@ -203,6 +207,7 @@ type
|
||||
procedure RefreshAudioDevices(AudioOut: TAudioOutput; AudioIn: TAudioInput);
|
||||
procedure LoadVisibility(ShowSpectrum, ShowWaterfall: Boolean);
|
||||
procedure LoadFPS(FPS: Integer);
|
||||
procedure LoadLightTheme(ALight: Boolean);
|
||||
procedure LoadPASettings(MaxPower: Double; const BandCal: array of Double);
|
||||
procedure LoadCATSettings(
|
||||
const SerEnabled: array of Boolean;
|
||||
@@ -219,6 +224,7 @@ type
|
||||
property OnAudioInDevChange: TOnAudioInDevChange read FOnAudioInDevChange write FOnAudioInDevChange;
|
||||
property OnVisibilityChange: TOnVisibilityChange read FOnVisibilityChange write FOnVisibilityChange;
|
||||
property OnFPSChange: TOnFPSChange read FOnFPSChange write FOnFPSChange;
|
||||
property OnThemeChange: TOnThemeChange read FOnThemeChange write FOnThemeChange;
|
||||
end;
|
||||
|
||||
implementation
|
||||
@@ -287,7 +293,7 @@ begin
|
||||
FLoading := False;
|
||||
FRXDevCount := 0;
|
||||
BuildUI;
|
||||
ApplyTheme;
|
||||
ApplyTheme(DarkTheme);
|
||||
end;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -611,6 +617,18 @@ begin
|
||||
for i := 0 to High(FPS_NAMES) do
|
||||
FCmbFPS.Items.Add(FPS_NAMES[i]);
|
||||
FCmbFPS.ItemIndex := 8;
|
||||
|
||||
Grp := MakeGroupPanel(FPageDisplay, 'Appearance', MARGIN, 292, 650, 68);
|
||||
|
||||
FChkLightTheme := TCheckBox.Create(Self);
|
||||
FChkLightTheme.Parent := Grp;
|
||||
FChkLightTheme.Caption := 'Light theme';
|
||||
FChkLightTheme.SetBounds(PAD, R1, CHKW, 22);
|
||||
FChkLightTheme.Checked := False;
|
||||
FChkLightTheme.Font.Color := CLR_TEXT;
|
||||
FChkLightTheme.Font.Name := UI_FONT;
|
||||
FChkLightTheme.Font.Size := 9;
|
||||
FChkLightTheme.OnChange := OnLightThemeChkChange;
|
||||
end;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -888,18 +906,66 @@ begin
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TSettingsForm.ApplyTheme;
|
||||
procedure TSettingsForm.ApplyTheme(const T: TAppTheme);
|
||||
|
||||
procedure StyleBtn(B: TFlatButton);
|
||||
begin
|
||||
Color := CLR_BG;
|
||||
if Assigned(FNavPanel) then FNavPanel.Color := TColor($00151515);
|
||||
if Assigned(FContentPanel) then FContentPanel.Color := CLR_BG;
|
||||
if Assigned(FPageAudio) then FPageAudio.Color := CLR_BG;
|
||||
if Assigned(FPageDisplay) then FPageDisplay.Color := CLR_BG;
|
||||
if Assigned(FPageSpectrum) then FPageSpectrum.Color := CLR_BG;
|
||||
if Assigned(FPageWaterfall) then FPageWaterfall.Color := CLR_BG;
|
||||
if Assigned(FPagePA) then FPagePA.Color := CLR_BG;
|
||||
if Assigned(FPageAdvanced) then FPageAdvanced.Color := CLR_BG;
|
||||
if Assigned(FPageCAT) then FPageCAT.Color := CLR_BG;
|
||||
B.ClrNorm := T.Panel;
|
||||
B.ClrBorder := T.Panel;
|
||||
B.ClrHot := T.BG;
|
||||
B.ClrActive := T.Panel;
|
||||
B.ClrText := T.Text;
|
||||
B.ClrTextAct := T.FreqActive;
|
||||
B.Invalidate;
|
||||
end;
|
||||
|
||||
procedure WalkControls(C: TWinControl);
|
||||
var i: Integer; Ctrl: TControl;
|
||||
begin
|
||||
for i := 0 to C.ControlCount - 1 do
|
||||
begin
|
||||
Ctrl := C.Controls[i];
|
||||
if Ctrl is TFlatButton then
|
||||
StyleBtn(TFlatButton(Ctrl))
|
||||
else if Ctrl is TScrollBox then
|
||||
begin
|
||||
TScrollBox(Ctrl).Color := T.BG;
|
||||
WalkControls(TScrollBox(Ctrl));
|
||||
end
|
||||
else if Ctrl is TPanel then
|
||||
begin
|
||||
if TPanel(Ctrl).Height = 1 then
|
||||
TPanel(Ctrl).Color := T.Border
|
||||
else
|
||||
TPanel(Ctrl).Color := T.Panel;
|
||||
WalkControls(TPanel(Ctrl));
|
||||
end
|
||||
else if Ctrl is TLabel then
|
||||
begin
|
||||
if Ctrl.Font.Size >= 12 then
|
||||
TLabel(Ctrl).Font.Color := T.Text
|
||||
else if fsBold in Ctrl.Font.Style then
|
||||
TLabel(Ctrl).Font.Color := T.FreqActive
|
||||
else
|
||||
TLabel(Ctrl).Font.Color := T.TextDim;
|
||||
end
|
||||
else if Ctrl is TCheckBox then
|
||||
TCheckBox(Ctrl).Font.Color := T.Text
|
||||
else if Ctrl is TListBox then
|
||||
begin
|
||||
TListBox(Ctrl).Color := T.BG;
|
||||
TListBox(Ctrl).Font.Color := T.Text;
|
||||
end
|
||||
else if Ctrl is TWinControl then
|
||||
WalkControls(TWinControl(Ctrl));
|
||||
end;
|
||||
end;
|
||||
|
||||
begin
|
||||
Color := T.BG;
|
||||
Font.Color := T.Text;
|
||||
WalkControls(Self);
|
||||
Invalidate;
|
||||
end;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -1265,6 +1331,15 @@ begin
|
||||
FOnVisibilityChange(FChkShowSpectrum.Checked, FChkShowWaterfall.Checked);
|
||||
end;
|
||||
|
||||
procedure TSettingsForm.OnLightThemeChkChange(Sender: TObject);
|
||||
begin
|
||||
if FLoading then Exit;
|
||||
if FChkLightTheme.Checked then ApplyTheme(LightTheme)
|
||||
else ApplyTheme(DarkTheme);
|
||||
if Assigned(FOnThemeChange) then
|
||||
FOnThemeChange(FChkLightTheme.Checked);
|
||||
end;
|
||||
|
||||
procedure TSettingsForm.OnFPSCmbChange(Sender: TObject);
|
||||
var
|
||||
Idx: Integer;
|
||||
@@ -1291,6 +1366,18 @@ begin
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TSettingsForm.LoadLightTheme(ALight: Boolean);
|
||||
begin
|
||||
FLoading := True;
|
||||
try
|
||||
FChkLightTheme.Checked := ALight;
|
||||
finally
|
||||
FLoading := False;
|
||||
end;
|
||||
if ALight then ApplyTheme(LightTheme)
|
||||
else ApplyTheme(DarkTheme);
|
||||
end;
|
||||
|
||||
procedure TSettingsForm.LoadPASettings(MaxPower: Double;
|
||||
const BandCal: array of Double);
|
||||
var i, n: Integer;
|
||||
|
||||
+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