mirror of
https://git.vladimir.cc/vladimir/ewsdr.git
synced 2026-08-25 18:43:51 +00:00
Разбор «IMD3 хуже, чем в Thetis»: юзерская плата (Orion MkII/Anvelina PRO3) при стоковой цели feedback 152 перегружает входные каскады ADC2208+кодека — фидбек сам интермодулирует, PS корректирует по искажённому наблюдению. Замеры eu2av (Thetis-Enhanced): оптимум FB≈22 при ATT≈10 дБ. - PSFBTarget: цель уровня feedback, авто по плате (board 5 → 22, иначе 152), настройка ps_fb_target (0 = авто) - авто-ATT: пороги от цели (FB > 1.5×цели / FB < 0.7×цели при ATT>0), ddB = 20*log10(FB/цель). Заодно чинит «ATT всегда 0»: старый порог >181 на этой плате никогда не достигался - PSOutlierSigmaEff → SetPSOutlierSigma (новый экспорт libwdsp): MAD- фильтр выбросов перед фитом calcc, авто σ=5.0 для Orion MkII, настройка ps_outlier_sigma (-1 = авто) - лампа FB в поповере: зоны от цели (0.7–1.3× зелёная), подпись «FB: тек/цель» Требует libwdsp с патчами eu2av (форк wdsp, коммит d27684c). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
454 lines
15 KiB
ObjectPascal
454 lines
15 KiB
ObjectPascal
unit PureSignalPopup;
|
||
|
||
{
|
||
TPureSignalPopup — поповер управления и настроек PureSignal, открывается
|
||
ПКМ по кнопке PS на левой панели. Управление: PS (авто-калибровка вкл/выкл),
|
||
CAL (одиночная калибровка), RESET (сброс коррекции), 2TON. Статус: лампа и
|
||
уровень feedback (оптимум ~152), счётчик калибровок, лампа коррекции,
|
||
GetPk/SetPk. Параметры calcc (MOX/CAL wait, PH#, Relax ptol, Auto-atten,
|
||
Pin/Map/Stbl, Ints, тона 2TON) пишутся в FTXSettings.PS* живьём:
|
||
ApplyPSSettingsToDSP + OnChanged (персист в MainForm).
|
||
|
||
Реализован как ДОЧЕРНИЙ контрол верхнеуровневой формы (не отдельное окно) —
|
||
как TPanDisplayPopup: Wayland не даёт клиенту позиционировать top-level окна.
|
||
}
|
||
|
||
{$IFDEF FPC}
|
||
{$MODE Delphi}
|
||
{$ENDIF}
|
||
|
||
interface
|
||
|
||
uses
|
||
Classes, SysUtils, Math, Types, Controls, ExtCtrls, StdCtrls, Graphics, Forms,
|
||
RadioController, Settings,
|
||
FlatButton, FlatComboBox, FlatCheckBox, FlatSpinEdit, FlatFloatSpinEdit;
|
||
|
||
type
|
||
TPureSignalPopup = class(TCustomControl)
|
||
private
|
||
FController: TRadioController; // не владеем
|
||
FLoading: Boolean; // подавляет Apply при программной заливке
|
||
FBg: TPanel;
|
||
FTitle: TLabel;
|
||
FBtnClose: TFlatButton;
|
||
// Управление
|
||
FBtnPS: TFlatButton;
|
||
FBtnCal: TFlatButton;
|
||
FBtnReset: TFlatButton;
|
||
FBtn2Ton: TFlatButton;
|
||
// Статус
|
||
FFbLamp: TPanel;
|
||
FLbFbVal: TLabel;
|
||
FLbCals: TLabel;
|
||
FCorrLamp: TPanel;
|
||
FLbGetPk: TLabel;
|
||
FLbAtt: TLabel;
|
||
// Параметры
|
||
FSePk: TFlatFloatSpinEdit;
|
||
FBtnDefPk: TFlatButton;
|
||
FSeMoxWait: TFlatFloatSpinEdit;
|
||
FSeCalWait: TFlatFloatSpinEdit;
|
||
FSePhNum: TFlatSpinEdit;
|
||
FChkRelax: TFlatCheckBox;
|
||
FChkAutoAtt:TFlatCheckBox;
|
||
FChkPin: TFlatCheckBox;
|
||
FChkMap: TFlatCheckBox;
|
||
FChkStbl: TFlatCheckBox;
|
||
FCbInts: TFlatComboBox;
|
||
FSeTT1: TFlatSpinEdit;
|
||
FSeTT2: TFlatSpinEdit;
|
||
FOnChanged: TNotifyEvent;
|
||
|
||
function MakeLbl(const Cap: string; ALeft, ATop, AW: Integer): TLabel;
|
||
procedure Apply(Sender: TObject); // контролы → FTXSettings.PS* → WDSP
|
||
procedure PSClick(Sender: TObject);
|
||
procedure CalClick(Sender: TObject);
|
||
procedure ResetClick(Sender: TObject);
|
||
procedure TwoToneClick(Sender: TObject);
|
||
procedure DefPkClick(Sender: TObject);
|
||
procedure CloseClick(Sender: TObject);
|
||
protected
|
||
procedure Paint; override;
|
||
public
|
||
constructor Create(AOwner: TComponent); override;
|
||
procedure LoadFrom(C: TRadioController);
|
||
// Обновление статусных ламп/значений (звать с UI-таймера пока Visible
|
||
// и по rfPureSignal/rfTwoTone).
|
||
procedure RefreshStatus;
|
||
// Показать под контролом-якорем (кнопкой PS) как дочерний контрол формы.
|
||
procedure PopupBelow(C: TRadioController; Anchor: TControl);
|
||
property OnChanged: TNotifyEvent read FOnChanged write FOnChanged;
|
||
end;
|
||
|
||
implementation
|
||
|
||
const
|
||
{$IFDEF WINDOWS}
|
||
UI_FONT = 'Segoe UI';
|
||
{$ELSE}
|
||
UI_FONT = 'Sans';
|
||
{$ENDIF}
|
||
CLR_BG = TColor($00303030); // рамка (1px кайма формы)
|
||
CLR_PANEL = TColor($001A1A1A);
|
||
CLR_TEXT = TColor($00E0E0E0);
|
||
CLR_TEXTDIM = TColor($00888888);
|
||
CLR_ACCENT = TColor($0040FF80);
|
||
CLR_INPUT = TColor($00FFFFFF);
|
||
CLR_INPUT_TEXT = TColor($00202020);
|
||
|
||
// Лампа feedback — пороги от цели PSFBTarget (Thetis-Enhanced eu2av):
|
||
// >1.5×цели горячо, 0.7..1.3×цели оптимум, 0.5×..0.7× слабовато, ниже — нет
|
||
// сигнала. TColor = BGR.
|
||
CLR_FB_HOT = TColor($00FF9030); // голубой: слишком горячо (атт. мал)
|
||
CLR_FB_OK = TColor($0000FF00); // зелёный: оптимум
|
||
CLR_FB_LOW = TColor($0000FFFF); // жёлтый: слабовато
|
||
CLR_FB_NONE = TColor($000000C0); // красный: нет feedback
|
||
CLR_LAMP_OFF= TColor($00101010);
|
||
|
||
POP_W = 272;
|
||
PAD = 10;
|
||
LBLW = 92;
|
||
CTLX = 108;
|
||
ROWH = 24;
|
||
|
||
constructor TPureSignalPopup.Create(AOwner: TComponent);
|
||
var
|
||
Y: Integer;
|
||
|
||
function NextRow: Integer;
|
||
begin
|
||
Result := Y;
|
||
Inc(Y, ROWH + 4);
|
||
end;
|
||
|
||
function MakeIntSpin(ATop, AMin, AMax, AInc: Integer): TFlatSpinEdit;
|
||
begin
|
||
Result := TFlatSpinEdit.Create(Self);
|
||
Result.Parent := FBg;
|
||
Result.SetBounds(CTLX, ATop, POP_W - CTLX - PAD, ROWH - 2);
|
||
Result.MinValue := AMin;
|
||
Result.MaxValue := AMax;
|
||
Result.Increment := AInc;
|
||
Result.Color := CLR_INPUT;
|
||
Result.Font.Color := CLR_INPUT_TEXT;
|
||
Result.Font.Name := UI_FONT;
|
||
Result.Font.Size := 9;
|
||
Result.OnChange := Apply;
|
||
end;
|
||
|
||
function MakeFloatSpin(ATop: Integer; AMin, AMax, AInc: Double;
|
||
ADec: Integer): TFlatFloatSpinEdit;
|
||
begin
|
||
Result := TFlatFloatSpinEdit.Create(Self);
|
||
Result.Parent := FBg;
|
||
Result.SetBounds(CTLX, ATop, POP_W - CTLX - PAD, ROWH - 2);
|
||
Result.MinValue := AMin;
|
||
Result.MaxValue := AMax;
|
||
Result.Increment := AInc;
|
||
Result.DecimalPlaces := ADec;
|
||
Result.Color := CLR_INPUT;
|
||
Result.Font.Color := CLR_INPUT_TEXT;
|
||
Result.Font.Name := UI_FONT;
|
||
Result.Font.Size := 9;
|
||
Result.OnChange := Apply;
|
||
end;
|
||
|
||
function MakeChk(const Cap: string; ALeft, ATop, AW: Integer): TFlatCheckBox;
|
||
begin
|
||
Result := TFlatCheckBox.Create(Self);
|
||
Result.Parent := FBg;
|
||
Result.SetBounds(ALeft, ATop, AW, ROWH - 4);
|
||
Result.Caption := Cap;
|
||
Result.Font.Color := CLR_TEXT;
|
||
Result.Font.Name := UI_FONT;
|
||
Result.Font.Size := 9;
|
||
Result.OnChange := Apply;
|
||
end;
|
||
|
||
function MakeLamp(ALeft, ATop: Integer): TPanel;
|
||
begin
|
||
Result := TPanel.Create(Self);
|
||
Result.Parent := FBg;
|
||
Result.SetBounds(ALeft, ATop, 16, 16);
|
||
Result.BevelOuter := bvNone;
|
||
Result.Color := CLR_LAMP_OFF;
|
||
Result.Caption := '';
|
||
end;
|
||
|
||
var
|
||
BW: Integer;
|
||
begin
|
||
inherited Create(AOwner);
|
||
ControlStyle := ControlStyle + [csOpaque];
|
||
Visible := False;
|
||
Color := CLR_BG;
|
||
Font.Name := UI_FONT;
|
||
Font.Size := 9;
|
||
Font.Color := CLR_TEXT;
|
||
FLoading := False;
|
||
|
||
FBg := TPanel.Create(Self);
|
||
FBg.Parent := Self;
|
||
FBg.BevelOuter := bvNone;
|
||
FBg.Color := CLR_PANEL;
|
||
FBg.SetBounds(1, 1, POP_W - 2, 10);
|
||
|
||
Y := PAD;
|
||
|
||
FTitle := MakeLbl('PureSignal', PAD, NextRow + 2, POP_W - 2 * PAD - 18);
|
||
FTitle.Font.Color := CLR_ACCENT;
|
||
FTitle.Font.Style := [fsBold];
|
||
FBtnClose := MakeFlatBtn(FBg, '×', POP_W - PAD - 16, PAD, 16, 16, CloseClick);
|
||
FBtnClose.ClrText := CLR_TEXTDIM;
|
||
FBtnClose.ClrTextAct := CLR_TEXT;
|
||
|
||
// ---- Управление: PS / CAL / RESET / 2TON ----
|
||
BW := (POP_W - 2 * PAD - 2 * 6) div 3;
|
||
FBtnPS := MakeFlatBtn(FBg, 'PS ON', PAD, Y, BW, ROWH, PSClick);
|
||
FBtnCal := MakeFlatBtn(FBg, 'CAL', PAD + BW + 6, Y, BW, ROWH, CalClick);
|
||
FBtnReset := MakeFlatBtn(FBg, 'RESET', PAD + 2 * (BW + 6), Y, BW, ROWH, ResetClick);
|
||
NextRow;
|
||
FBtn2Ton := MakeFlatBtn(FBg, '2TON', PAD, Y, BW, ROWH, TwoToneClick);
|
||
// ---- Статус: лампа + FB + cals + corr в одной строке с 2TON ----
|
||
FFbLamp := MakeLamp(PAD + BW + 6, Y + 4);
|
||
FLbFbVal := MakeLbl('FB: —', PAD + BW + 28, Y + 4, 68);
|
||
FCorrLamp := MakeLamp(POP_W - PAD - 62, Y + 4);
|
||
MakeLbl('corr', POP_W - PAD - 42, Y + 4, 36).Font.Color := CLR_TEXTDIM;
|
||
NextRow;
|
||
FLbCals := MakeLbl('cals: 0', PAD, Y + 2, 90);
|
||
FLbCals.Font.Color := CLR_TEXTDIM;
|
||
FLbAtt := MakeLbl('TX ATT: — dB', PAD + 96, Y + 2, POP_W - 2 * PAD - 96);
|
||
FLbAtt.Font.Color := CLR_TEXTDIM;
|
||
Inc(Y, 20);
|
||
|
||
MakeLbl('— Calibration —', PAD, Y + 3, POP_W - 2 * PAD).Font.Color := CLR_TEXTDIM;
|
||
NextRow;
|
||
|
||
// ---- GetPk / SetPk ----
|
||
FLbGetPk := MakeLbl('GetPk: —', PAD, Y + 3, LBLW);
|
||
FSePk := MakeFloatSpin(Y, 0.05, 1.00, 0.01, 4);
|
||
FSePk.Width := FSePk.Width - 42;
|
||
FBtnDefPk := MakeFlatBtn(FBg, 'DEF', POP_W - PAD - 38, Y, 38, ROWH - 2, DefPkClick);
|
||
NextRow;
|
||
|
||
MakeLbl('MOX wait, s', PAD, Y + 3, LBLW);
|
||
FSeMoxWait := MakeFloatSpin(Y, 0.0, 0.9, 0.1, 1);
|
||
NextRow;
|
||
MakeLbl('CAL wait, s', PAD, Y + 3, LBLW);
|
||
FSeCalWait := MakeFloatSpin(Y, 0.0, 10.0, 0.5, 1);
|
||
NextRow;
|
||
MakeLbl('PH# delay, ns', PAD, Y + 3, LBLW);
|
||
FSePhNum := MakeIntSpin(Y, 0, 1000, 10);
|
||
NextRow;
|
||
|
||
FChkAutoAtt := MakeChk('Auto-attenuate', PAD, Y + 2, 130);
|
||
FChkRelax := MakeChk('Relax tolerance', PAD + 134, Y + 2, POP_W - 2 * PAD - 134);
|
||
NextRow;
|
||
FChkPin := MakeChk('Pin', PAD, Y + 2, 56);
|
||
FChkMap := MakeChk('Map', PAD + 62, Y + 2, 56);
|
||
FChkStbl := MakeChk('Stbl', PAD + 124, Y + 2, 60);
|
||
NextRow;
|
||
|
||
MakeLbl('Tint (ints/spi)', PAD, Y + 3, LBLW);
|
||
FCbInts := TFlatComboBox.Create(Self);
|
||
FCbInts.Parent := FBg;
|
||
FCbInts.SetBounds(CTLX, Y, POP_W - CTLX - PAD, ROWH - 2);
|
||
FCbInts.Items.Add('16 / 256');
|
||
FCbInts.Items.Add('8 / 512');
|
||
FCbInts.Items.Add('4 / 1024');
|
||
FCbInts.Color := CLR_INPUT;
|
||
FCbInts.Font.Color := CLR_INPUT_TEXT;
|
||
FCbInts.Font.Name := UI_FONT;
|
||
FCbInts.Font.Size := 9;
|
||
FCbInts.OnChange := Apply;
|
||
NextRow;
|
||
|
||
MakeLbl('— Two-tone, Hz —', PAD, Y + 3, POP_W - 2 * PAD).Font.Color := CLR_TEXTDIM;
|
||
NextRow;
|
||
MakeLbl('F1 / F2', PAD, Y + 3, LBLW);
|
||
FSeTT1 := MakeIntSpin(Y, 100, 5000, 100);
|
||
FSeTT1.Width := (POP_W - CTLX - PAD - 6) div 2;
|
||
FSeTT2 := MakeIntSpin(Y, 100, 5000, 100);
|
||
FSeTT2.SetBounds(CTLX + FSeTT1.Width + 6, Y, (POP_W - CTLX - PAD - 6) div 2, ROWH - 2);
|
||
NextRow;
|
||
|
||
FBg.Height := Y - 1 + PAD;
|
||
Width := POP_W;
|
||
Height := Y + 1 + PAD;
|
||
end;
|
||
|
||
procedure TPureSignalPopup.Paint;
|
||
begin
|
||
Canvas.Brush.Style := bsSolid;
|
||
Canvas.Brush.Color := CLR_BG;
|
||
Canvas.Pen.Style := psClear;
|
||
Canvas.FillRect(0, 0, Width, Height);
|
||
end;
|
||
|
||
function TPureSignalPopup.MakeLbl(const Cap: string; ALeft, ATop, AW: Integer): TLabel;
|
||
begin
|
||
Result := TLabel.Create(Self);
|
||
Result.Parent := FBg;
|
||
Result.Caption := Cap;
|
||
Result.Left := ALeft;
|
||
Result.Top := ATop;
|
||
if AW > 0 then Result.Width := AW;
|
||
Result.Font.Color := CLR_TEXT;
|
||
Result.Font.Name := UI_FONT;
|
||
Result.Font.Size := 9;
|
||
end;
|
||
|
||
procedure TPureSignalPopup.LoadFrom(C: TRadioController);
|
||
begin
|
||
FController := C;
|
||
if C = nil then Exit;
|
||
FLoading := True;
|
||
try
|
||
FSePk.Value := EnsureRange(C.FTXSettings.PSHWPeak, 0.05, 1.00);
|
||
FSeMoxWait.Value := EnsureRange(C.FTXSettings.PSMoxDelay, 0.0, 0.9);
|
||
FSeCalWait.Value := EnsureRange(C.FTXSettings.PSLoopDelay, 0.0, 10.0);
|
||
FSePhNum.Value := EnsureRange(C.FTXSettings.PSTXDelayNs, 0, 1000);
|
||
FChkRelax.Checked := C.FTXSettings.PSRelaxPtol;
|
||
FChkAutoAtt.Checked := C.FTXSettings.PSAutoAtten;
|
||
FChkPin.Checked := C.FTXSettings.PSPin;
|
||
FChkMap.Checked := C.FTXSettings.PSMap;
|
||
FChkStbl.Checked := C.FTXSettings.PSStabilize;
|
||
FCbInts.ItemIndex := EnsureRange(C.FTXSettings.PSIntsIdx, 0, 2);
|
||
FSeTT1.Value := EnsureRange(C.FTXSettings.PSTTFreq1, 100, 5000);
|
||
FSeTT2.Value := EnsureRange(C.FTXSettings.PSTTFreq2, 100, 5000);
|
||
finally
|
||
FLoading := False;
|
||
end;
|
||
RefreshStatus;
|
||
end;
|
||
|
||
procedure TPureSignalPopup.Apply(Sender: TObject);
|
||
begin
|
||
if FLoading or (FController = nil) then Exit;
|
||
FController.FTXSettings.PSHWPeak := FSePk.Value;
|
||
FController.FTXSettings.PSMoxDelay := FSeMoxWait.Value;
|
||
FController.FTXSettings.PSLoopDelay := FSeCalWait.Value;
|
||
FController.FTXSettings.PSTXDelayNs := FSePhNum.Value;
|
||
FController.FTXSettings.PSRelaxPtol := FChkRelax.Checked;
|
||
FController.FTXSettings.PSAutoAtten := FChkAutoAtt.Checked;
|
||
FController.FTXSettings.PSPin := FChkPin.Checked;
|
||
FController.FTXSettings.PSMap := FChkMap.Checked;
|
||
FController.FTXSettings.PSStabilize := FChkStbl.Checked;
|
||
FController.FTXSettings.PSIntsIdx := EnsureRange(FCbInts.ItemIndex, 0, 2);
|
||
FController.FTXSettings.PSTTFreq1 := FSeTT1.Value;
|
||
FController.FTXSettings.PSTTFreq2 := FSeTT2.Value;
|
||
FController.ApplyPSSettingsToDSP;
|
||
if Assigned(FOnChanged) then FOnChanged(Self);
|
||
end;
|
||
|
||
procedure TPureSignalPopup.PSClick(Sender: TObject);
|
||
begin
|
||
if FController = nil then Exit;
|
||
FController.SetPureSignal(not FController.FPSEnabled);
|
||
RefreshStatus;
|
||
end;
|
||
|
||
procedure TPureSignalPopup.CalClick(Sender: TObject);
|
||
begin
|
||
if FController = nil then Exit;
|
||
FController.PSSingleCal;
|
||
RefreshStatus;
|
||
end;
|
||
|
||
procedure TPureSignalPopup.ResetClick(Sender: TObject);
|
||
begin
|
||
if FController = nil then Exit;
|
||
FController.PSReset;
|
||
RefreshStatus;
|
||
end;
|
||
|
||
procedure TPureSignalPopup.TwoToneClick(Sender: TObject);
|
||
begin
|
||
if FController = nil then Exit;
|
||
FController.SetTwoTone(not FController.FTwoTone);
|
||
RefreshStatus;
|
||
end;
|
||
|
||
procedure TPureSignalPopup.DefPkClick(Sender: TObject);
|
||
// Дефолт SetPk для протокола 2 (Thetis clsHardwareSpecific.PSDefaultPeak).
|
||
begin
|
||
FSePk.Value := 0.2899; // OnChange → Apply
|
||
end;
|
||
|
||
procedure TPureSignalPopup.CloseClick(Sender: TObject);
|
||
begin
|
||
Hide;
|
||
end;
|
||
|
||
procedure TPureSignalPopup.RefreshStatus;
|
||
var
|
||
FB, FBTarget: Integer;
|
||
LampClr: TColor;
|
||
begin
|
||
if (FController = nil) or not Visible then Exit;
|
||
|
||
FBtnPS.Active := FController.FPSEnabled;
|
||
if FController.FPSEnabled then FBtnPS.Caption := 'PS ON'
|
||
else FBtnPS.Caption := 'PS OFF';
|
||
FBtnCal.Active := FController.PSSingleCalActive;
|
||
FBtn2Ton.Active := FController.FTwoTone;
|
||
|
||
FB := FController.PSFeedbackLevel;
|
||
FBTarget := FController.PSFBTarget;
|
||
if FController.FPSFeedbackOn then
|
||
begin
|
||
if FB > Round(FBTarget * 1.5) then LampClr := CLR_FB_HOT
|
||
else if FB >= Round(FBTarget * 0.7) then
|
||
begin
|
||
if FB <= Round(FBTarget * 1.3) then LampClr := CLR_FB_OK
|
||
else LampClr := CLR_FB_LOW // 1.3..1.5×: желтовато
|
||
end
|
||
else if FB >= Round(FBTarget * 0.5) then LampClr := CLR_FB_LOW
|
||
else LampClr := CLR_FB_NONE;
|
||
end
|
||
else
|
||
LampClr := CLR_LAMP_OFF;
|
||
if FFbLamp.Color <> LampClr then FFbLamp.Color := LampClr;
|
||
FLbFbVal.Caption := Format('FB: %d/%d', [FB, FBTarget]);
|
||
|
||
if FController.PSCorrecting then
|
||
begin
|
||
if FCorrLamp.Color <> CLR_FB_OK then FCorrLamp.Color := CLR_FB_OK;
|
||
end
|
||
else if FCorrLamp.Color <> CLR_LAMP_OFF then
|
||
FCorrLamp.Color := CLR_LAMP_OFF;
|
||
|
||
FLbCals.Caption := Format('cals: %d', [FController.PSCalCount]);
|
||
FLbAtt.Caption := Format('TX ATT: %d dB', [FController.FTXSettings.AttOnTX]);
|
||
FLbGetPk.Caption := Format('GetPk: %.3f', [FController.PSMaxTXVal]);
|
||
end;
|
||
|
||
procedure TPureSignalPopup.PopupBelow(C: TRadioController; Anchor: TControl);
|
||
var
|
||
Root: TCustomForm;
|
||
L, T: Integer;
|
||
Cl: TPoint;
|
||
begin
|
||
if Anchor = nil then Exit;
|
||
Root := GetParentForm(Anchor);
|
||
if Root = nil then Exit;
|
||
LoadFrom(C);
|
||
// Позиция под якорем в КЛИЕНТСКИХ координатах формы (не top-level окно!).
|
||
Cl := Root.ScreenToClient(Anchor.ClientToScreen(Point(0, Anchor.Height + 2)));
|
||
L := Cl.X;
|
||
T := Cl.Y;
|
||
if L + Width > Root.ClientWidth then L := Root.ClientWidth - Width;
|
||
if L < 0 then L := 0;
|
||
if T + Height > Root.ClientHeight then
|
||
T := Root.ScreenToClient(Anchor.ClientToScreen(Point(0, 0))).Y - Height; // вверх
|
||
if T < 0 then T := 0;
|
||
Parent := Root;
|
||
SetBounds(L, T, Width, Height);
|
||
Visible := True;
|
||
BringToFront;
|
||
RefreshStatus;
|
||
end;
|
||
|
||
end.
|