mirror of
https://git.vladimir.cc/vladimir/ewsdr.git
synced 2026-08-25 19:45:09 +00:00
1039 lines
34 KiB
ObjectPascal
1039 lines
34 KiB
ObjectPascal
unit SettingsForm;
|
|
|
|
{
|
|
SettingsForm.pas — диалог настроек EWSDR SDR приложения.
|
|
|
|
Вкладки: General | Audio | Display | DSP | Transmit | PA Settings | CAT
|
|
Display имеет подвкладки: General | RX1 | RX2 | TX
|
|
|
|
Все изменения применяются МГНОВЕННО через events:
|
|
OnDisplayChange — изменены FFT/Window/Detector/Avg параметры
|
|
OnWaterfallChange — изменены параметры водопада
|
|
OnGridChange — изменена сетка спектра
|
|
OnAudioChange — изменено аудио устройство/буфер
|
|
OnSampleRateChange — изменён audio output sample rate (только PortAudio, DDC не трогается)
|
|
|
|
Для применения настроек: создайте форму, зарегистрируйте callbacks,
|
|
потом вызовите Show или ShowModal.
|
|
}
|
|
|
|
{$IFDEF FPC}
|
|
{$MODE Delphi}
|
|
{$ENDIF}
|
|
|
|
interface
|
|
|
|
uses
|
|
Classes, SysUtils, Math,
|
|
Forms, Controls, Graphics, Dialogs,
|
|
StdCtrls, ExtCtrls, ComCtrls, Spin,
|
|
FlatButton, AudioOutput, AudioInput;
|
|
|
|
type
|
|
TOnDisplayParamChange = procedure(FFTSize, WindowType, SpecDetector,
|
|
SpecAvgMode, SpecAvgCount: Integer; SpecBackmult: Double) of object;
|
|
TOnWaterfallParamChange = procedure(WfDetector, WfAvgMode, WfAvgCount: Integer;
|
|
WfBackmult: Double) of object;
|
|
TOnGridParamChange = procedure(RefLevel, Range, GridStep: Double) of object;
|
|
TOnAudioDevChange = procedure(DevIndex: Integer; const DevName: string) of object;
|
|
TOnAudioInDevChange = procedure(DevIndex: Integer; const DevName: string) of object;
|
|
TOnVisibilityChange = procedure(ShowSpectrum, ShowWaterfall: Boolean) of object;
|
|
TOnFPSChange = procedure(FPS: Integer) of object;
|
|
|
|
{ TSettingsForm }
|
|
TSettingsForm = class(TForm)
|
|
private
|
|
// ---- Tabs ----
|
|
FPageCtrl: TPageControl;
|
|
FTabGeneral: TTabSheet;
|
|
FTabAudio: TTabSheet;
|
|
FTabDisplay: TTabSheet;
|
|
FTabDSP: TTabSheet;
|
|
FTabTransmit: TTabSheet;
|
|
FTabPA: TTabSheet;
|
|
FTabCAT: TTabSheet;
|
|
|
|
// ---- Audio tab controls ----
|
|
FCmbRXDev: TComboBox;
|
|
FRXDevIndices: array[0..63] of Integer;
|
|
FRXDevCount: Integer;
|
|
FCmbTXDev: TComboBox;
|
|
FTXDevIndices: array[0..63] of Integer;
|
|
FTXDevCount: Integer;
|
|
|
|
// ---- Display sub-tabs ----
|
|
FDispPageCtrl: TPageControl;
|
|
FTabDispGeneral: TTabSheet;
|
|
FTabDispRX1: TTabSheet;
|
|
FTabDispRX2: TTabSheet;
|
|
FTabDispTX: TTabSheet;
|
|
|
|
// ---- RX1 sub-tab controls ----
|
|
// FFT group
|
|
FCmbFFTSize: TComboBox;
|
|
FCmbWindow: TComboBox;
|
|
// Spectrum group
|
|
FCmbSpecDet: TComboBox;
|
|
FCmbSpecAvg: TComboBox;
|
|
FSpinSpecCount: TSpinEdit;
|
|
FEdSpecBackmult: TEdit;
|
|
// Waterfall group
|
|
FCmbWfDet: TComboBox;
|
|
FCmbWfAvg: TComboBox;
|
|
FSpinWfCount: TSpinEdit;
|
|
FEdWfBackmult: TEdit;
|
|
// Grid group
|
|
FEdRefLevel: TEdit;
|
|
FEdRange: TEdit;
|
|
FCmbGridStep: TComboBox;
|
|
|
|
// ---- General display tab controls ----
|
|
FChkShowSpectrum: TCheckBox;
|
|
FChkShowWaterfall: TCheckBox;
|
|
FCmbFPS: TComboBox;
|
|
|
|
// ---- Close button ----
|
|
FBtnClose: TFlatButton;
|
|
|
|
// ---- Callbacks ----
|
|
FOnDisplayChange: TOnDisplayParamChange;
|
|
FOnWaterfallChange: TOnWaterfallParamChange;
|
|
FOnGridChange: TOnGridParamChange;
|
|
FOnAudioDevChange: TOnAudioDevChange;
|
|
FOnAudioInDevChange: TOnAudioInDevChange;
|
|
FOnVisibilityChange: TOnVisibilityChange;
|
|
FOnFPSChange: TOnFPSChange;
|
|
|
|
// ---- Suppress change events during load ----
|
|
FLoading: Boolean;
|
|
|
|
procedure BuildUI;
|
|
procedure BuildAudioTab;
|
|
procedure BuildDisplayTab;
|
|
procedure BuildRX1Tab;
|
|
procedure ApplyTheme;
|
|
|
|
// Helpers
|
|
function MakeLbl(AParent: TWinControl; const Cap: string;
|
|
ALeft, ATop, AW: Integer): TLabel;
|
|
function MakeCombo(AParent: TWinControl; ALeft, ATop, AW: Integer;
|
|
OnChange: TNotifyEvent): TComboBox;
|
|
function MakeGroupBox(AParent: TWinControl; const Cap: string;
|
|
ALeft, ATop, AW, AH: Integer): TGroupBox;
|
|
|
|
// Event handlers
|
|
procedure OnFFTSizeChange(Sender: TObject);
|
|
procedure OnWindowChange(Sender: TObject);
|
|
procedure OnSpecDetChange(Sender: TObject);
|
|
procedure OnSpecAvgModeChange(Sender: TObject);
|
|
procedure OnSpecCountChange(Sender: TObject);
|
|
procedure OnSpecBackmultChange(Sender: TObject);
|
|
procedure OnWfDetChange(Sender: TObject);
|
|
procedure OnWfAvgModeChange(Sender: TObject);
|
|
procedure OnWfCountChange(Sender: TObject);
|
|
procedure OnWfBackmultChange(Sender: TObject);
|
|
procedure OnRefLevelChange(Sender: TObject);
|
|
procedure OnRangeChange(Sender: TObject);
|
|
procedure OnGridStepChange(Sender: TObject);
|
|
procedure OnRXDevChange(Sender: TObject);
|
|
procedure OnTXDevChange(Sender: TObject);
|
|
procedure OnVisibilityChkChange(Sender: TObject);
|
|
procedure OnFPSCmbChange(Sender: TObject);
|
|
procedure BtnCloseClick(Sender: TObject);
|
|
|
|
procedure FireDisplayChange;
|
|
procedure FireWaterfallChange;
|
|
procedure FireGridChange;
|
|
|
|
function GetSpecBackmult: Double;
|
|
function GetWfBackmult: Double;
|
|
function GetRefLevel: Double;
|
|
function GetRange: Double;
|
|
function GetGridStep: Double;
|
|
function GetFFTSize: Integer;
|
|
|
|
public
|
|
constructor Create(AOwner: TComponent); override;
|
|
|
|
// Заполнить элементы управления текущими значениями (без firing events)
|
|
procedure LoadValues(
|
|
FFTSize, WinType, SpecDet, SpecAvgMode, SpecAvgCount: Integer;
|
|
SpecBackmult: Double;
|
|
WfDet, WfAvgMode, WfAvgCount: Integer;
|
|
WfBackmult: Double;
|
|
RefLevel, Range, GridStep: Double;
|
|
const AudioOutDevName: string;
|
|
const AudioInDevName: string);
|
|
|
|
// Перечислить PA устройства ввода и вывода
|
|
procedure RefreshAudioDevices(AudioOut: TAudioOutput; AudioIn: TAudioInput);
|
|
// Загрузить текущие значения видимости (без fire событий)
|
|
procedure LoadVisibility(ShowSpectrum, ShowWaterfall: Boolean);
|
|
// Загрузить текущий FPS (без fire события)
|
|
procedure LoadFPS(FPS: Integer);
|
|
|
|
property OnDisplayChange: TOnDisplayParamChange read FOnDisplayChange write FOnDisplayChange;
|
|
property OnWaterfallChange: TOnWaterfallParamChange read FOnWaterfallChange write FOnWaterfallChange;
|
|
property OnGridChange: TOnGridParamChange read FOnGridChange write FOnGridChange;
|
|
property OnAudioDevChange: TOnAudioDevChange read FOnAudioDevChange write FOnAudioDevChange;
|
|
property OnAudioInDevChange: TOnAudioInDevChange read FOnAudioInDevChange write FOnAudioInDevChange;
|
|
property OnVisibilityChange: TOnVisibilityChange read FOnVisibilityChange write FOnVisibilityChange;
|
|
property OnFPSChange: TOnFPSChange read FOnFPSChange write FOnFPSChange;
|
|
end;
|
|
|
|
implementation
|
|
|
|
const
|
|
CLR_BG = TColor($00121212);
|
|
CLR_PANEL = TColor($001A1A1A);
|
|
CLR_GROUP = TColor($00202020);
|
|
CLR_TEXT = TColor($00E0E0E0);
|
|
CLR_TEXTDIM = TColor($00888888);
|
|
CLR_BORDER = TColor($00303030);
|
|
CLR_ACCENT = TColor($0040FF80);
|
|
BTN_H = 24;
|
|
|
|
// FFT sizes (index → value)
|
|
FFT_SIZES: array[0..5] of Integer = (1024, 2048, 4096, 8192, 16384, 32768);
|
|
FFT_NAMES: array[0..5] of string = ('1024','2048','4096','8192','16384','32768');
|
|
|
|
WINDOW_NAMES: array[0..6] of string = (
|
|
'Rectangular', '4-term Blackman-Harris', 'Hann',
|
|
'Flat-top', 'Hamming', 'Kaiser', '7-term Blackman-Harris');
|
|
|
|
DETECTOR_NAMES: array[0..4] of string = (
|
|
'Peak', 'Rosenfell', 'Average', 'Sample', 'RMS');
|
|
|
|
AVGMODE_NAMES: array[0..4] of string = (
|
|
'Peak Hold', 'None', 'Recursive', 'Time Window', 'Log Recursive');
|
|
// Note: AVERAGE_PEAK_HOLD=-1, NONE=0, RECURSIVE=1, TIME_WINDOW=2, LOG_RECURSIVE=3
|
|
// Index → WDSP value mapping: 0→-1, 1→0, 2→1, 3→2, 4→3
|
|
AVGMODE_VALUES: array[0..4] of Integer = (-1, 0, 1, 2, 3);
|
|
|
|
GRID_STEPS: array[0..4] of Double = (5.0, 10.0, 20.0, 30.0, 40.0);
|
|
GRID_STEP_NAMES: array[0..4] of string = ('5 dB','10 dB','20 dB','30 dB','40 dB');
|
|
|
|
FPS_VALUES: array[0..5] of Integer = (5, 10, 15, 20, 25, 30);
|
|
FPS_NAMES: array[0..5] of string = ('5 fps','10 fps','15 fps','20 fps','25 fps','30 fps');
|
|
|
|
{ TSettingsForm }
|
|
|
|
constructor TSettingsForm.Create(AOwner: TComponent);
|
|
begin
|
|
inherited CreateNew(AOwner);
|
|
Caption := 'Settings';
|
|
Width := 700;
|
|
Height := 560;
|
|
Position := poScreenCenter;
|
|
BorderStyle := bsSizeable;
|
|
Color := CLR_BG;
|
|
Font.Name := 'Courier New';
|
|
Font.Size := 8;
|
|
Font.Color := CLR_TEXT;
|
|
FLoading := False;
|
|
FRXDevCount := 0;
|
|
BuildUI;
|
|
ApplyTheme;
|
|
end;
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Helpers
|
|
// ---------------------------------------------------------------------------
|
|
|
|
function TSettingsForm.MakeLbl(AParent: TWinControl; const Cap: string;
|
|
ALeft, ATop, AW: Integer): TLabel;
|
|
begin
|
|
Result := TLabel.Create(Self);
|
|
Result.Parent := AParent;
|
|
Result.Caption := Cap;
|
|
Result.Left := ALeft;
|
|
Result.Top := ATop;
|
|
if AW > 0 then Result.Width := AW;
|
|
Result.Font.Color := CLR_TEXTDIM;
|
|
Result.Font.Name := 'Courier New';
|
|
Result.Font.Size := 8;
|
|
end;
|
|
|
|
function TSettingsForm.MakeCombo(AParent: TWinControl; ALeft, ATop, AW: Integer;
|
|
OnChange: TNotifyEvent): TComboBox;
|
|
begin
|
|
Result := TComboBox.Create(Self);
|
|
Result.Parent := AParent;
|
|
Result.Left := ALeft;
|
|
Result.Top := ATop;
|
|
Result.Width := AW;
|
|
Result.Height := BTN_H;
|
|
Result.Style := csDropDownList;
|
|
Result.Color := CLR_PANEL;
|
|
Result.Font.Color := CLR_TEXT;
|
|
Result.Font.Name := 'Courier New';
|
|
Result.Font.Size := 8;
|
|
Result.OnChange := OnChange;
|
|
end;
|
|
|
|
function TSettingsForm.MakeGroupBox(AParent: TWinControl; const Cap: string;
|
|
ALeft, ATop, AW, AH: Integer): TGroupBox;
|
|
begin
|
|
Result := TGroupBox.Create(Self);
|
|
Result.Parent := AParent;
|
|
Result.Caption := Cap;
|
|
Result.SetBounds(ALeft, ATop, AW, AH);
|
|
Result.Color := CLR_GROUP;
|
|
Result.Font.Color := CLR_ACCENT;
|
|
Result.Font.Name := 'Courier New';
|
|
Result.Font.Size := 8;
|
|
end;
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// BuildUI
|
|
// ---------------------------------------------------------------------------
|
|
|
|
procedure TSettingsForm.BuildUI;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
FPageCtrl := TPageControl.Create(Self);
|
|
FPageCtrl.Parent := Self;
|
|
FPageCtrl.SetBounds(4, 4, Width - 12, Height - 48);
|
|
FPageCtrl.Anchors := [akLeft, akTop, akRight, akBottom];
|
|
FPageCtrl.Color := CLR_BG;
|
|
FPageCtrl.Font.Color := CLR_TEXT;
|
|
|
|
FTabGeneral := FPageCtrl.AddTabSheet; FTabGeneral.Caption := 'General';
|
|
FTabAudio := FPageCtrl.AddTabSheet; FTabAudio.Caption := 'Audio';
|
|
FTabDisplay := FPageCtrl.AddTabSheet; FTabDisplay.Caption := 'Display';
|
|
FTabDSP := FPageCtrl.AddTabSheet; FTabDSP.Caption := 'DSP';
|
|
FTabTransmit := FPageCtrl.AddTabSheet; FTabTransmit.Caption := 'Transmit';
|
|
FTabPA := FPageCtrl.AddTabSheet; FTabPA.Caption := 'PA Settings';
|
|
FTabCAT := FPageCtrl.AddTabSheet; FTabCAT.Caption := 'CAT';
|
|
|
|
for i := 0 to FPageCtrl.PageCount - 1 do
|
|
FPageCtrl.Pages[i].Color := CLR_BG;
|
|
|
|
BuildAudioTab;
|
|
BuildDisplayTab;
|
|
|
|
// Close button
|
|
FBtnClose := TFlatButton.Create(Self);
|
|
FBtnClose.Parent := Self;
|
|
FBtnClose.Caption := 'CLOSE';
|
|
FBtnClose.SetBounds(Width - 90, Height - 38, 82, BTN_H);
|
|
FBtnClose.Anchors := [akRight, akBottom];
|
|
FBtnClose.OnClick := BtnCloseClick;
|
|
FBtnClose.ClrNorm := CLR_PANEL;
|
|
FBtnClose.ClrBorder := TColor($00404040);
|
|
FBtnClose.ClrHot := TColor($00303030);
|
|
FBtnClose.ClrActive := TColor($00003300);
|
|
FBtnClose.ClrText := CLR_TEXT;
|
|
FBtnClose.ClrTextAct:= CLR_ACCENT;
|
|
end;
|
|
|
|
procedure TSettingsForm.BuildAudioTab;
|
|
const
|
|
R1 = 10; // top padding inside groups
|
|
LW = 110; // label width
|
|
var
|
|
Grp: TGroupBox;
|
|
|
|
procedure MkGrp(out G: TGroupBox; const Cap: string);
|
|
begin
|
|
G := TGroupBox.Create(Self);
|
|
G.Parent := FTabAudio;
|
|
G.Caption := Cap;
|
|
G.Color := CLR_GROUP;
|
|
G.Font.Color := CLR_ACCENT;
|
|
G.Font.Name := 'Courier New';
|
|
G.Font.Size := 8;
|
|
G.AutoSize := True;
|
|
G.Align := alTop;
|
|
end;
|
|
|
|
procedure MkSep;
|
|
var S: TPanel;
|
|
begin
|
|
S := TPanel.Create(Self);
|
|
S.Parent := FTabAudio;
|
|
S.BevelOuter := bvNone;
|
|
S.Color := CLR_BG;
|
|
S.Align := alTop;
|
|
S.Height := 12;
|
|
end;
|
|
|
|
begin
|
|
// ── Top padding ───────────────────────────────────────────────────────────
|
|
MkSep;
|
|
|
|
// ── TX Audio Input ────────────────────────────────────────────────────────
|
|
MkGrp(Grp, 'TX Audio Input');
|
|
MakeLbl(Grp, 'Input Device:', 10, R1+4, LW);
|
|
FCmbTXDev := MakeCombo(Grp, LW+14, R1, 440, OnTXDevChange);
|
|
FCmbTXDev.Items.Add('None');
|
|
|
|
MkSep;
|
|
|
|
// ── RX Audio Output ───────────────────────────────────────────────────────
|
|
MkGrp(Grp, 'RX Audio Output');
|
|
MakeLbl(Grp, 'Output Device:', 10, R1+4, LW);
|
|
FCmbRXDev := MakeCombo(Grp, LW+14, R1, 440, OnRXDevChange);
|
|
FCmbRXDev.Items.Add('Default');
|
|
end;
|
|
|
|
procedure TSettingsForm.BuildDisplayTab;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
FDispPageCtrl := TPageControl.Create(Self);
|
|
FDispPageCtrl.Parent := FTabDisplay;
|
|
FDispPageCtrl.Align := alClient;
|
|
FDispPageCtrl.Color := CLR_BG;
|
|
FDispPageCtrl.Font.Color := CLR_TEXT;
|
|
|
|
FTabDispGeneral := FDispPageCtrl.AddTabSheet; FTabDispGeneral.Caption := 'General';
|
|
FTabDispRX1 := FDispPageCtrl.AddTabSheet; FTabDispRX1.Caption := 'RX1';
|
|
FTabDispRX2 := FDispPageCtrl.AddTabSheet; FTabDispRX2.Caption := 'RX2';
|
|
FTabDispTX := FDispPageCtrl.AddTabSheet; FTabDispTX.Caption := 'TX';
|
|
|
|
FTabDispGeneral.Color := CLR_BG;
|
|
FTabDispRX1.Color := CLR_BG;
|
|
FTabDispRX2.Color := CLR_BG;
|
|
FTabDispTX.Color := CLR_BG;
|
|
|
|
// ---- General sub-tab: visibility checkboxes ----
|
|
FChkShowSpectrum := TCheckBox.Create(Self);
|
|
FChkShowSpectrum.Parent := FTabDispGeneral;
|
|
FChkShowSpectrum.Caption := 'Show Spectrum';
|
|
FChkShowSpectrum.SetBounds(12, 16, 200, 22);
|
|
FChkShowSpectrum.Checked := True;
|
|
FChkShowSpectrum.Font.Color := CLR_TEXT;
|
|
FChkShowSpectrum.Font.Name := 'Courier New';
|
|
FChkShowSpectrum.Font.Size := 8;
|
|
FChkShowSpectrum.OnChange := OnVisibilityChkChange;
|
|
|
|
FChkShowWaterfall := TCheckBox.Create(Self);
|
|
FChkShowWaterfall.Parent := FTabDispGeneral;
|
|
FChkShowWaterfall.Caption := 'Show Waterfall';
|
|
FChkShowWaterfall.SetBounds(12, 44, 200, 22);
|
|
FChkShowWaterfall.Checked := True;
|
|
FChkShowWaterfall.Font.Color := CLR_TEXT;
|
|
FChkShowWaterfall.Font.Name := 'Courier New';
|
|
FChkShowWaterfall.Font.Size := 8;
|
|
FChkShowWaterfall.OnChange := OnVisibilityChkChange;
|
|
|
|
// FPS label + combo
|
|
with TLabel.Create(Self) do
|
|
begin
|
|
Parent := FTabDispGeneral;
|
|
Caption := 'Display FPS:';
|
|
SetBounds(12, 76, 100, 22);
|
|
Font.Color := CLR_TEXTDIM;
|
|
Font.Name := 'Courier New';
|
|
Font.Size := 8;
|
|
end;
|
|
FCmbFPS := MakeCombo(FTabDispGeneral, 116, 74, 100, OnFPSCmbChange);
|
|
for i := 0 to High(FPS_NAMES) do
|
|
FCmbFPS.Items.Add(FPS_NAMES[i]);
|
|
FCmbFPS.ItemIndex := 3; // 20 fps default
|
|
|
|
BuildRX1Tab;
|
|
end;
|
|
|
|
procedure TSettingsForm.BuildRX1Tab;
|
|
// Layout (alTop stack in FTabDispRX1):
|
|
// 8px top padding
|
|
// FFT group — full width
|
|
// 12px separator
|
|
// PnlMid — Spectrum (alLeft) | 10px gap | Waterfall (alClient), same height
|
|
// 12px separator
|
|
// Spectrum Grid group — full width
|
|
// hint (alBottom)
|
|
//
|
|
// Row step: STEP = BTN_H + 10 = 34px. Inner padding: R1 = 10.
|
|
const
|
|
R1 = 10; // top padding inside groups
|
|
LX = 10; // left margin inside group
|
|
LW = 88; // label column width
|
|
CX = LX + LW + 4; // combo/edit column start (= 102)
|
|
STEP = BTN_H + 10; // row step (= 34)
|
|
SEP = 12; // gap between groups
|
|
COLW = 328; // left column panel width
|
|
COLGAP = 10; // horizontal gap between columns
|
|
GH2 = 100; // GroupBox height for 2-row groups
|
|
GH3 = 136; // GroupBox height for 3-row groups
|
|
var
|
|
PnlFFT, PnlMid, PnlL, PnlSepH, PnlR: TPanel;
|
|
Grp: TGroupBox;
|
|
Lbl: TLabel;
|
|
i: Integer;
|
|
|
|
function MkGrp(AParent: TWinControl; const Cap: string; AH: Integer): TGroupBox;
|
|
begin
|
|
Result := TGroupBox.Create(Self);
|
|
Result.Parent := AParent;
|
|
Result.Caption := Cap;
|
|
Result.Color := CLR_GROUP;
|
|
Result.Font.Color := CLR_ACCENT;
|
|
Result.Font.Name := 'Courier New';
|
|
Result.Font.Size := 8;
|
|
Result.Align := alTop;
|
|
Result.Height := AH;
|
|
end;
|
|
|
|
procedure MkSep(AParent: TWinControl; AH: Integer);
|
|
var S: TPanel;
|
|
begin
|
|
S := TPanel.Create(Self);
|
|
S.Parent := AParent;
|
|
S.BevelOuter := bvNone;
|
|
S.Color := CLR_BG;
|
|
S.Align := alTop;
|
|
S.Height := AH;
|
|
end;
|
|
|
|
function MkEdit(AParent: TWinControl; AX, AY, AW: Integer;
|
|
AOnExit: TNotifyEvent): TEdit;
|
|
begin
|
|
Result := TEdit.Create(Self);
|
|
Result.Parent := AParent;
|
|
Result.SetBounds(AX, AY, AW, BTN_H);
|
|
Result.Color := CLR_PANEL;
|
|
Result.Font.Color := CLR_TEXT;
|
|
Result.Font.Name := 'Courier New';
|
|
Result.Font.Size := 8;
|
|
Result.OnExit := AOnExit;
|
|
end;
|
|
|
|
function MkSpin(AParent: TWinControl; AX, AY, AW: Integer;
|
|
AOnChange: TNotifyEvent): TSpinEdit;
|
|
begin
|
|
Result := TSpinEdit.Create(Self);
|
|
Result.Parent := AParent;
|
|
Result.SetBounds(AX, AY, AW, BTN_H);
|
|
Result.MinValue := 1;
|
|
Result.MaxValue := 256;
|
|
Result.Value := 2;
|
|
Result.Color := CLR_PANEL;
|
|
Result.Font.Color := CLR_TEXT;
|
|
Result.Font.Name := 'Courier New';
|
|
Result.Font.Size := 8;
|
|
Result.OnChange := AOnChange;
|
|
end;
|
|
|
|
begin
|
|
// Hint at bottom (added first so alBottom is honoured by later alTop panels)
|
|
Lbl := MakeLbl(FTabDispRX1,
|
|
'Backmult: higher value = slower decay (range 0.0 .. 1.0)', LX, 0, 0);
|
|
Lbl.Align := alBottom;
|
|
Lbl.Height := 22;
|
|
Lbl.Font.Color := CLR_TEXTDIM;
|
|
|
|
// ── Top padding ───────────────────────────────────────────────────────────
|
|
MkSep(FTabDispRX1, 8);
|
|
|
|
// ── FFT (full width, alTop) ───────────────────────────────────────────────
|
|
PnlFFT := TPanel.Create(Self);
|
|
PnlFFT.Parent := FTabDispRX1;
|
|
PnlFFT.BevelOuter := bvNone;
|
|
PnlFFT.Color := CLR_BG;
|
|
PnlFFT.Align := alTop;
|
|
PnlFFT.Height := GH2;
|
|
Grp := MkGrp(PnlFFT, 'FFT', GH2);
|
|
MakeLbl(Grp, 'FFT Size:', LX, R1+4, LW);
|
|
FCmbFFTSize := MakeCombo(Grp, CX, R1, 130, OnFFTSizeChange);
|
|
for i := 0 to High(FFT_NAMES) do FCmbFFTSize.Items.Add(FFT_NAMES[i]);
|
|
MakeLbl(Grp, 'Window:', LX, R1+STEP+4, LW);
|
|
FCmbWindow := MakeCombo(Grp, CX, R1+STEP, 250, OnWindowChange);
|
|
for i := 0 to High(WINDOW_NAMES) do FCmbWindow.Items.Add(WINDOW_NAMES[i]);
|
|
|
|
// ── Separator ─────────────────────────────────────────────────────────────
|
|
MkSep(FTabDispRX1, SEP);
|
|
|
|
// ── Middle row: Spectrum (left) | Waterfall (right) at the same level ─────
|
|
PnlMid := TPanel.Create(Self);
|
|
PnlMid.Parent := FTabDispRX1;
|
|
PnlMid.BevelOuter := bvNone;
|
|
PnlMid.Color := CLR_BG;
|
|
PnlMid.Align := alTop;
|
|
PnlMid.Height := GH3;
|
|
|
|
PnlL := TPanel.Create(Self);
|
|
PnlL.Parent := PnlMid;
|
|
PnlL.BevelOuter := bvNone;
|
|
PnlL.Color := CLR_BG;
|
|
PnlL.Align := alLeft;
|
|
PnlL.Width := COLW;
|
|
|
|
PnlSepH := TPanel.Create(Self);
|
|
PnlSepH.Parent := PnlMid;
|
|
PnlSepH.BevelOuter := bvNone;
|
|
PnlSepH.Color := CLR_BG;
|
|
PnlSepH.Align := alLeft;
|
|
PnlSepH.Width := COLGAP;
|
|
|
|
PnlR := TPanel.Create(Self);
|
|
PnlR.Parent := PnlMid;
|
|
PnlR.BevelOuter := bvNone;
|
|
PnlR.Color := CLR_BG;
|
|
PnlR.Align := alClient;
|
|
|
|
// ── Spectrum (left column) ────────────────────────────────────────────────
|
|
Grp := MkGrp(PnlL, 'Spectrum', GH3);
|
|
MakeLbl(Grp, 'Detector:', LX, R1+4, LW);
|
|
FCmbSpecDet := MakeCombo(Grp, CX, R1, 172, OnSpecDetChange);
|
|
for i := 0 to High(DETECTOR_NAMES) do FCmbSpecDet.Items.Add(DETECTOR_NAMES[i]);
|
|
MakeLbl(Grp, 'Avg Mode:', LX, R1+STEP+4, LW);
|
|
FCmbSpecAvg := MakeCombo(Grp, CX, R1+STEP, 172, OnSpecAvgModeChange);
|
|
for i := 0 to High(AVGMODE_NAMES) do FCmbSpecAvg.Items.Add(AVGMODE_NAMES[i]);
|
|
MakeLbl(Grp, 'Avg Count:', LX, R1+2*STEP+4, LW);
|
|
FSpinSpecCount := MkSpin(Grp, CX, R1+2*STEP, 72, OnSpecCountChange);
|
|
MakeLbl(Grp, 'Backmult:', CX+78, R1+2*STEP+4, 64);
|
|
FEdSpecBackmult := MkEdit(Grp, CX+78+68, R1+2*STEP, 74, OnSpecBackmultChange);
|
|
FEdSpecBackmult.Text := '0.45';
|
|
|
|
// ── Waterfall (right column) ──────────────────────────────────────────────
|
|
Grp := MkGrp(PnlR, 'Waterfall', GH3);
|
|
MakeLbl(Grp, 'Detector:', LX, R1+4, LW);
|
|
FCmbWfDet := MakeCombo(Grp, CX, R1, 172, OnWfDetChange);
|
|
for i := 0 to High(DETECTOR_NAMES) do FCmbWfDet.Items.Add(DETECTOR_NAMES[i]);
|
|
MakeLbl(Grp, 'Avg Mode:', LX, R1+STEP+4, LW);
|
|
FCmbWfAvg := MakeCombo(Grp, CX, R1+STEP, 172, OnWfAvgModeChange);
|
|
for i := 0 to High(AVGMODE_NAMES) do FCmbWfAvg.Items.Add(AVGMODE_NAMES[i]);
|
|
MakeLbl(Grp, 'Avg Count:', LX, R1+2*STEP+4, LW);
|
|
FSpinWfCount := MkSpin(Grp, CX, R1+2*STEP, 72, OnWfCountChange);
|
|
MakeLbl(Grp, 'Backmult:', CX+78, R1+2*STEP+4, 64);
|
|
FEdWfBackmult := MkEdit(Grp, CX+78+68, R1+2*STEP, 74, OnWfBackmultChange);
|
|
FEdWfBackmult.Text := '0.45';
|
|
|
|
// ── Separator ─────────────────────────────────────────────────────────────
|
|
MkSep(FTabDispRX1, SEP);
|
|
|
|
// ── Spectrum Grid (full width, alTop) ─────────────────────────────────────
|
|
Grp := MkGrp(FTabDispRX1, 'Spectrum Grid', GH2);
|
|
MakeLbl(Grp, 'Ref Level (dBm):', LX, R1+4, 116);
|
|
FEdRefLevel := MkEdit(Grp, LX+120, R1, 80, OnRefLevelChange);
|
|
FEdRefLevel.Text := '-20';
|
|
MakeLbl(Grp, 'Range (dB):', LX, R1+STEP+4, LW);
|
|
FEdRange := MkEdit(Grp, CX, R1+STEP, 80, OnRangeChange);
|
|
FEdRange.Text := '110';
|
|
MakeLbl(Grp, 'Grid Step:', CX+90, R1+STEP+4, 72);
|
|
FCmbGridStep := MakeCombo(Grp, CX+90+76, R1+STEP, 80, OnGridStepChange);
|
|
for i := 0 to High(GRID_STEP_NAMES) do FCmbGridStep.Items.Add(GRID_STEP_NAMES[i]);
|
|
FCmbGridStep.ItemIndex := 1;
|
|
end;
|
|
|
|
procedure TSettingsForm.ApplyTheme;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
Color := CLR_BG;
|
|
for i := 0 to FPageCtrl.PageCount - 1 do
|
|
FPageCtrl.Pages[i].Color := CLR_BG;
|
|
FDispPageCtrl.Color := CLR_BG;
|
|
for i := 0 to FDispPageCtrl.PageCount - 1 do
|
|
FDispPageCtrl.Pages[i].Color := CLR_BG;
|
|
end;
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// LoadValues — заполняет элементы без fire событий
|
|
// ---------------------------------------------------------------------------
|
|
|
|
procedure TSettingsForm.LoadValues(
|
|
FFTSize, WinType, SpecDet, SpecAvgMode, SpecAvgCount: Integer;
|
|
SpecBackmult: Double;
|
|
WfDet, WfAvgMode, WfAvgCount: Integer;
|
|
WfBackmult: Double;
|
|
RefLevel, Range, GridStep: Double;
|
|
const AudioOutDevName: string;
|
|
const AudioInDevName: string);
|
|
var
|
|
i, Idx: Integer;
|
|
begin
|
|
FLoading := True;
|
|
try
|
|
// FFT Size
|
|
Idx := 2; // default 4096
|
|
for i := 0 to High(FFT_SIZES) do
|
|
if FFT_SIZES[i] = FFTSize then begin Idx := i; Break; end;
|
|
FCmbFFTSize.ItemIndex := Idx;
|
|
|
|
// Window Type
|
|
if (WinType >= 0) and (WinType < FCmbWindow.Items.Count) then
|
|
FCmbWindow.ItemIndex := WinType
|
|
else
|
|
FCmbWindow.ItemIndex := 5; // Kaiser
|
|
|
|
// Spectrum Detector
|
|
if (SpecDet >= 0) and (SpecDet < FCmbSpecDet.Items.Count) then
|
|
FCmbSpecDet.ItemIndex := SpecDet
|
|
else
|
|
FCmbSpecDet.ItemIndex := 0;
|
|
|
|
// Spectrum Avg Mode (WDSP value → combo index)
|
|
Idx := 2; // default Recursive
|
|
for i := 0 to High(AVGMODE_VALUES) do
|
|
if AVGMODE_VALUES[i] = SpecAvgMode then begin Idx := i; Break; end;
|
|
FCmbSpecAvg.ItemIndex := Idx;
|
|
|
|
FSpinSpecCount.Value := EnsureRange(SpecAvgCount, 1, 256);
|
|
FEdSpecBackmult.Text := FormatFloat('0.##', SpecBackmult);
|
|
|
|
// Waterfall Detector
|
|
if (WfDet >= 0) and (WfDet < FCmbWfDet.Items.Count) then
|
|
FCmbWfDet.ItemIndex := WfDet
|
|
else
|
|
FCmbWfDet.ItemIndex := 2; // Average
|
|
|
|
// Waterfall Avg Mode
|
|
Idx := 4; // default Log Recursive
|
|
for i := 0 to High(AVGMODE_VALUES) do
|
|
if AVGMODE_VALUES[i] = WfAvgMode then begin Idx := i; Break; end;
|
|
FCmbWfAvg.ItemIndex := Idx;
|
|
|
|
FSpinWfCount.Value := EnsureRange(WfAvgCount, 1, 256);
|
|
FEdWfBackmult.Text := FormatFloat('0.##', WfBackmult);
|
|
|
|
// Grid
|
|
FEdRefLevel.Text := FormatFloat('0.#', RefLevel);
|
|
FEdRange.Text := FormatFloat('0.#', Range);
|
|
Idx := 1; // 10 dB default
|
|
for i := 0 to High(GRID_STEPS) do
|
|
if Abs(GRID_STEPS[i] - GridStep) < 0.5 then begin Idx := i; Break; end;
|
|
FCmbGridStep.ItemIndex := Idx;
|
|
|
|
// RX Audio Output
|
|
if AudioOutDevName = '' then
|
|
FCmbRXDev.ItemIndex := 0 // 'Default'
|
|
else
|
|
begin
|
|
Idx := FCmbRXDev.Items.IndexOf(AudioOutDevName);
|
|
if Idx >= 0 then FCmbRXDev.ItemIndex := Idx
|
|
else FCmbRXDev.ItemIndex := 0;
|
|
end;
|
|
|
|
// TX Audio Input
|
|
if AudioInDevName = '' then
|
|
FCmbTXDev.ItemIndex := 0 // 'None'
|
|
else
|
|
begin
|
|
Idx := FCmbTXDev.Items.IndexOf(AudioInDevName);
|
|
if Idx >= 0 then FCmbTXDev.ItemIndex := Idx
|
|
else FCmbTXDev.ItemIndex := 0;
|
|
end;
|
|
finally
|
|
FLoading := False;
|
|
end;
|
|
end;
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// RefreshAudioDevices
|
|
// ---------------------------------------------------------------------------
|
|
|
|
procedure TSettingsForm.RefreshAudioDevices(AudioOut: TAudioOutput; AudioIn: TAudioInput);
|
|
var
|
|
Names: TStringList;
|
|
Cnt, i: Integer;
|
|
OldRXSel: string;
|
|
OldTXSel: string;
|
|
begin
|
|
Names := TStringList.Create;
|
|
try
|
|
// ── RX Output devices ─────────────────────────────────────────────────
|
|
if AudioOut <> nil then
|
|
begin
|
|
OldRXSel := FCmbRXDev.Text;
|
|
FCmbRXDev.Items.Clear;
|
|
FCmbRXDev.Items.Add('Default');
|
|
FRXDevCount := 0;
|
|
|
|
Cnt := 0;
|
|
Names.Clear;
|
|
if AudioOut.EnumOutputDevices(Names, FRXDevIndices, Cnt) then
|
|
begin
|
|
FRXDevCount := Cnt;
|
|
for i := 0 to Cnt - 1 do
|
|
FCmbRXDev.Items.Add(Names[i]);
|
|
end;
|
|
|
|
i := FCmbRXDev.Items.IndexOf(OldRXSel);
|
|
if i >= 0 then FCmbRXDev.ItemIndex := i
|
|
else FCmbRXDev.ItemIndex := 0;
|
|
end;
|
|
|
|
// ── TX Input devices ──────────────────────────────────────────────────
|
|
if AudioIn <> nil then
|
|
begin
|
|
OldTXSel := FCmbTXDev.Text;
|
|
FCmbTXDev.Items.Clear;
|
|
FCmbTXDev.Items.Add('None');
|
|
FTXDevCount := 0;
|
|
|
|
Cnt := 0;
|
|
Names.Clear;
|
|
if AudioIn.EnumInputDevices(Names, FTXDevIndices, Cnt) then
|
|
begin
|
|
FTXDevCount := Cnt;
|
|
for i := 0 to Cnt - 1 do
|
|
FCmbTXDev.Items.Add(Names[i]);
|
|
end;
|
|
|
|
i := FCmbTXDev.Items.IndexOf(OldTXSel);
|
|
if i >= 0 then FCmbTXDev.ItemIndex := i
|
|
else FCmbTXDev.ItemIndex := 0;
|
|
end;
|
|
finally
|
|
Names.Free;
|
|
end;
|
|
end;
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Value accessors
|
|
// ---------------------------------------------------------------------------
|
|
|
|
function TSettingsForm.GetFFTSize: Integer;
|
|
var Idx: Integer;
|
|
begin
|
|
Idx := FCmbFFTSize.ItemIndex;
|
|
if (Idx >= 0) and (Idx <= High(FFT_SIZES)) then
|
|
Result := FFT_SIZES[Idx]
|
|
else
|
|
Result := 4096;
|
|
end;
|
|
|
|
function TSettingsForm.GetSpecBackmult: Double;
|
|
var V: Double; Err: Integer;
|
|
begin
|
|
Val(FEdSpecBackmult.Text, V, Err);
|
|
if Err <> 0 then V := 0.45;
|
|
Result := EnsureRange(V, 0.0, 1.0);
|
|
end;
|
|
|
|
function TSettingsForm.GetWfBackmult: Double;
|
|
var V: Double; Err: Integer;
|
|
begin
|
|
Val(FEdWfBackmult.Text, V, Err);
|
|
if Err <> 0 then V := 0.45;
|
|
Result := EnsureRange(V, 0.0, 1.0);
|
|
end;
|
|
|
|
function TSettingsForm.GetRefLevel: Double;
|
|
var V: Double; Err: Integer;
|
|
begin
|
|
Val(FEdRefLevel.Text, V, Err);
|
|
if Err <> 0 then V := -20.0;
|
|
Result := EnsureRange(V, -200.0, 0.0);
|
|
end;
|
|
|
|
function TSettingsForm.GetRange: Double;
|
|
var V: Double; Err: Integer;
|
|
begin
|
|
Val(FEdRange.Text, V, Err);
|
|
if Err <> 0 then V := 110.0;
|
|
Result := EnsureRange(V, 10.0, 200.0);
|
|
end;
|
|
|
|
function TSettingsForm.GetGridStep: Double;
|
|
var Idx: Integer;
|
|
begin
|
|
Idx := FCmbGridStep.ItemIndex;
|
|
if (Idx >= 0) and (Idx <= High(GRID_STEPS)) then
|
|
Result := GRID_STEPS[Idx]
|
|
else
|
|
Result := 10.0;
|
|
end;
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Fire helpers
|
|
// ---------------------------------------------------------------------------
|
|
|
|
procedure TSettingsForm.FireDisplayChange;
|
|
var
|
|
AvgIdx: Integer;
|
|
AvgVal: Integer;
|
|
begin
|
|
if FLoading then Exit;
|
|
if not Assigned(FOnDisplayChange) then Exit;
|
|
AvgIdx := FCmbSpecAvg.ItemIndex;
|
|
if (AvgIdx >= 0) and (AvgIdx <= High(AVGMODE_VALUES)) then
|
|
AvgVal := AVGMODE_VALUES[AvgIdx]
|
|
else
|
|
AvgVal := 3;
|
|
FOnDisplayChange(
|
|
GetFFTSize,
|
|
FCmbWindow.ItemIndex,
|
|
FCmbSpecDet.ItemIndex,
|
|
AvgVal,
|
|
FSpinSpecCount.Value,
|
|
GetSpecBackmult);
|
|
end;
|
|
|
|
procedure TSettingsForm.FireWaterfallChange;
|
|
var
|
|
AvgIdx: Integer;
|
|
AvgVal: Integer;
|
|
begin
|
|
if FLoading then Exit;
|
|
if not Assigned(FOnWaterfallChange) then Exit;
|
|
AvgIdx := FCmbWfAvg.ItemIndex;
|
|
if (AvgIdx >= 0) and (AvgIdx <= High(AVGMODE_VALUES)) then
|
|
AvgVal := AVGMODE_VALUES[AvgIdx]
|
|
else
|
|
AvgVal := 3;
|
|
FOnWaterfallChange(
|
|
FCmbWfDet.ItemIndex,
|
|
AvgVal,
|
|
FSpinWfCount.Value,
|
|
GetWfBackmult);
|
|
end;
|
|
|
|
procedure TSettingsForm.FireGridChange;
|
|
begin
|
|
if FLoading then Exit;
|
|
if not Assigned(FOnGridChange) then Exit;
|
|
FOnGridChange(GetRefLevel, GetRange, GetGridStep);
|
|
end;
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Event handlers
|
|
// ---------------------------------------------------------------------------
|
|
|
|
procedure TSettingsForm.OnFFTSizeChange(Sender: TObject);
|
|
begin FireDisplayChange; end;
|
|
|
|
procedure TSettingsForm.OnWindowChange(Sender: TObject);
|
|
begin FireDisplayChange; end;
|
|
|
|
procedure TSettingsForm.OnSpecDetChange(Sender: TObject);
|
|
begin FireDisplayChange; end;
|
|
|
|
procedure TSettingsForm.OnSpecAvgModeChange(Sender: TObject);
|
|
begin FireDisplayChange; end;
|
|
|
|
procedure TSettingsForm.OnSpecCountChange(Sender: TObject);
|
|
begin FireDisplayChange; end;
|
|
|
|
procedure TSettingsForm.OnSpecBackmultChange(Sender: TObject);
|
|
begin FireDisplayChange; end;
|
|
|
|
procedure TSettingsForm.OnWfDetChange(Sender: TObject);
|
|
begin FireWaterfallChange; end;
|
|
|
|
procedure TSettingsForm.OnWfAvgModeChange(Sender: TObject);
|
|
begin FireWaterfallChange; end;
|
|
|
|
procedure TSettingsForm.OnWfCountChange(Sender: TObject);
|
|
begin FireWaterfallChange; end;
|
|
|
|
procedure TSettingsForm.OnWfBackmultChange(Sender: TObject);
|
|
begin FireWaterfallChange; end;
|
|
|
|
procedure TSettingsForm.OnRefLevelChange(Sender: TObject);
|
|
begin FireGridChange; end;
|
|
|
|
procedure TSettingsForm.OnRangeChange(Sender: TObject);
|
|
begin FireGridChange; end;
|
|
|
|
procedure TSettingsForm.OnGridStepChange(Sender: TObject);
|
|
begin FireGridChange; end;
|
|
|
|
procedure TSettingsForm.OnRXDevChange(Sender: TObject);
|
|
var
|
|
Idx: Integer;
|
|
DevIdx: Integer;
|
|
DevName: string;
|
|
begin
|
|
if FLoading then Exit;
|
|
if not Assigned(FOnAudioDevChange) then Exit;
|
|
Idx := FCmbRXDev.ItemIndex;
|
|
if Idx <= 0 then
|
|
begin
|
|
// "Default"
|
|
FOnAudioDevChange(-1, '');
|
|
end
|
|
else
|
|
begin
|
|
DevName := FCmbRXDev.Items[Idx];
|
|
// Map combo index to PA device index
|
|
// Combo: 0=Default, 1..N = real devices
|
|
DevIdx := Idx - 1;
|
|
if (DevIdx >= 0) and (DevIdx < FRXDevCount) then
|
|
FOnAudioDevChange(FRXDevIndices[DevIdx], DevName)
|
|
else
|
|
FOnAudioDevChange(-1, '');
|
|
end;
|
|
end;
|
|
|
|
procedure TSettingsForm.OnTXDevChange(Sender: TObject);
|
|
var
|
|
Idx: Integer;
|
|
DevIdx: Integer;
|
|
DevName: string;
|
|
begin
|
|
if FLoading then Exit;
|
|
if not Assigned(FOnAudioInDevChange) then Exit;
|
|
Idx := FCmbTXDev.ItemIndex;
|
|
if Idx <= 0 then
|
|
begin
|
|
// 'None'
|
|
FOnAudioInDevChange(-1, '');
|
|
end
|
|
else
|
|
begin
|
|
DevName := FCmbTXDev.Items[Idx];
|
|
DevIdx := Idx - 1;
|
|
if (DevIdx >= 0) and (DevIdx < FTXDevCount) then
|
|
FOnAudioInDevChange(FTXDevIndices[DevIdx], DevName)
|
|
else
|
|
FOnAudioInDevChange(-1, '');
|
|
end;
|
|
end;
|
|
|
|
procedure TSettingsForm.LoadVisibility(ShowSpectrum, ShowWaterfall: Boolean);
|
|
begin
|
|
FLoading := True;
|
|
try
|
|
FChkShowSpectrum.Checked := ShowSpectrum;
|
|
FChkShowWaterfall.Checked := ShowWaterfall;
|
|
finally
|
|
FLoading := False;
|
|
end;
|
|
end;
|
|
|
|
procedure TSettingsForm.OnVisibilityChkChange(Sender: TObject);
|
|
begin
|
|
if FLoading then Exit;
|
|
if not Assigned(FOnVisibilityChange) then Exit;
|
|
FOnVisibilityChange(FChkShowSpectrum.Checked, FChkShowWaterfall.Checked);
|
|
end;
|
|
|
|
procedure TSettingsForm.OnFPSCmbChange(Sender: TObject);
|
|
var
|
|
Idx: Integer;
|
|
begin
|
|
if FLoading then Exit;
|
|
if not Assigned(FOnFPSChange) then Exit;
|
|
Idx := FCmbFPS.ItemIndex;
|
|
if (Idx >= 0) and (Idx <= High(FPS_VALUES)) then
|
|
FOnFPSChange(FPS_VALUES[Idx]);
|
|
end;
|
|
|
|
procedure TSettingsForm.LoadFPS(FPS: Integer);
|
|
var
|
|
i, Idx: Integer;
|
|
begin
|
|
FLoading := True;
|
|
try
|
|
Idx := 3; // 20 fps default
|
|
for i := 0 to High(FPS_VALUES) do
|
|
if FPS_VALUES[i] = FPS then begin Idx := i; Break; end;
|
|
FCmbFPS.ItemIndex := Idx;
|
|
finally
|
|
FLoading := False;
|
|
end;
|
|
end;
|
|
|
|
procedure TSettingsForm.BtnCloseClick(Sender: TObject);
|
|
begin
|
|
Close;
|
|
end;
|
|
|
|
end.
|