mirror of
https://git.vladimir.cc/vladimir/ewsdr.git
synced 2026-08-25 18:43:51 +00:00
RX MUTE (self-monitor даунлинка на передаче): кнопка теперь доступна на
ВСЕХ бэкендах, а не только Pluto/QO-100. Активна только в DUP; без DUP
кнопка disabled и звук на TX не проходит (движок гейтит по
FKeepRXDuringTX=FDisplayDuplex — двойная защита: аудио-гейт + RXA не
кормится). Добавлен RadioController.DisplayDuplex. TFlatButton теперь
рисует disabled-состояние приглушённым (текст/рамка к фону).
TX pre-roll 8144->6096 (2 блока подушки вместо 3): обоснование «нужно 3
блока» опиралось на щелчок ~0.5с после старта, который на деле оказался
bfo-глитчом тона (устранён f2d0519), а не underrun'ом подушки. Латентность
старта TX ~-10мс; startup-щелчков под нагрузкой DUP не слышно (подтверждено).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
213 lines
6.8 KiB
ObjectPascal
213 lines
6.8 KiB
ObjectPascal
unit FlatButton;
|
|
|
|
{ Кнопка с полным контролем цвета на Windows и Linux.
|
|
Используй вместо TButton везде где нужна тёмная тема. }
|
|
|
|
{$mode objfpc}{$H+}
|
|
|
|
interface
|
|
|
|
uses
|
|
Classes, SysUtils, Controls, Graphics, LCLType, LMessages, Types;
|
|
|
|
type
|
|
TFlatButton = class(TGraphicControl)
|
|
private
|
|
FActive: Boolean;
|
|
FHot: Boolean;
|
|
FClrNorm: TColor;
|
|
FClrActive: TColor;
|
|
FClrHot: TColor;
|
|
FClrBorder: TColor;
|
|
FClrText: TColor;
|
|
FClrTextAct:TColor;
|
|
FOnClick: TNotifyEvent;
|
|
procedure SetActive(V: Boolean);
|
|
protected
|
|
procedure Paint; override;
|
|
procedure MouseEnter; override;
|
|
procedure MouseLeave; override;
|
|
procedure CMTextChanged(var Msg: TLMessage); message CM_TEXTCHANGED;
|
|
procedure MouseDown(Button: TMouseButton; Shift: TShiftState;
|
|
X, Y: Integer); override;
|
|
procedure MouseUp(Button: TMouseButton; Shift: TShiftState;
|
|
X, Y: Integer); override;
|
|
public
|
|
constructor Create(AOwner: TComponent); override;
|
|
|
|
property Active: Boolean read FActive write SetActive;
|
|
property ClrNorm: TColor read FClrNorm write FClrNorm;
|
|
property ClrActive: TColor read FClrActive write FClrActive;
|
|
property ClrHot: TColor read FClrHot write FClrHot;
|
|
property ClrBorder: TColor read FClrBorder write FClrBorder;
|
|
property ClrText: TColor read FClrText write FClrText;
|
|
property ClrTextAct:TColor read FClrTextAct write FClrTextAct;
|
|
property OnClick: TNotifyEvent read FOnClick write FOnClick;
|
|
property OnMouseDown;
|
|
property OnMouseUp;
|
|
property Caption;
|
|
property Font;
|
|
property Enabled;
|
|
property Visible;
|
|
end;
|
|
|
|
// Отрисовка кнопки в стиле TFlatButton на произвольном канвасе/прямоугольнике.
|
|
// Используется и TFlatButton.Paint, и оверлеями, которые рендерятся в битмап
|
|
// (VfoOverlay) — чтобы вид кнопок был одинаковым по всей программе. Шрифт
|
|
// берётся текущий у канваса (вызывающий выставляет Name/Size заранее).
|
|
procedure PaintFlatButton(C: TCanvas; const R: TRect; const ACaption: string;
|
|
AActive, AHot: Boolean;
|
|
ClrNorm, ClrActive, ClrHot, ClrBorder, ClrText, ClrTextAct: TColor);
|
|
|
|
// Фабрика — аналог MakeBtn, возвращает TFlatButton
|
|
function MakeFlatBtn(AParent: TWinControl; const ACap: string;
|
|
ALeft, ATop, AW, AH: Integer;
|
|
AHandler: TNotifyEvent;
|
|
ClrNorm: TColor = TColor($00202020);
|
|
ClrActive: TColor = TColor($00003300);
|
|
ClrHot: TColor = TColor($00303030);
|
|
ClrBorder: TColor = TColor($00404040);
|
|
ClrText: TColor = TColor($00E0E0E0);
|
|
ClrTextAct: TColor = TColor($0000FF88)): TFlatButton;
|
|
|
|
implementation
|
|
|
|
constructor TFlatButton.Create(AOwner: TComponent);
|
|
begin
|
|
inherited Create(AOwner);
|
|
FActive := False;
|
|
FHot := False;
|
|
FClrNorm := TColor($00202020);
|
|
FClrActive := TColor($00003300);
|
|
FClrHot := TColor($00303030);
|
|
FClrBorder := TColor($00404040);
|
|
FClrText := TColor($00E0E0E0);
|
|
FClrTextAct := TColor($0000FF88);
|
|
Cursor := crHandPoint;
|
|
end;
|
|
|
|
procedure TFlatButton.SetActive(V: Boolean);
|
|
begin
|
|
if FActive = V then Exit;
|
|
FActive := V;
|
|
Invalidate;
|
|
end;
|
|
|
|
procedure PaintFlatButton(C: TCanvas; const R: TRect; const ACaption: string;
|
|
AActive, AHot: Boolean;
|
|
ClrNorm, ClrActive, ClrHot, ClrBorder, ClrText, ClrTextAct: TColor);
|
|
const
|
|
CORNER = 3;
|
|
var
|
|
BG: TColor;
|
|
TW, TH: Integer;
|
|
begin
|
|
if AActive then BG := ClrActive
|
|
else if AHot then BG := ClrHot
|
|
else BG := ClrNorm;
|
|
|
|
// Фон со скруглёнными углами
|
|
C.Brush.Color := BG;
|
|
C.Brush.Style := bsSolid;
|
|
C.Pen.Color := BG;
|
|
C.Pen.Style := psSolid;
|
|
C.RoundRect(R.Left, R.Top, R.Right, R.Bottom, CORNER * 2, CORNER * 2);
|
|
|
|
// Рамка
|
|
C.Pen.Color := ClrBorder;
|
|
C.Brush.Style := bsClear;
|
|
C.RoundRect(R.Left, R.Top, R.Right - 1, R.Bottom - 1, CORNER * 2, CORNER * 2);
|
|
|
|
// Текст
|
|
if AActive then C.Font.Color := ClrTextAct
|
|
else C.Font.Color := ClrText;
|
|
C.Brush.Style := bsClear;
|
|
TW := C.TextWidth(ACaption);
|
|
TH := C.TextHeight('A');
|
|
C.TextOut(R.Left + (R.Right - R.Left - TW) div 2,
|
|
R.Top + (R.Bottom - R.Top - TH) div 2, ACaption);
|
|
end;
|
|
|
|
function BlendCol(A, B: TColor; T: Double): TColor;
|
|
// Линейная смесь A→B на долю T (0..1); системные цвета резолвим через ColorToRGB.
|
|
var ca, cb: LongInt; r, g, bl: Integer;
|
|
begin
|
|
ca := ColorToRGB(A); cb := ColorToRGB(B);
|
|
r := Round((ca and $FF) + ((cb and $FF) - (ca and $FF)) * T);
|
|
g := Round(((ca shr 8) and $FF) + (((cb shr 8) and $FF) - ((ca shr 8) and $FF)) * T);
|
|
bl := Round(((ca shr 16) and $FF)+ (((cb shr 16) and $FF)- ((ca shr 16) and $FF))* T);
|
|
Result := TColor(r or (g shl 8) or (bl shl 16));
|
|
end;
|
|
|
|
procedure TFlatButton.Paint;
|
|
begin
|
|
if Enabled then
|
|
PaintFlatButton(Canvas, ClientRect, Caption, FActive, FHot,
|
|
FClrNorm, FClrActive, FClrHot, FClrBorder, FClrText, FClrTextAct)
|
|
else
|
|
// disabled: не active/не hot, текст и рамка приглушены к фону.
|
|
PaintFlatButton(Canvas, ClientRect, Caption, False, False,
|
|
FClrNorm, FClrActive, FClrHot,
|
|
BlendCol(FClrBorder, FClrNorm, 0.6),
|
|
BlendCol(FClrText, FClrNorm, 0.55), FClrTextAct);
|
|
end;
|
|
|
|
procedure TFlatButton.MouseEnter;
|
|
begin
|
|
inherited;
|
|
FHot := True;
|
|
Invalidate;
|
|
end;
|
|
|
|
procedure TFlatButton.MouseLeave;
|
|
begin
|
|
inherited;
|
|
FHot := False;
|
|
Invalidate;
|
|
end;
|
|
|
|
procedure TFlatButton.CMTextChanged(var Msg: TLMessage);
|
|
begin
|
|
Invalidate;
|
|
end;
|
|
|
|
procedure TFlatButton.MouseDown(Button: TMouseButton; Shift: TShiftState;
|
|
X, Y: Integer);
|
|
begin
|
|
inherited;
|
|
end;
|
|
|
|
procedure TFlatButton.MouseUp(Button: TMouseButton; Shift: TShiftState;
|
|
X, Y: Integer);
|
|
begin
|
|
inherited;
|
|
if (Button = mbLeft) and PtInRect(ClientRect, Point(X, Y)) then
|
|
if Assigned(FOnClick) then FOnClick(Self);
|
|
end;
|
|
|
|
function MakeFlatBtn(AParent: TWinControl; const ACap: string;
|
|
ALeft, ATop, AW, AH: Integer;
|
|
AHandler: TNotifyEvent;
|
|
ClrNorm, ClrActive, ClrHot, ClrBorder, ClrText, ClrTextAct: TColor): TFlatButton;
|
|
begin
|
|
Result := TFlatButton.Create(AParent);
|
|
Result.Parent := AParent;
|
|
Result.Caption := ACap;
|
|
Result.Left := ALeft;
|
|
Result.Top := ATop;
|
|
Result.Width := AW;
|
|
Result.Height := AH;
|
|
Result.OnClick := AHandler;
|
|
Result.ClrNorm := ClrNorm;
|
|
Result.ClrActive := ClrActive;
|
|
Result.ClrHot := ClrHot;
|
|
Result.ClrBorder := ClrBorder;
|
|
Result.ClrText := ClrText;
|
|
Result.ClrTextAct := ClrTextAct;
|
|
Result.Font.Name := 'Courier New';
|
|
Result.Font.Size := 8;
|
|
Result.Font.Color := ClrText;
|
|
end;
|
|
|
|
end.
|