mirror of
https://git.vladimir.cc/vladimir/ewsdr.git
synced 2026-08-25 20:37:33 +00:00
Add custom flat form controls
This commit is contained in:
+13
-28
@@ -5,7 +5,7 @@ unit DeviceForm;
|
|||||||
interface
|
interface
|
||||||
|
|
||||||
uses
|
uses
|
||||||
Classes, SysUtils, FlatButton, AppTheme, Forms, Controls, Graphics, Dialogs,
|
Classes, SysUtils, FlatButton, FlatEdit, FlatListBox, AppTheme, Forms, Controls, Graphics, Dialogs,
|
||||||
StdCtrls, ExtCtrls, ComCtrls, IniFiles;
|
StdCtrls, ExtCtrls, ComCtrls, IniFiles;
|
||||||
|
|
||||||
// Декодирование типа платы (совпадает с MainForm.BoardTypeName)
|
// Декодирование типа платы (совпадает с MainForm.BoardTypeName)
|
||||||
@@ -51,17 +51,17 @@ type
|
|||||||
PanelRight: TPanel;
|
PanelRight: TPanel;
|
||||||
|
|
||||||
LblSaved: TLabel;
|
LblSaved: TLabel;
|
||||||
LstSaved: TListBox;
|
LstSaved: TFlatListBox;
|
||||||
BtnAdd: TFlatButton;
|
BtnAdd: TFlatButton;
|
||||||
BtnRemove: TFlatButton;
|
BtnRemove: TFlatButton;
|
||||||
BtnSetAuto: TFlatButton;
|
BtnSetAuto: TFlatButton;
|
||||||
EdName: TEdit;
|
EdName: TFlatEdit;
|
||||||
EdIP: TEdit;
|
EdIP: TFlatEdit;
|
||||||
LblName: TLabel;
|
LblName: TLabel;
|
||||||
LblIP: TLabel;
|
LblIP: TLabel;
|
||||||
|
|
||||||
LblFound: TLabel;
|
LblFound: TLabel;
|
||||||
LstFound: TListBox;
|
LstFound: TFlatListBox;
|
||||||
BtnDiscover: TFlatButton;
|
BtnDiscover: TFlatButton;
|
||||||
BtnAddFound: TFlatButton;
|
BtnAddFound: TFlatButton;
|
||||||
|
|
||||||
@@ -205,7 +205,7 @@ begin
|
|||||||
|
|
||||||
MakeLbl(PanelLeft, 'SAVED DEVICES', PAD, PAD);
|
MakeLbl(PanelLeft, 'SAVED DEVICES', PAD, PAD);
|
||||||
|
|
||||||
LstSaved := TListBox.Create(Self);
|
LstSaved := TFlatListBox.Create(Self);
|
||||||
LstSaved.Parent := PanelLeft;
|
LstSaved.Parent := PanelLeft;
|
||||||
LstSaved.SetBounds(PAD, PAD + LabelH, LEFT_W - PAD * 2, 174);
|
LstSaved.SetBounds(PAD, PAD + LabelH, LEFT_W - PAD * 2, 174);
|
||||||
LstSaved.Color := CLR_BG;
|
LstSaved.Color := CLR_BG;
|
||||||
@@ -217,7 +217,7 @@ begin
|
|||||||
|
|
||||||
FieldTop := 212;
|
FieldTop := 212;
|
||||||
MakeLbl(PanelLeft, 'Name:', PAD, FieldTop + 5);
|
MakeLbl(PanelLeft, 'Name:', PAD, FieldTop + 5);
|
||||||
EdName := TEdit.Create(Self);
|
EdName := TFlatEdit.Create(Self);
|
||||||
EdName.Parent := PanelLeft;
|
EdName.Parent := PanelLeft;
|
||||||
EdName.SetBounds(76, FieldTop, LEFT_W - 76 - PAD, EDIT_H);
|
EdName.SetBounds(76, FieldTop, LEFT_W - 76 - PAD, EDIT_H);
|
||||||
EdName.Color := CLR_BG;
|
EdName.Color := CLR_BG;
|
||||||
@@ -227,7 +227,7 @@ begin
|
|||||||
|
|
||||||
Inc(FieldTop, EDIT_H + 10);
|
Inc(FieldTop, EDIT_H + 10);
|
||||||
MakeLbl(PanelLeft, 'IP:', PAD, FieldTop + 5);
|
MakeLbl(PanelLeft, 'IP:', PAD, FieldTop + 5);
|
||||||
EdIP := TEdit.Create(Self);
|
EdIP := TFlatEdit.Create(Self);
|
||||||
EdIP.Parent := PanelLeft;
|
EdIP.Parent := PanelLeft;
|
||||||
EdIP.SetBounds(76, FieldTop, LEFT_W - 76 - PAD, EDIT_H);
|
EdIP.SetBounds(76, FieldTop, LEFT_W - 76 - PAD, EDIT_H);
|
||||||
EdIP.Color := CLR_BG;
|
EdIP.Color := CLR_BG;
|
||||||
@@ -259,7 +259,7 @@ begin
|
|||||||
|
|
||||||
MakeLbl(PanelRight, 'DISCOVERED DEVICES', PAD, PAD);
|
MakeLbl(PanelRight, 'DISCOVERED DEVICES', PAD, PAD);
|
||||||
|
|
||||||
LstFound := TListBox.Create(Self);
|
LstFound := TFlatListBox.Create(Self);
|
||||||
LstFound.Parent := PanelRight;
|
LstFound.Parent := PanelRight;
|
||||||
LstFound.SetBounds(PAD, PAD + LabelH, RIGHT_W - PAD * 2, 250);
|
LstFound.SetBounds(PAD, PAD + LabelH, RIGHT_W - PAD * 2, 250);
|
||||||
LstFound.Color := CLR_BG;
|
LstFound.Color := CLR_BG;
|
||||||
@@ -349,14 +349,8 @@ begin
|
|||||||
Font.Color := T.Text;
|
Font.Color := T.Text;
|
||||||
if PanelLeft <> nil then PanelLeft.Color := T.Panel;
|
if PanelLeft <> nil then PanelLeft.Color := T.Panel;
|
||||||
if PanelRight <> nil then PanelRight.Color := T.Panel;
|
if PanelRight <> nil then PanelRight.Color := T.Panel;
|
||||||
if LstSaved <> nil then begin
|
if LstSaved <> nil then LstSaved.SetAppTheme(T);
|
||||||
LstSaved.Color := T.BG;
|
if LstFound <> nil then LstFound.SetAppTheme(T);
|
||||||
LstSaved.Font.Color := T.Text;
|
|
||||||
end;
|
|
||||||
if LstFound <> nil then begin
|
|
||||||
LstFound.Color := T.BG;
|
|
||||||
LstFound.Font.Color := T.Text;
|
|
||||||
end;
|
|
||||||
StyleButton(BtnAdd, False);
|
StyleButton(BtnAdd, False);
|
||||||
StyleButton(BtnRemove, False);
|
StyleButton(BtnRemove, False);
|
||||||
StyleButton(BtnSetAuto, False);
|
StyleButton(BtnSetAuto, False);
|
||||||
@@ -364,17 +358,8 @@ begin
|
|||||||
StyleButton(BtnAddFound, False);
|
StyleButton(BtnAddFound, False);
|
||||||
StyleButton(BtnCancel, False);
|
StyleButton(BtnCancel, False);
|
||||||
StyleConnectButton(BtnConnect);
|
StyleConnectButton(BtnConnect);
|
||||||
// Edit fields: white bg for light theme, dark bg for dark theme
|
if EdName <> nil then EdName.SetAppTheme(T);
|
||||||
if Integer(T.BG) > Integer(TColor($00808080)) then
|
if EdIP <> nil then EdIP.SetAppTheme(T);
|
||||||
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);
|
WalkLabels(Self);
|
||||||
Invalidate;
|
Invalidate;
|
||||||
end;
|
end;
|
||||||
|
|||||||
@@ -0,0 +1,285 @@
|
|||||||
|
unit FlatCheckBox;
|
||||||
|
|
||||||
|
{ Custom checkbox with canvas rendering.
|
||||||
|
Use instead of TCheckBox where native widgetset styling is undesirable. }
|
||||||
|
|
||||||
|
{$mode objfpc}{$H+}
|
||||||
|
|
||||||
|
interface
|
||||||
|
|
||||||
|
uses
|
||||||
|
Classes, SysUtils, Controls, Graphics, Forms, LCLType, LMessages, Types,
|
||||||
|
AppTheme;
|
||||||
|
|
||||||
|
type
|
||||||
|
TFlatCheckBox = class(TCustomControl)
|
||||||
|
private
|
||||||
|
FChecked: Boolean;
|
||||||
|
FHot: Boolean;
|
||||||
|
FDown: Boolean;
|
||||||
|
FOnChange: TNotifyEvent;
|
||||||
|
FClrBG: TColor;
|
||||||
|
FClrBox: TColor;
|
||||||
|
FClrBoxChecked: TColor;
|
||||||
|
FClrBoxHot: TColor;
|
||||||
|
FClrBorder: TColor;
|
||||||
|
FClrBorderHot: TColor;
|
||||||
|
FClrCheck: TColor;
|
||||||
|
FClrText: TColor;
|
||||||
|
FClrTextDisabled: TColor;
|
||||||
|
function DpiScale(V: Integer): Integer;
|
||||||
|
procedure SetChecked(V: Boolean);
|
||||||
|
procedure DoChange;
|
||||||
|
procedure CMTextChanged(var Msg: TLMessage); message CM_TEXTCHANGED;
|
||||||
|
protected
|
||||||
|
procedure Paint; override;
|
||||||
|
procedure MouseEnter; override;
|
||||||
|
procedure MouseLeave; override;
|
||||||
|
procedure MouseDown(Button: TMouseButton; Shift: TShiftState;
|
||||||
|
X, Y: Integer); override;
|
||||||
|
procedure MouseUp(Button: TMouseButton; Shift: TShiftState;
|
||||||
|
X, Y: Integer); override;
|
||||||
|
procedure KeyDown(var Key: Word; Shift: TShiftState); override;
|
||||||
|
public
|
||||||
|
constructor Create(AOwner: TComponent); override;
|
||||||
|
procedure SetAppTheme(const T: TAppTheme);
|
||||||
|
|
||||||
|
property Checked: Boolean read FChecked write SetChecked;
|
||||||
|
property OnChange: TNotifyEvent read FOnChange write FOnChange;
|
||||||
|
property Align;
|
||||||
|
property Anchors;
|
||||||
|
property Caption;
|
||||||
|
property Enabled;
|
||||||
|
property Font;
|
||||||
|
property ParentShowHint;
|
||||||
|
property PopupMenu;
|
||||||
|
property ShowHint;
|
||||||
|
property TabOrder;
|
||||||
|
property TabStop;
|
||||||
|
property Tag;
|
||||||
|
property Visible;
|
||||||
|
property OnClick;
|
||||||
|
end;
|
||||||
|
|
||||||
|
implementation
|
||||||
|
|
||||||
|
const
|
||||||
|
BASE_BOX_SIZE = 16;
|
||||||
|
BASE_BOX_LEFT = 1;
|
||||||
|
BASE_TEXT_GAP = 8;
|
||||||
|
BASE_CORNER = 2;
|
||||||
|
|
||||||
|
constructor TFlatCheckBox.Create(AOwner: TComponent);
|
||||||
|
begin
|
||||||
|
inherited Create(AOwner);
|
||||||
|
ControlStyle := ControlStyle + [csOpaque, csCaptureMouse, csClickEvents];
|
||||||
|
Width := 160;
|
||||||
|
Height := 22;
|
||||||
|
TabStop := True;
|
||||||
|
Cursor := crHandPoint;
|
||||||
|
|
||||||
|
Font.Name := 'Courier New';
|
||||||
|
Font.Size := 9;
|
||||||
|
|
||||||
|
FClrBG := TColor($00181818);
|
||||||
|
FClrBox := TColor($001A1A1A);
|
||||||
|
FClrBoxChecked := TColor($000C3010);
|
||||||
|
FClrBoxHot := TColor($00282828);
|
||||||
|
FClrBorder := TColor($00444444);
|
||||||
|
FClrBorderHot := TColor($00387838);
|
||||||
|
FClrCheck := TColor($0000FF88);
|
||||||
|
FClrText := TColor($00CCCCCC);
|
||||||
|
FClrTextDisabled := TColor($00666666);
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TFlatCheckBox.DpiScale(V: Integer): Integer;
|
||||||
|
begin
|
||||||
|
Result := MulDiv(V, Screen.PixelsPerInch, 96);
|
||||||
|
if (V > 0) and (Result < 1) then
|
||||||
|
Result := 1;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatCheckBox.SetAppTheme(const T: TAppTheme);
|
||||||
|
begin
|
||||||
|
FClrBG := T.Panel;
|
||||||
|
FClrBox := T.BtnNorm;
|
||||||
|
FClrBoxChecked := T.BtnActive;
|
||||||
|
FClrBoxHot := T.BtnHot;
|
||||||
|
FClrBorder := T.BtnBorderNorm;
|
||||||
|
FClrBorderHot := T.BtnBorderActive;
|
||||||
|
FClrCheck := T.BtnTextActive;
|
||||||
|
FClrText := T.Text;
|
||||||
|
FClrTextDisabled := T.TextDim;
|
||||||
|
Font.Color := T.Text;
|
||||||
|
Invalidate;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatCheckBox.SetChecked(V: Boolean);
|
||||||
|
begin
|
||||||
|
if FChecked = V then Exit;
|
||||||
|
FChecked := V;
|
||||||
|
Invalidate;
|
||||||
|
DoChange;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatCheckBox.DoChange;
|
||||||
|
begin
|
||||||
|
if Assigned(FOnChange) then
|
||||||
|
FOnChange(Self);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatCheckBox.CMTextChanged(var Msg: TLMessage);
|
||||||
|
begin
|
||||||
|
Invalidate;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatCheckBox.Paint;
|
||||||
|
var
|
||||||
|
BoxSize, BoxLeft, BoxTop, TextGap, TextTop, TX, Corner: Integer;
|
||||||
|
R: TRect;
|
||||||
|
BoxColor, BorderColor, TextColor: TColor;
|
||||||
|
|
||||||
|
procedure DrawCheckMark(const AR: TRect);
|
||||||
|
var
|
||||||
|
P: array[0..6] of TPoint;
|
||||||
|
W, H: Integer;
|
||||||
|
|
||||||
|
function PX(V: Integer): Integer;
|
||||||
|
begin
|
||||||
|
Result := AR.Left + MulDiv(V, W, BASE_BOX_SIZE);
|
||||||
|
end;
|
||||||
|
|
||||||
|
function PY(V: Integer): Integer;
|
||||||
|
begin
|
||||||
|
Result := AR.Top + MulDiv(V, H, BASE_BOX_SIZE);
|
||||||
|
end;
|
||||||
|
begin
|
||||||
|
W := AR.Right - AR.Left;
|
||||||
|
H := AR.Bottom - AR.Top;
|
||||||
|
P[0] := Point(PX(4), PY(8));
|
||||||
|
P[1] := Point(PX(6), PY(8));
|
||||||
|
P[2] := Point(PX(7), PY(10));
|
||||||
|
P[3] := Point(PX(12), PY(4));
|
||||||
|
P[4] := Point(PX(13), PY(6));
|
||||||
|
P[5] := Point(PX(8), PY(12));
|
||||||
|
P[6] := Point(PX(6), PY(12));
|
||||||
|
|
||||||
|
Canvas.Brush.Color := FClrCheck;
|
||||||
|
Canvas.Brush.Style := bsSolid;
|
||||||
|
Canvas.Pen.Style := psClear;
|
||||||
|
Canvas.Polygon(P);
|
||||||
|
end;
|
||||||
|
begin
|
||||||
|
Canvas.Brush.Color := FClrBG;
|
||||||
|
Canvas.Brush.Style := bsSolid;
|
||||||
|
Canvas.Pen.Style := psClear;
|
||||||
|
Canvas.FillRect(ClientRect);
|
||||||
|
|
||||||
|
BoxSize := DpiScale(BASE_BOX_SIZE);
|
||||||
|
if BoxSize > Height - DpiScale(4) then
|
||||||
|
BoxSize := Height - DpiScale(4);
|
||||||
|
if BoxSize < DpiScale(10) then
|
||||||
|
BoxSize := DpiScale(10);
|
||||||
|
BoxLeft := DpiScale(BASE_BOX_LEFT);
|
||||||
|
BoxTop := (Height - BoxSize) div 2;
|
||||||
|
TextGap := DpiScale(BASE_TEXT_GAP);
|
||||||
|
Corner := DpiScale(BASE_CORNER);
|
||||||
|
R := Rect(BoxLeft, BoxTop, BoxLeft + BoxSize, BoxTop + BoxSize);
|
||||||
|
|
||||||
|
if FChecked then
|
||||||
|
BoxColor := FClrBoxChecked
|
||||||
|
else if FHot or Focused then
|
||||||
|
BoxColor := FClrBoxHot
|
||||||
|
else
|
||||||
|
BoxColor := FClrBox;
|
||||||
|
|
||||||
|
if FHot or Focused then
|
||||||
|
begin
|
||||||
|
BorderColor := FClrBorderHot;
|
||||||
|
end
|
||||||
|
else
|
||||||
|
BorderColor := FClrBorder;
|
||||||
|
|
||||||
|
if not Enabled then
|
||||||
|
begin
|
||||||
|
BoxColor := FClrBG;
|
||||||
|
BorderColor := FClrBorder;
|
||||||
|
TextColor := FClrTextDisabled;
|
||||||
|
end
|
||||||
|
else
|
||||||
|
TextColor := FClrText;
|
||||||
|
|
||||||
|
Canvas.Brush.Color := BoxColor;
|
||||||
|
Canvas.Brush.Style := bsSolid;
|
||||||
|
Canvas.Pen.Color := BorderColor;
|
||||||
|
Canvas.Pen.Style := psSolid;
|
||||||
|
Canvas.Pen.Width := 1;
|
||||||
|
Canvas.RoundRect(R.Left, R.Top, R.Right - 1, R.Bottom - 1, CORNER * 2, CORNER * 2);
|
||||||
|
|
||||||
|
if FChecked then
|
||||||
|
DrawCheckMark(R);
|
||||||
|
|
||||||
|
TX := R.Right + TextGap;
|
||||||
|
TextTop := (Height - Canvas.TextHeight('Ag')) div 2;
|
||||||
|
Canvas.Brush.Style := bsClear;
|
||||||
|
Canvas.Font.Assign(Font);
|
||||||
|
Canvas.Font.Color := TextColor;
|
||||||
|
Canvas.TextOut(TX, TextTop, Caption);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatCheckBox.MouseEnter;
|
||||||
|
begin
|
||||||
|
inherited;
|
||||||
|
FHot := True;
|
||||||
|
Invalidate;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatCheckBox.MouseLeave;
|
||||||
|
begin
|
||||||
|
inherited;
|
||||||
|
FHot := False;
|
||||||
|
FDown := False;
|
||||||
|
Invalidate;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatCheckBox.MouseDown(Button: TMouseButton; Shift: TShiftState;
|
||||||
|
X, Y: Integer);
|
||||||
|
begin
|
||||||
|
inherited;
|
||||||
|
if (Button = mbLeft) and Enabled then
|
||||||
|
begin
|
||||||
|
SetFocus;
|
||||||
|
FDown := True;
|
||||||
|
Invalidate;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatCheckBox.MouseUp(Button: TMouseButton; Shift: TShiftState;
|
||||||
|
X, Y: Integer);
|
||||||
|
begin
|
||||||
|
inherited;
|
||||||
|
if (Button = mbLeft) and FDown and Enabled then
|
||||||
|
begin
|
||||||
|
FDown := False;
|
||||||
|
if PtInRect(ClientRect, Point(X, Y)) then
|
||||||
|
begin
|
||||||
|
Checked := not Checked;
|
||||||
|
Click;
|
||||||
|
end
|
||||||
|
else
|
||||||
|
Invalidate;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatCheckBox.KeyDown(var Key: Word; Shift: TShiftState);
|
||||||
|
begin
|
||||||
|
inherited;
|
||||||
|
if Enabled and ((Key = VK_SPACE) or (Key = VK_RETURN)) then
|
||||||
|
begin
|
||||||
|
Checked := not Checked;
|
||||||
|
Click;
|
||||||
|
Key := 0;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
end.
|
||||||
@@ -0,0 +1,532 @@
|
|||||||
|
unit FlatComboBox;
|
||||||
|
|
||||||
|
{ Custom drop-down list with canvas rendering.
|
||||||
|
Use instead of TComboBox where native widgetset styling is undesirable. }
|
||||||
|
|
||||||
|
{$mode objfpc}{$H+}
|
||||||
|
|
||||||
|
interface
|
||||||
|
|
||||||
|
uses
|
||||||
|
Classes, SysUtils, Controls, Graphics, Forms, LCLType, LMessages, Types,
|
||||||
|
Math, AppTheme;
|
||||||
|
|
||||||
|
type
|
||||||
|
TFlatComboBox = class;
|
||||||
|
|
||||||
|
TFlatComboPopup = class(TCustomControl)
|
||||||
|
private
|
||||||
|
FCombo: TFlatComboBox;
|
||||||
|
FHotIndex: Integer;
|
||||||
|
FTopIndex: Integer;
|
||||||
|
function ItemAt(Y: Integer): Integer;
|
||||||
|
procedure EnsureHotVisible;
|
||||||
|
protected
|
||||||
|
procedure Paint; override;
|
||||||
|
procedure MouseMove(Shift: TShiftState; X, Y: Integer); override;
|
||||||
|
procedure MouseDown(Button: TMouseButton; Shift: TShiftState;
|
||||||
|
X, Y: Integer); override;
|
||||||
|
procedure DoExit; override;
|
||||||
|
procedure KeyDown(var Key: Word; Shift: TShiftState); override;
|
||||||
|
function DoMouseWheelUp(Shift: TShiftState; MousePos: TPoint): Boolean; override;
|
||||||
|
function DoMouseWheelDown(Shift: TShiftState; MousePos: TPoint): Boolean; override;
|
||||||
|
public
|
||||||
|
constructor CreatePopup(AOwner: TComponent; ACombo: TFlatComboBox); reintroduce;
|
||||||
|
end;
|
||||||
|
|
||||||
|
TFlatComboBox = class(TCustomControl)
|
||||||
|
private
|
||||||
|
FItems: TStringList;
|
||||||
|
FItemIndex: Integer;
|
||||||
|
FHot: Boolean;
|
||||||
|
FDown: Boolean;
|
||||||
|
FPopup: TFlatComboPopup;
|
||||||
|
FOnChange: TNotifyEvent;
|
||||||
|
FClrOuterBG: TColor;
|
||||||
|
FClrBG: TColor;
|
||||||
|
FClrBGHot: TColor;
|
||||||
|
FClrBorder: TColor;
|
||||||
|
FClrBorderHot: TColor;
|
||||||
|
FClrText: TColor;
|
||||||
|
FClrTextDim: TColor;
|
||||||
|
FClrPopupBG: TColor;
|
||||||
|
FClrPopupHot: TColor;
|
||||||
|
FClrPopupSel: TColor;
|
||||||
|
function DpiScale(V: Integer): Integer;
|
||||||
|
function ItemHeight: Integer;
|
||||||
|
function ArrowWidth: Integer;
|
||||||
|
procedure ItemsChanged(Sender: TObject);
|
||||||
|
procedure SetItemIndex(V: Integer);
|
||||||
|
function GetText: string;
|
||||||
|
procedure SetText(const V: string);
|
||||||
|
procedure DoChange;
|
||||||
|
procedure ClosePopup;
|
||||||
|
procedure CMTextChanged(var Msg: TLMessage); message CM_TEXTCHANGED;
|
||||||
|
protected
|
||||||
|
procedure Paint; override;
|
||||||
|
procedure MouseEnter; override;
|
||||||
|
procedure MouseLeave; override;
|
||||||
|
procedure MouseDown(Button: TMouseButton; Shift: TShiftState;
|
||||||
|
X, Y: Integer); override;
|
||||||
|
procedure MouseUp(Button: TMouseButton; Shift: TShiftState;
|
||||||
|
X, Y: Integer); override;
|
||||||
|
procedure KeyDown(var Key: Word; Shift: TShiftState); override;
|
||||||
|
public
|
||||||
|
constructor Create(AOwner: TComponent); override;
|
||||||
|
destructor Destroy; override;
|
||||||
|
procedure SetAppTheme(const T: TAppTheme);
|
||||||
|
procedure DropDown;
|
||||||
|
|
||||||
|
property Items: TStringList read FItems;
|
||||||
|
property ItemIndex: Integer read FItemIndex write SetItemIndex;
|
||||||
|
property Text: string read GetText write SetText;
|
||||||
|
property OnChange: TNotifyEvent read FOnChange write FOnChange;
|
||||||
|
property Align;
|
||||||
|
property Anchors;
|
||||||
|
property Enabled;
|
||||||
|
property Font;
|
||||||
|
property ParentShowHint;
|
||||||
|
property PopupMenu;
|
||||||
|
property ShowHint;
|
||||||
|
property TabOrder;
|
||||||
|
property TabStop;
|
||||||
|
property Tag;
|
||||||
|
property Visible;
|
||||||
|
end;
|
||||||
|
|
||||||
|
implementation
|
||||||
|
|
||||||
|
const
|
||||||
|
BASE_ITEM_H = 24;
|
||||||
|
BASE_ARROW_W = 24;
|
||||||
|
BASE_PAD = 8;
|
||||||
|
|
||||||
|
{ TFlatComboPopup }
|
||||||
|
|
||||||
|
constructor TFlatComboPopup.CreatePopup(AOwner: TComponent; ACombo: TFlatComboBox);
|
||||||
|
begin
|
||||||
|
inherited Create(AOwner);
|
||||||
|
FCombo := ACombo;
|
||||||
|
FHotIndex := ACombo.ItemIndex;
|
||||||
|
FTopIndex := Max(0, FHotIndex - 4);
|
||||||
|
TabStop := True;
|
||||||
|
Cursor := crDefault;
|
||||||
|
Color := ACombo.FClrPopupBG;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TFlatComboPopup.ItemAt(Y: Integer): Integer;
|
||||||
|
begin
|
||||||
|
Result := FTopIndex + Y div FCombo.ItemHeight;
|
||||||
|
if (Result < 0) or (Result >= FCombo.Items.Count) then
|
||||||
|
Result := -1;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatComboPopup.EnsureHotVisible;
|
||||||
|
var
|
||||||
|
VisibleCount: Integer;
|
||||||
|
begin
|
||||||
|
VisibleCount := Max(1, Height div FCombo.ItemHeight);
|
||||||
|
if FHotIndex < FTopIndex then
|
||||||
|
FTopIndex := FHotIndex
|
||||||
|
else if FHotIndex >= FTopIndex + VisibleCount then
|
||||||
|
FTopIndex := FHotIndex - VisibleCount + 1;
|
||||||
|
FTopIndex := EnsureRange(FTopIndex, 0, Max(0, FCombo.Items.Count - VisibleCount));
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatComboPopup.Paint;
|
||||||
|
var
|
||||||
|
i, ItemIdx, Y, TextY, VisibleCount: Integer;
|
||||||
|
R: TRect;
|
||||||
|
begin
|
||||||
|
Canvas.Brush.Color := FCombo.FClrPopupBG;
|
||||||
|
Canvas.Brush.Style := bsSolid;
|
||||||
|
Canvas.Pen.Style := psClear;
|
||||||
|
Canvas.FillRect(ClientRect);
|
||||||
|
|
||||||
|
Canvas.Font.Assign(FCombo.Font);
|
||||||
|
VisibleCount := Max(1, Height div FCombo.ItemHeight);
|
||||||
|
for i := 0 to VisibleCount - 1 do
|
||||||
|
begin
|
||||||
|
ItemIdx := FTopIndex + i;
|
||||||
|
if ItemIdx >= FCombo.Items.Count then Break;
|
||||||
|
Y := i * FCombo.ItemHeight;
|
||||||
|
R := Rect(0, Y, Width, Y + FCombo.ItemHeight);
|
||||||
|
if ItemIdx = FCombo.ItemIndex then
|
||||||
|
Canvas.Brush.Color := FCombo.FClrPopupSel
|
||||||
|
else if ItemIdx = FHotIndex then
|
||||||
|
Canvas.Brush.Color := FCombo.FClrPopupHot
|
||||||
|
else
|
||||||
|
Canvas.Brush.Color := FCombo.FClrPopupBG;
|
||||||
|
Canvas.FillRect(R);
|
||||||
|
|
||||||
|
Canvas.Brush.Style := bsClear;
|
||||||
|
Canvas.Font.Color := FCombo.FClrText;
|
||||||
|
TextY := Y + (FCombo.ItemHeight - Canvas.TextHeight('Ag')) div 2;
|
||||||
|
Canvas.TextOut(FCombo.DpiScale(BASE_PAD), TextY, FCombo.Items[ItemIdx]);
|
||||||
|
Canvas.Brush.Style := bsSolid;
|
||||||
|
end;
|
||||||
|
|
||||||
|
Canvas.Pen.Color := FCombo.FClrBorder;
|
||||||
|
Canvas.Pen.Style := psSolid;
|
||||||
|
Canvas.Brush.Style := bsClear;
|
||||||
|
Canvas.Rectangle(0, 0, Width, Height);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatComboPopup.MouseMove(Shift: TShiftState; X, Y: Integer);
|
||||||
|
var
|
||||||
|
Idx: Integer;
|
||||||
|
begin
|
||||||
|
inherited;
|
||||||
|
if PtInRect(ClientRect, Point(X, Y)) then
|
||||||
|
Idx := ItemAt(Y)
|
||||||
|
else
|
||||||
|
Idx := -1;
|
||||||
|
if FHotIndex = Idx then Exit;
|
||||||
|
FHotIndex := Idx;
|
||||||
|
Invalidate;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatComboPopup.MouseDown(Button: TMouseButton; Shift: TShiftState;
|
||||||
|
X, Y: Integer);
|
||||||
|
var
|
||||||
|
Idx: Integer;
|
||||||
|
begin
|
||||||
|
inherited;
|
||||||
|
if Button <> mbLeft then Exit;
|
||||||
|
if PtInRect(ClientRect, Point(X, Y)) then
|
||||||
|
begin
|
||||||
|
Idx := ItemAt(Y);
|
||||||
|
if Idx >= 0 then
|
||||||
|
FCombo.ItemIndex := Idx;
|
||||||
|
end;
|
||||||
|
FCombo.ClosePopup;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatComboPopup.DoExit;
|
||||||
|
begin
|
||||||
|
inherited;
|
||||||
|
if Assigned(FCombo) then
|
||||||
|
FCombo.ClosePopup;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatComboPopup.KeyDown(var Key: Word; Shift: TShiftState);
|
||||||
|
begin
|
||||||
|
inherited;
|
||||||
|
case Key of
|
||||||
|
VK_ESCAPE:
|
||||||
|
begin
|
||||||
|
FCombo.ClosePopup;
|
||||||
|
Key := 0;
|
||||||
|
end;
|
||||||
|
VK_RETURN:
|
||||||
|
begin
|
||||||
|
if FHotIndex >= 0 then
|
||||||
|
FCombo.ItemIndex := FHotIndex;
|
||||||
|
FCombo.ClosePopup;
|
||||||
|
Key := 0;
|
||||||
|
end;
|
||||||
|
VK_UP:
|
||||||
|
begin
|
||||||
|
FHotIndex := EnsureRange(FHotIndex - 1, 0, FCombo.Items.Count - 1);
|
||||||
|
EnsureHotVisible;
|
||||||
|
Invalidate;
|
||||||
|
Key := 0;
|
||||||
|
end;
|
||||||
|
VK_DOWN:
|
||||||
|
begin
|
||||||
|
FHotIndex := EnsureRange(FHotIndex + 1, 0, FCombo.Items.Count - 1);
|
||||||
|
EnsureHotVisible;
|
||||||
|
Invalidate;
|
||||||
|
Key := 0;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TFlatComboPopup.DoMouseWheelUp(Shift: TShiftState; MousePos: TPoint): Boolean;
|
||||||
|
begin
|
||||||
|
FTopIndex := Max(0, FTopIndex - 1);
|
||||||
|
Invalidate;
|
||||||
|
Result := True;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TFlatComboPopup.DoMouseWheelDown(Shift: TShiftState; MousePos: TPoint): Boolean;
|
||||||
|
var
|
||||||
|
VisibleCount: Integer;
|
||||||
|
begin
|
||||||
|
VisibleCount := Max(1, Height div FCombo.ItemHeight);
|
||||||
|
FTopIndex := Min(Max(0, FCombo.Items.Count - VisibleCount), FTopIndex + 1);
|
||||||
|
Invalidate;
|
||||||
|
Result := True;
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ TFlatComboBox }
|
||||||
|
|
||||||
|
constructor TFlatComboBox.Create(AOwner: TComponent);
|
||||||
|
begin
|
||||||
|
inherited Create(AOwner);
|
||||||
|
ControlStyle := ControlStyle + [csOpaque, csCaptureMouse, csClickEvents];
|
||||||
|
FItems := TStringList.Create;
|
||||||
|
FItems.OnChange := @ItemsChanged;
|
||||||
|
FItemIndex := -1;
|
||||||
|
Width := 140;
|
||||||
|
Height := DpiScale(26);
|
||||||
|
TabStop := True;
|
||||||
|
Cursor := crDefault;
|
||||||
|
|
||||||
|
Font.Name := 'Courier New';
|
||||||
|
Font.Size := 9;
|
||||||
|
|
||||||
|
FClrBG := TColor($001A1A1A);
|
||||||
|
FClrOuterBG := TColor($00181818);
|
||||||
|
FClrBGHot := TColor($00282828);
|
||||||
|
FClrBorder := TColor($00444444);
|
||||||
|
FClrBorderHot := TColor($00387838);
|
||||||
|
FClrText := TColor($00E0E0E0);
|
||||||
|
FClrTextDim := TColor($00888888);
|
||||||
|
FClrPopupBG := TColor($00181818);
|
||||||
|
FClrPopupHot := TColor($000C3010);
|
||||||
|
FClrPopupSel := TColor($00183618);
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TFlatComboBox.DpiScale(V: Integer): Integer;
|
||||||
|
begin
|
||||||
|
Result := MulDiv(V, Screen.PixelsPerInch, 96);
|
||||||
|
if (V > 0) and (Result < 1) then
|
||||||
|
Result := 1;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TFlatComboBox.ItemHeight: Integer;
|
||||||
|
begin
|
||||||
|
Result := DpiScale(BASE_ITEM_H);
|
||||||
|
if Result < Canvas.TextHeight('Ag') + DpiScale(8) then
|
||||||
|
Result := Canvas.TextHeight('Ag') + DpiScale(8);
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TFlatComboBox.ArrowWidth: Integer;
|
||||||
|
begin
|
||||||
|
Result := DpiScale(BASE_ARROW_W);
|
||||||
|
end;
|
||||||
|
|
||||||
|
destructor TFlatComboBox.Destroy;
|
||||||
|
begin
|
||||||
|
ClosePopup;
|
||||||
|
FItems.Free;
|
||||||
|
inherited Destroy;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatComboBox.SetAppTheme(const T: TAppTheme);
|
||||||
|
begin
|
||||||
|
FClrBG := T.BtnNorm;
|
||||||
|
FClrOuterBG := T.Panel;
|
||||||
|
FClrBGHot := T.BtnHot;
|
||||||
|
FClrBorder := T.Border;
|
||||||
|
FClrBorderHot := T.Border;
|
||||||
|
FClrText := T.Text;
|
||||||
|
FClrTextDim := T.TextDim;
|
||||||
|
FClrPopupBG := T.Panel;
|
||||||
|
FClrPopupHot := T.BtnActive;
|
||||||
|
FClrPopupSel := T.SliderTrackFill;
|
||||||
|
Font.Color := T.Text;
|
||||||
|
Invalidate;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatComboBox.ItemsChanged(Sender: TObject);
|
||||||
|
begin
|
||||||
|
if FItemIndex >= FItems.Count then
|
||||||
|
FItemIndex := FItems.Count - 1;
|
||||||
|
if FItems.Count = 0 then
|
||||||
|
FItemIndex := -1;
|
||||||
|
Invalidate;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatComboBox.SetItemIndex(V: Integer);
|
||||||
|
begin
|
||||||
|
V := EnsureRange(V, -1, FItems.Count - 1);
|
||||||
|
if FItemIndex = V then Exit;
|
||||||
|
FItemIndex := V;
|
||||||
|
Invalidate;
|
||||||
|
DoChange;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TFlatComboBox.GetText: string;
|
||||||
|
begin
|
||||||
|
if (FItemIndex >= 0) and (FItemIndex < FItems.Count) then
|
||||||
|
Result := FItems[FItemIndex]
|
||||||
|
else
|
||||||
|
Result := '';
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatComboBox.SetText(const V: string);
|
||||||
|
var
|
||||||
|
Idx: Integer;
|
||||||
|
begin
|
||||||
|
Idx := FItems.IndexOf(V);
|
||||||
|
if Idx >= 0 then
|
||||||
|
ItemIndex := Idx
|
||||||
|
else
|
||||||
|
begin
|
||||||
|
FItems.Add(V);
|
||||||
|
ItemIndex := FItems.Count - 1;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatComboBox.DoChange;
|
||||||
|
begin
|
||||||
|
if Assigned(FOnChange) then
|
||||||
|
FOnChange(Self);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatComboBox.ClosePopup;
|
||||||
|
begin
|
||||||
|
if FPopup = nil then Exit;
|
||||||
|
FPopup.MouseCapture := False;
|
||||||
|
FreeAndNil(FPopup);
|
||||||
|
FDown := False;
|
||||||
|
Invalidate;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatComboBox.DropDown;
|
||||||
|
var
|
||||||
|
Root: TCustomForm;
|
||||||
|
P: TPoint;
|
||||||
|
H, IH: Integer;
|
||||||
|
begin
|
||||||
|
if (FPopup <> nil) or (FItems.Count = 0) then Exit;
|
||||||
|
Root := GetParentForm(Self);
|
||||||
|
if Root = nil then Exit;
|
||||||
|
P := Root.ScreenToClient(ClientToScreen(Point(0, Height)));
|
||||||
|
IH := ItemHeight;
|
||||||
|
H := Min(FItems.Count * IH + 1, IH * 10 + 1);
|
||||||
|
if P.Y + H > Root.ClientHeight then
|
||||||
|
P.Y := Root.ScreenToClient(ClientToScreen(Point(0, 0))).Y - H;
|
||||||
|
if P.Y < 0 then
|
||||||
|
P.Y := 0;
|
||||||
|
|
||||||
|
FPopup := TFlatComboPopup.CreatePopup(Root, Self);
|
||||||
|
FPopup.Parent := Root;
|
||||||
|
FPopup.SetBounds(P.X, P.Y, Width, H);
|
||||||
|
FPopup.BringToFront;
|
||||||
|
FPopup.Visible := True;
|
||||||
|
FPopup.SetFocus;
|
||||||
|
FPopup.MouseCapture := True;
|
||||||
|
FDown := True;
|
||||||
|
Invalidate;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatComboBox.CMTextChanged(var Msg: TLMessage);
|
||||||
|
begin
|
||||||
|
Invalidate;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatComboBox.Paint;
|
||||||
|
var
|
||||||
|
R: TRect;
|
||||||
|
TextY, MidY, X, Pad, AW, Chevron: Integer;
|
||||||
|
BG, Bdr: TColor;
|
||||||
|
S: string;
|
||||||
|
begin
|
||||||
|
R := ClientRect;
|
||||||
|
if FHot or FDown or Focused then
|
||||||
|
begin
|
||||||
|
BG := FClrBGHot;
|
||||||
|
Bdr := FClrBorderHot;
|
||||||
|
end
|
||||||
|
else
|
||||||
|
begin
|
||||||
|
BG := FClrBG;
|
||||||
|
Bdr := FClrBorder;
|
||||||
|
end;
|
||||||
|
|
||||||
|
Canvas.Brush.Color := FClrOuterBG;
|
||||||
|
Canvas.Brush.Style := bsSolid;
|
||||||
|
Canvas.Pen.Style := psClear;
|
||||||
|
Canvas.FillRect(R);
|
||||||
|
|
||||||
|
Canvas.Brush.Color := BG;
|
||||||
|
Canvas.Brush.Style := bsSolid;
|
||||||
|
Canvas.Pen.Color := Bdr;
|
||||||
|
Canvas.Pen.Style := psSolid;
|
||||||
|
Canvas.Pen.Width := 1;
|
||||||
|
Canvas.Rectangle(R.Left, R.Top, R.Right, R.Bottom);
|
||||||
|
|
||||||
|
Pad := DpiScale(BASE_PAD);
|
||||||
|
AW := ArrowWidth;
|
||||||
|
Canvas.Font.Assign(Font);
|
||||||
|
if Enabled then
|
||||||
|
Canvas.Font.Color := FClrText
|
||||||
|
else
|
||||||
|
Canvas.Font.Color := FClrTextDim;
|
||||||
|
Canvas.Brush.Style := bsClear;
|
||||||
|
S := Text;
|
||||||
|
TextY := (Height - Canvas.TextHeight('Ag')) div 2;
|
||||||
|
Canvas.TextRect(Rect(Pad, 0, Width - AW - DpiScale(4), Height), Pad, TextY, S);
|
||||||
|
|
||||||
|
X := Width - AW div 2;
|
||||||
|
MidY := Height div 2;
|
||||||
|
Chevron := DpiScale(4);
|
||||||
|
Canvas.Pen.Color := FClrTextDim;
|
||||||
|
Canvas.Pen.Style := psSolid;
|
||||||
|
Canvas.Pen.Width := Max(1, DpiScale(2));
|
||||||
|
Canvas.Line(X - Chevron, MidY - Chevron div 2, X, MidY + Chevron div 2);
|
||||||
|
Canvas.Line(X, MidY + Chevron div 2, X + Chevron, MidY - Chevron div 2);
|
||||||
|
Canvas.Pen.Width := 1;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatComboBox.MouseEnter;
|
||||||
|
begin
|
||||||
|
inherited;
|
||||||
|
FHot := True;
|
||||||
|
Invalidate;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatComboBox.MouseLeave;
|
||||||
|
begin
|
||||||
|
inherited;
|
||||||
|
FHot := False;
|
||||||
|
Invalidate;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatComboBox.MouseDown(Button: TMouseButton; Shift: TShiftState;
|
||||||
|
X, Y: Integer);
|
||||||
|
begin
|
||||||
|
inherited;
|
||||||
|
if (Button = mbLeft) and Enabled then
|
||||||
|
SetFocus;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatComboBox.MouseUp(Button: TMouseButton; Shift: TShiftState;
|
||||||
|
X, Y: Integer);
|
||||||
|
begin
|
||||||
|
inherited;
|
||||||
|
if (Button = mbLeft) and Enabled and PtInRect(ClientRect, Point(X, Y)) then
|
||||||
|
begin
|
||||||
|
if FPopup <> nil then
|
||||||
|
ClosePopup
|
||||||
|
else
|
||||||
|
DropDown;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatComboBox.KeyDown(var Key: Word; Shift: TShiftState);
|
||||||
|
begin
|
||||||
|
inherited;
|
||||||
|
case Key of
|
||||||
|
VK_SPACE, VK_RETURN:
|
||||||
|
begin
|
||||||
|
DropDown;
|
||||||
|
Key := 0;
|
||||||
|
end;
|
||||||
|
VK_UP:
|
||||||
|
begin
|
||||||
|
ItemIndex := FItemIndex - 1;
|
||||||
|
Key := 0;
|
||||||
|
end;
|
||||||
|
VK_DOWN:
|
||||||
|
begin
|
||||||
|
ItemIndex := FItemIndex + 1;
|
||||||
|
Key := 0;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
end.
|
||||||
+586
@@ -0,0 +1,586 @@
|
|||||||
|
unit FlatEdit;
|
||||||
|
|
||||||
|
{ Custom single-line edit with canvas rendering.
|
||||||
|
Use instead of TEdit where native widgetset styling is undesirable. }
|
||||||
|
|
||||||
|
{$mode objfpc}{$H+}
|
||||||
|
|
||||||
|
interface
|
||||||
|
|
||||||
|
uses
|
||||||
|
Classes, SysUtils, Controls, Graphics, Forms, LCLType, LMessages, Types,
|
||||||
|
Clipbrd, Math, LazUTF8, AppTheme;
|
||||||
|
|
||||||
|
type
|
||||||
|
TFlatEdit = class(TCustomControl)
|
||||||
|
private
|
||||||
|
FText: string;
|
||||||
|
FTextHint: string;
|
||||||
|
FPasswordChar: Char;
|
||||||
|
FMaxLength: Integer;
|
||||||
|
FCaretPos: Integer;
|
||||||
|
FSelStart: Integer;
|
||||||
|
FSelLen: Integer;
|
||||||
|
FHot: Boolean;
|
||||||
|
FEditing: Boolean;
|
||||||
|
FDragging: Boolean;
|
||||||
|
FOnChange: TNotifyEvent;
|
||||||
|
FClrOuterBG: TColor;
|
||||||
|
FClrBG: TColor;
|
||||||
|
FClrBGHot: TColor;
|
||||||
|
FClrBorder: TColor;
|
||||||
|
FClrText: TColor;
|
||||||
|
FClrTextDim: TColor;
|
||||||
|
FClrSelBG: TColor;
|
||||||
|
FClrSelText: TColor;
|
||||||
|
function DpiScale(V: Integer): Integer;
|
||||||
|
function DisplayText: string;
|
||||||
|
function TextLen: Integer;
|
||||||
|
function HasSelection: Boolean;
|
||||||
|
function SelFirst: Integer;
|
||||||
|
function SelAfter: Integer;
|
||||||
|
function CaretPosAtX(X: Integer): Integer;
|
||||||
|
procedure SetText(const V: string);
|
||||||
|
procedure SetTextHint(const V: string);
|
||||||
|
procedure SetPasswordChar(V: Char);
|
||||||
|
procedure SetMaxLength(V: Integer);
|
||||||
|
procedure SetCaretPos(V: Integer);
|
||||||
|
procedure DeleteSelection;
|
||||||
|
procedure InsertText(const S: string);
|
||||||
|
procedure DoChange;
|
||||||
|
procedure AppUserInput(Sender: TObject; Msg: Cardinal);
|
||||||
|
procedure CMTextChanged(var Msg: TLMessage); message CM_TEXTCHANGED;
|
||||||
|
protected
|
||||||
|
procedure Paint; override;
|
||||||
|
procedure MouseEnter; override;
|
||||||
|
procedure MouseLeave; override;
|
||||||
|
procedure MouseDown(Button: TMouseButton; Shift: TShiftState;
|
||||||
|
X, Y: Integer); override;
|
||||||
|
procedure MouseMove(Shift: TShiftState; X, Y: Integer); override;
|
||||||
|
procedure MouseUp(Button: TMouseButton; Shift: TShiftState;
|
||||||
|
X, Y: Integer); override;
|
||||||
|
procedure DblClick; override;
|
||||||
|
procedure KeyDown(var Key: Word; Shift: TShiftState); override;
|
||||||
|
procedure KeyPress(var Key: char); override;
|
||||||
|
procedure UTF8KeyPress(var UTF8Key: TUTF8Char); override;
|
||||||
|
procedure DoEnter; override;
|
||||||
|
procedure DoExit; override;
|
||||||
|
public
|
||||||
|
constructor Create(AOwner: TComponent); override;
|
||||||
|
destructor Destroy; override;
|
||||||
|
procedure SetAppTheme(const T: TAppTheme);
|
||||||
|
procedure SelectAll;
|
||||||
|
|
||||||
|
property Text: string read FText write SetText;
|
||||||
|
property TextHint: string read FTextHint write SetTextHint;
|
||||||
|
property PasswordChar: Char read FPasswordChar write SetPasswordChar;
|
||||||
|
property MaxLength: Integer read FMaxLength write SetMaxLength;
|
||||||
|
property OnChange: TNotifyEvent read FOnChange write FOnChange;
|
||||||
|
property Align;
|
||||||
|
property Anchors;
|
||||||
|
property Enabled;
|
||||||
|
property Font;
|
||||||
|
property ParentShowHint;
|
||||||
|
property PopupMenu;
|
||||||
|
property ShowHint;
|
||||||
|
property TabOrder;
|
||||||
|
property TabStop;
|
||||||
|
property Tag;
|
||||||
|
property Visible;
|
||||||
|
property OnEnter;
|
||||||
|
property OnExit;
|
||||||
|
property OnKeyDown;
|
||||||
|
property OnKeyPress;
|
||||||
|
end;
|
||||||
|
|
||||||
|
implementation
|
||||||
|
|
||||||
|
const
|
||||||
|
BASE_PAD_X = 8;
|
||||||
|
|
||||||
|
constructor TFlatEdit.Create(AOwner: TComponent);
|
||||||
|
begin
|
||||||
|
inherited Create(AOwner);
|
||||||
|
ControlStyle := ControlStyle + [csOpaque, csCaptureMouse, csClickEvents];
|
||||||
|
Width := 120;
|
||||||
|
Height := DpiScale(26);
|
||||||
|
TabStop := True;
|
||||||
|
Cursor := crIBeam;
|
||||||
|
FMaxLength := 0;
|
||||||
|
FCaretPos := 0;
|
||||||
|
FSelStart := 0;
|
||||||
|
FSelLen := 0;
|
||||||
|
FEditing := False;
|
||||||
|
FDragging := False;
|
||||||
|
|
||||||
|
Font.Name := 'Courier New';
|
||||||
|
Font.Size := 9;
|
||||||
|
|
||||||
|
FClrOuterBG := TColor($00181818);
|
||||||
|
FClrBG := TColor($001A1A1A);
|
||||||
|
FClrBGHot := TColor($00202020);
|
||||||
|
FClrBorder := TColor($00444444);
|
||||||
|
FClrText := TColor($00E0E0E0);
|
||||||
|
FClrTextDim := TColor($00666666);
|
||||||
|
FClrSelBG := TColor($000C3010);
|
||||||
|
FClrSelText := TColor($0000FF88);
|
||||||
|
Application.AddOnUserInputHandler(@AppUserInput);
|
||||||
|
end;
|
||||||
|
|
||||||
|
destructor TFlatEdit.Destroy;
|
||||||
|
begin
|
||||||
|
Application.RemoveOnUserInputHandler(@AppUserInput);
|
||||||
|
inherited Destroy;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TFlatEdit.DpiScale(V: Integer): Integer;
|
||||||
|
begin
|
||||||
|
Result := MulDiv(V, Screen.PixelsPerInch, 96);
|
||||||
|
if (V > 0) and (Result < 1) then
|
||||||
|
Result := 1;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatEdit.SetAppTheme(const T: TAppTheme);
|
||||||
|
begin
|
||||||
|
FClrOuterBG := T.Panel;
|
||||||
|
FClrBG := T.BtnNorm;
|
||||||
|
FClrBGHot := T.BtnHot;
|
||||||
|
FClrBorder := T.Border;
|
||||||
|
FClrText := T.Text;
|
||||||
|
FClrTextDim := T.TextDim;
|
||||||
|
FClrSelBG := T.BtnActive;
|
||||||
|
FClrSelText := T.BtnTextActive;
|
||||||
|
Font.Color := T.Text;
|
||||||
|
Invalidate;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TFlatEdit.DisplayText: string;
|
||||||
|
begin
|
||||||
|
if FPasswordChar <> #0 then
|
||||||
|
Result := StringOfChar(FPasswordChar, TextLen)
|
||||||
|
else
|
||||||
|
Result := FText;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TFlatEdit.TextLen: Integer;
|
||||||
|
begin
|
||||||
|
Result := UTF8Length(FText);
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TFlatEdit.HasSelection: Boolean;
|
||||||
|
begin
|
||||||
|
Result := FSelLen <> 0;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TFlatEdit.SelFirst: Integer;
|
||||||
|
begin
|
||||||
|
if FSelLen >= 0 then
|
||||||
|
Result := FSelStart
|
||||||
|
else
|
||||||
|
Result := FSelStart + FSelLen;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TFlatEdit.SelAfter: Integer;
|
||||||
|
begin
|
||||||
|
if FSelLen >= 0 then
|
||||||
|
Result := FSelStart + FSelLen
|
||||||
|
else
|
||||||
|
Result := FSelStart;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TFlatEdit.CaretPosAtX(X: Integer): Integer;
|
||||||
|
var
|
||||||
|
I, Pad, RelX, BestDist, Dist: Integer;
|
||||||
|
S: string;
|
||||||
|
begin
|
||||||
|
Pad := DpiScale(BASE_PAD_X);
|
||||||
|
RelX := X - Pad;
|
||||||
|
S := DisplayText;
|
||||||
|
Result := 0;
|
||||||
|
BestDist := Abs(RelX);
|
||||||
|
for I := 1 to UTF8Length(S) do
|
||||||
|
begin
|
||||||
|
Dist := Abs(RelX - Canvas.TextWidth(UTF8Copy(S, 1, I)));
|
||||||
|
if Dist < BestDist then
|
||||||
|
begin
|
||||||
|
BestDist := Dist;
|
||||||
|
Result := I;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatEdit.SetText(const V: string);
|
||||||
|
var
|
||||||
|
S: string;
|
||||||
|
begin
|
||||||
|
S := V;
|
||||||
|
if (FMaxLength > 0) and (UTF8Length(S) > FMaxLength) then
|
||||||
|
S := UTF8Copy(S, 1, FMaxLength);
|
||||||
|
if FText = S then Exit;
|
||||||
|
FText := S;
|
||||||
|
FCaretPos := EnsureRange(FCaretPos, 0, TextLen);
|
||||||
|
FSelStart := FCaretPos;
|
||||||
|
FSelLen := 0;
|
||||||
|
Invalidate;
|
||||||
|
DoChange;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatEdit.SetTextHint(const V: string);
|
||||||
|
begin
|
||||||
|
if FTextHint = V then Exit;
|
||||||
|
FTextHint := V;
|
||||||
|
Invalidate;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatEdit.SetPasswordChar(V: Char);
|
||||||
|
begin
|
||||||
|
if FPasswordChar = V then Exit;
|
||||||
|
FPasswordChar := V;
|
||||||
|
Invalidate;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatEdit.SetMaxLength(V: Integer);
|
||||||
|
begin
|
||||||
|
FMaxLength := Max(0, V);
|
||||||
|
if (FMaxLength > 0) and (UTF8Length(FText) > FMaxLength) then
|
||||||
|
SetText(UTF8Copy(FText, 1, FMaxLength));
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatEdit.SetCaretPos(V: Integer);
|
||||||
|
begin
|
||||||
|
FCaretPos := EnsureRange(V, 0, TextLen);
|
||||||
|
FSelStart := FCaretPos;
|
||||||
|
FSelLen := 0;
|
||||||
|
Invalidate;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatEdit.DeleteSelection;
|
||||||
|
var
|
||||||
|
A, B: Integer;
|
||||||
|
begin
|
||||||
|
if not HasSelection then Exit;
|
||||||
|
A := SelFirst;
|
||||||
|
B := SelAfter;
|
||||||
|
UTF8Delete(FText, A + 1, B - A);
|
||||||
|
FCaretPos := A;
|
||||||
|
FSelStart := FCaretPos;
|
||||||
|
FSelLen := 0;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatEdit.InsertText(const S: string);
|
||||||
|
var
|
||||||
|
AddText: string;
|
||||||
|
SpaceLeft: Integer;
|
||||||
|
begin
|
||||||
|
if S = '' then Exit;
|
||||||
|
DeleteSelection;
|
||||||
|
AddText := S;
|
||||||
|
if FMaxLength > 0 then
|
||||||
|
begin
|
||||||
|
SpaceLeft := FMaxLength - TextLen;
|
||||||
|
if SpaceLeft <= 0 then Exit;
|
||||||
|
AddText := UTF8Copy(AddText, 1, SpaceLeft);
|
||||||
|
end;
|
||||||
|
UTF8Insert(AddText, FText, FCaretPos + 1);
|
||||||
|
Inc(FCaretPos, UTF8Length(AddText));
|
||||||
|
FSelStart := FCaretPos;
|
||||||
|
FSelLen := 0;
|
||||||
|
Invalidate;
|
||||||
|
DoChange;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatEdit.DoChange;
|
||||||
|
begin
|
||||||
|
if Assigned(FOnChange) then
|
||||||
|
FOnChange(Self);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatEdit.AppUserInput(Sender: TObject; Msg: Cardinal);
|
||||||
|
var
|
||||||
|
P: TPoint;
|
||||||
|
begin
|
||||||
|
if not FEditing then Exit;
|
||||||
|
case Msg of
|
||||||
|
LM_LBUTTONDOWN, LM_RBUTTONDOWN, LM_MBUTTONDOWN: ;
|
||||||
|
else
|
||||||
|
Exit;
|
||||||
|
end;
|
||||||
|
|
||||||
|
P := ScreenToClient(Mouse.CursorPos);
|
||||||
|
if PtInRect(ClientRect, P) then Exit;
|
||||||
|
|
||||||
|
FDragging := False;
|
||||||
|
MouseCapture := False;
|
||||||
|
FEditing := False;
|
||||||
|
FHot := False;
|
||||||
|
FSelLen := 0;
|
||||||
|
Invalidate;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatEdit.CMTextChanged(var Msg: TLMessage);
|
||||||
|
begin
|
||||||
|
Invalidate;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatEdit.Paint;
|
||||||
|
var
|
||||||
|
R, TextR, SelR: TRect;
|
||||||
|
Pad, TextY, CaretX, A, B: Integer;
|
||||||
|
S, BeforeSel, SelText: string;
|
||||||
|
begin
|
||||||
|
R := ClientRect;
|
||||||
|
Canvas.Brush.Color := FClrOuterBG;
|
||||||
|
Canvas.Brush.Style := bsSolid;
|
||||||
|
Canvas.Pen.Style := psClear;
|
||||||
|
Canvas.FillRect(R);
|
||||||
|
|
||||||
|
if FHot or FEditing then
|
||||||
|
Canvas.Brush.Color := FClrBGHot
|
||||||
|
else
|
||||||
|
Canvas.Brush.Color := FClrBG;
|
||||||
|
Canvas.Pen.Color := FClrBorder;
|
||||||
|
Canvas.Pen.Style := psSolid;
|
||||||
|
Canvas.Pen.Width := 1;
|
||||||
|
Canvas.Rectangle(R.Left, R.Top, R.Right, R.Bottom);
|
||||||
|
|
||||||
|
Canvas.Font.Assign(Font);
|
||||||
|
Pad := DpiScale(BASE_PAD_X);
|
||||||
|
TextR := Rect(Pad, 0, Width - Pad, Height);
|
||||||
|
TextY := (Height - Canvas.TextHeight('Ag')) div 2;
|
||||||
|
|
||||||
|
if (FText = '') and not FEditing and (FTextHint <> '') then
|
||||||
|
begin
|
||||||
|
Canvas.Brush.Style := bsClear;
|
||||||
|
Canvas.Font.Color := FClrTextDim;
|
||||||
|
Canvas.TextRect(TextR, TextR.Left, TextY, FTextHint);
|
||||||
|
Exit;
|
||||||
|
end;
|
||||||
|
|
||||||
|
S := DisplayText;
|
||||||
|
if HasSelection then
|
||||||
|
begin
|
||||||
|
A := SelFirst;
|
||||||
|
B := SelAfter;
|
||||||
|
BeforeSel := UTF8Copy(S, 1, A);
|
||||||
|
SelText := UTF8Copy(S, A + 1, B - A);
|
||||||
|
SelR := Rect(TextR.Left + Canvas.TextWidth(BeforeSel), TextY,
|
||||||
|
TextR.Left + Canvas.TextWidth(BeforeSel + SelText), TextY + Canvas.TextHeight('Ag'));
|
||||||
|
Canvas.Brush.Color := FClrSelBG;
|
||||||
|
Canvas.Brush.Style := bsSolid;
|
||||||
|
Canvas.Pen.Style := psClear;
|
||||||
|
Canvas.FillRect(SelR);
|
||||||
|
end;
|
||||||
|
|
||||||
|
Canvas.Brush.Style := bsClear;
|
||||||
|
Canvas.Font.Color := FClrText;
|
||||||
|
Canvas.TextRect(TextR, TextR.Left, TextY, S);
|
||||||
|
|
||||||
|
if HasSelection then
|
||||||
|
begin
|
||||||
|
A := SelFirst;
|
||||||
|
B := SelAfter;
|
||||||
|
BeforeSel := UTF8Copy(S, 1, A);
|
||||||
|
SelText := UTF8Copy(S, A + 1, B - A);
|
||||||
|
Canvas.Font.Color := FClrSelText;
|
||||||
|
Canvas.TextOut(TextR.Left + Canvas.TextWidth(BeforeSel), TextY, SelText);
|
||||||
|
end;
|
||||||
|
|
||||||
|
if FEditing and not HasSelection then
|
||||||
|
begin
|
||||||
|
CaretX := TextR.Left + Canvas.TextWidth(UTF8Copy(S, 1, FCaretPos));
|
||||||
|
Canvas.Pen.Color := FClrText;
|
||||||
|
Canvas.Pen.Style := psSolid;
|
||||||
|
Canvas.Pen.Width := 1;
|
||||||
|
Canvas.Line(CaretX, TextY, CaretX, TextY + Canvas.TextHeight('Ag'));
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatEdit.MouseEnter;
|
||||||
|
begin
|
||||||
|
inherited;
|
||||||
|
FHot := True;
|
||||||
|
Invalidate;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatEdit.MouseLeave;
|
||||||
|
begin
|
||||||
|
inherited;
|
||||||
|
FHot := False;
|
||||||
|
Invalidate;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatEdit.MouseDown(Button: TMouseButton; Shift: TShiftState;
|
||||||
|
X, Y: Integer);
|
||||||
|
var
|
||||||
|
Pos: Integer;
|
||||||
|
begin
|
||||||
|
inherited;
|
||||||
|
if (Button <> mbLeft) or not Enabled then Exit;
|
||||||
|
SetFocus;
|
||||||
|
FEditing := True;
|
||||||
|
FDragging := True;
|
||||||
|
MouseCapture := True;
|
||||||
|
Pos := CaretPosAtX(X);
|
||||||
|
FCaretPos := Pos;
|
||||||
|
FSelStart := Pos;
|
||||||
|
FSelLen := 0;
|
||||||
|
Invalidate;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatEdit.MouseMove(Shift: TShiftState; X, Y: Integer);
|
||||||
|
var
|
||||||
|
Pos: Integer;
|
||||||
|
begin
|
||||||
|
inherited;
|
||||||
|
if not FDragging then Exit;
|
||||||
|
Pos := CaretPosAtX(X);
|
||||||
|
FCaretPos := Pos;
|
||||||
|
FSelLen := Pos - FSelStart;
|
||||||
|
Invalidate;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatEdit.MouseUp(Button: TMouseButton; Shift: TShiftState;
|
||||||
|
X, Y: Integer);
|
||||||
|
begin
|
||||||
|
inherited;
|
||||||
|
if Button <> mbLeft then Exit;
|
||||||
|
FDragging := False;
|
||||||
|
MouseCapture := False;
|
||||||
|
Invalidate;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatEdit.DblClick;
|
||||||
|
begin
|
||||||
|
inherited;
|
||||||
|
FEditing := True;
|
||||||
|
SelectAll;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatEdit.KeyDown(var Key: Word; Shift: TShiftState);
|
||||||
|
begin
|
||||||
|
inherited;
|
||||||
|
if not Enabled then Exit;
|
||||||
|
FEditing := True;
|
||||||
|
case Key of
|
||||||
|
VK_LEFT:
|
||||||
|
begin
|
||||||
|
SetCaretPos(FCaretPos - 1);
|
||||||
|
Key := 0;
|
||||||
|
end;
|
||||||
|
VK_RIGHT:
|
||||||
|
begin
|
||||||
|
SetCaretPos(FCaretPos + 1);
|
||||||
|
Key := 0;
|
||||||
|
end;
|
||||||
|
VK_HOME:
|
||||||
|
begin
|
||||||
|
SetCaretPos(0);
|
||||||
|
Key := 0;
|
||||||
|
end;
|
||||||
|
VK_END:
|
||||||
|
begin
|
||||||
|
SetCaretPos(TextLen);
|
||||||
|
Key := 0;
|
||||||
|
end;
|
||||||
|
VK_BACK:
|
||||||
|
begin
|
||||||
|
if HasSelection then
|
||||||
|
DeleteSelection
|
||||||
|
else if FCaretPos > 0 then
|
||||||
|
begin
|
||||||
|
UTF8Delete(FText, FCaretPos, 1);
|
||||||
|
Dec(FCaretPos);
|
||||||
|
end;
|
||||||
|
FSelStart := FCaretPos;
|
||||||
|
FSelLen := 0;
|
||||||
|
Invalidate;
|
||||||
|
DoChange;
|
||||||
|
Key := 0;
|
||||||
|
end;
|
||||||
|
VK_DELETE:
|
||||||
|
begin
|
||||||
|
if HasSelection then
|
||||||
|
DeleteSelection
|
||||||
|
else if FCaretPos < TextLen then
|
||||||
|
UTF8Delete(FText, FCaretPos + 1, 1);
|
||||||
|
FSelStart := FCaretPos;
|
||||||
|
FSelLen := 0;
|
||||||
|
Invalidate;
|
||||||
|
DoChange;
|
||||||
|
Key := 0;
|
||||||
|
end;
|
||||||
|
Ord('A'):
|
||||||
|
if ssCtrl in Shift then
|
||||||
|
begin
|
||||||
|
SelectAll;
|
||||||
|
Key := 0;
|
||||||
|
end;
|
||||||
|
Ord('C'):
|
||||||
|
if (ssCtrl in Shift) and HasSelection then
|
||||||
|
begin
|
||||||
|
Clipboard.AsText := UTF8Copy(FText, SelFirst + 1, SelAfter - SelFirst);
|
||||||
|
Key := 0;
|
||||||
|
end;
|
||||||
|
Ord('X'):
|
||||||
|
if (ssCtrl in Shift) and HasSelection then
|
||||||
|
begin
|
||||||
|
Clipboard.AsText := UTF8Copy(FText, SelFirst + 1, SelAfter - SelFirst);
|
||||||
|
DeleteSelection;
|
||||||
|
Invalidate;
|
||||||
|
DoChange;
|
||||||
|
Key := 0;
|
||||||
|
end;
|
||||||
|
Ord('V'):
|
||||||
|
if ssCtrl in Shift then
|
||||||
|
begin
|
||||||
|
InsertText(Clipboard.AsText);
|
||||||
|
Key := 0;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatEdit.KeyPress(var Key: char);
|
||||||
|
begin
|
||||||
|
inherited;
|
||||||
|
if not Enabled then Exit;
|
||||||
|
if Key >= #32 then
|
||||||
|
Key := #0;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatEdit.UTF8KeyPress(var UTF8Key: TUTF8Char);
|
||||||
|
begin
|
||||||
|
inherited;
|
||||||
|
if not Enabled then Exit;
|
||||||
|
FEditing := True;
|
||||||
|
if (UTF8Key <> '') and (UTF8Key[1] >= #32) then
|
||||||
|
begin
|
||||||
|
InsertText(UTF8Key);
|
||||||
|
UTF8Key := '';
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatEdit.DoEnter;
|
||||||
|
begin
|
||||||
|
inherited;
|
||||||
|
FEditing := True;
|
||||||
|
Invalidate;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatEdit.DoExit;
|
||||||
|
begin
|
||||||
|
inherited;
|
||||||
|
FEditing := False;
|
||||||
|
FHot := False;
|
||||||
|
FSelLen := 0;
|
||||||
|
Invalidate;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatEdit.SelectAll;
|
||||||
|
begin
|
||||||
|
FSelStart := 0;
|
||||||
|
FSelLen := TextLen;
|
||||||
|
FCaretPos := TextLen;
|
||||||
|
Invalidate;
|
||||||
|
end;
|
||||||
|
|
||||||
|
end.
|
||||||
@@ -0,0 +1,821 @@
|
|||||||
|
unit FlatFloatSpinEdit;
|
||||||
|
|
||||||
|
{ Custom floating-point spin edit with canvas rendering.
|
||||||
|
Use instead of TFloatSpinEdit where native widgetset styling is undesirable. }
|
||||||
|
|
||||||
|
{$mode objfpc}{$H+}
|
||||||
|
|
||||||
|
interface
|
||||||
|
|
||||||
|
uses
|
||||||
|
Classes, SysUtils, Controls, Graphics, Forms, LCLType, LMessages, Types,
|
||||||
|
Clipbrd, Math, AppTheme;
|
||||||
|
|
||||||
|
type
|
||||||
|
TFlatFloatSpinPart = (ffspNone, ffspEdit, ffspUp, ffspDown);
|
||||||
|
|
||||||
|
TFlatFloatSpinEdit = class(TCustomControl)
|
||||||
|
private
|
||||||
|
FValue: Double;
|
||||||
|
FMinValue: Double;
|
||||||
|
FMaxValue: Double;
|
||||||
|
FIncrement: Double;
|
||||||
|
FDecimalPlaces: Integer;
|
||||||
|
FText: string;
|
||||||
|
FCaretPos: Integer;
|
||||||
|
FSelStart: Integer;
|
||||||
|
FSelLen: Integer;
|
||||||
|
FHot: Boolean;
|
||||||
|
FEditing: Boolean;
|
||||||
|
FDragging: Boolean;
|
||||||
|
FHotPart: TFlatFloatSpinPart;
|
||||||
|
FDownPart: TFlatFloatSpinPart;
|
||||||
|
FOnChange: TNotifyEvent;
|
||||||
|
FClrOuterBG: TColor;
|
||||||
|
FClrBG: TColor;
|
||||||
|
FClrBGHot: TColor;
|
||||||
|
FClrBorder: TColor;
|
||||||
|
FClrText: TColor;
|
||||||
|
FClrTextDim: TColor;
|
||||||
|
FClrSelBG: TColor;
|
||||||
|
FClrSelText: TColor;
|
||||||
|
FClrBtnHot: TColor;
|
||||||
|
FClrBtnDown: TColor;
|
||||||
|
FClrBtnText: TColor;
|
||||||
|
function DpiScale(V: Integer): Integer;
|
||||||
|
function ButtonWidth: Integer;
|
||||||
|
function EditRect: TRect;
|
||||||
|
function UpRect: TRect;
|
||||||
|
function DownRect: TRect;
|
||||||
|
function PartAt(X, Y: Integer): TFlatFloatSpinPart;
|
||||||
|
function HasSelection: Boolean;
|
||||||
|
function SelFirst: Integer;
|
||||||
|
function SelAfter: Integer;
|
||||||
|
function CaretPosAtX(X: Integer): Integer;
|
||||||
|
function FormatValue(V: Double): string;
|
||||||
|
function TryTextToValue(const S: string; out V: Double): Boolean;
|
||||||
|
procedure SetValue(V: Double);
|
||||||
|
procedure SetMinValue(V: Double);
|
||||||
|
procedure SetMaxValue(V: Double);
|
||||||
|
procedure SetIncrement(V: Double);
|
||||||
|
procedure SetDecimalPlaces(V: Integer);
|
||||||
|
procedure SetTextInternal(const S: string);
|
||||||
|
procedure DeleteSelection;
|
||||||
|
procedure InsertText(const S: string);
|
||||||
|
procedure UpdateValueFromText;
|
||||||
|
procedure CommitText;
|
||||||
|
procedure StepValue(Dir: Integer);
|
||||||
|
procedure DoChange;
|
||||||
|
procedure AppUserInput(Sender: TObject; Msg: Cardinal);
|
||||||
|
protected
|
||||||
|
procedure Paint; override;
|
||||||
|
procedure MouseEnter; override;
|
||||||
|
procedure MouseLeave; override;
|
||||||
|
procedure MouseDown(Button: TMouseButton; Shift: TShiftState;
|
||||||
|
X, Y: Integer); override;
|
||||||
|
procedure MouseMove(Shift: TShiftState; X, Y: Integer); override;
|
||||||
|
procedure MouseUp(Button: TMouseButton; Shift: TShiftState;
|
||||||
|
X, Y: Integer); override;
|
||||||
|
procedure DblClick; override;
|
||||||
|
procedure KeyDown(var Key: Word; Shift: TShiftState); override;
|
||||||
|
procedure KeyPress(var Key: char); override;
|
||||||
|
procedure DoEnter; override;
|
||||||
|
procedure DoExit; override;
|
||||||
|
function DoMouseWheelUp(Shift: TShiftState; MousePos: TPoint): Boolean; override;
|
||||||
|
function DoMouseWheelDown(Shift: TShiftState; MousePos: TPoint): Boolean; override;
|
||||||
|
public
|
||||||
|
constructor Create(AOwner: TComponent); override;
|
||||||
|
destructor Destroy; override;
|
||||||
|
procedure SetAppTheme(const T: TAppTheme);
|
||||||
|
procedure SelectAll;
|
||||||
|
|
||||||
|
property Value: Double read FValue write SetValue;
|
||||||
|
property MinValue: Double read FMinValue write SetMinValue;
|
||||||
|
property MaxValue: Double read FMaxValue write SetMaxValue;
|
||||||
|
property Increment: Double read FIncrement write SetIncrement;
|
||||||
|
property DecimalPlaces: Integer read FDecimalPlaces write SetDecimalPlaces;
|
||||||
|
property OnChange: TNotifyEvent read FOnChange write FOnChange;
|
||||||
|
property Align;
|
||||||
|
property Anchors;
|
||||||
|
property Enabled;
|
||||||
|
property Font;
|
||||||
|
property ParentShowHint;
|
||||||
|
property PopupMenu;
|
||||||
|
property ShowHint;
|
||||||
|
property TabOrder;
|
||||||
|
property TabStop;
|
||||||
|
property Tag;
|
||||||
|
property Visible;
|
||||||
|
property OnEnter;
|
||||||
|
property OnExit;
|
||||||
|
property OnKeyDown;
|
||||||
|
property OnKeyPress;
|
||||||
|
end;
|
||||||
|
|
||||||
|
implementation
|
||||||
|
|
||||||
|
const
|
||||||
|
BASE_PAD_X = 8;
|
||||||
|
BASE_BTN_W = 22;
|
||||||
|
VALUE_EPS = 1.0e-12;
|
||||||
|
|
||||||
|
constructor TFlatFloatSpinEdit.Create(AOwner: TComponent);
|
||||||
|
begin
|
||||||
|
inherited Create(AOwner);
|
||||||
|
ControlStyle := ControlStyle + [csOpaque, csCaptureMouse, csClickEvents];
|
||||||
|
Width := 96;
|
||||||
|
Height := DpiScale(26);
|
||||||
|
TabStop := True;
|
||||||
|
Cursor := crIBeam;
|
||||||
|
|
||||||
|
Font.Name := 'Courier New';
|
||||||
|
Font.Size := 9;
|
||||||
|
|
||||||
|
FMinValue := 0.0;
|
||||||
|
FMaxValue := 100.0;
|
||||||
|
FIncrement := 1.0;
|
||||||
|
FDecimalPlaces := 2;
|
||||||
|
FValue := 0.0;
|
||||||
|
FText := FormatValue(FValue);
|
||||||
|
FCaretPos := Length(FText);
|
||||||
|
FSelStart := FCaretPos;
|
||||||
|
|
||||||
|
FClrOuterBG := TColor($00181818);
|
||||||
|
FClrBG := TColor($001A1A1A);
|
||||||
|
FClrBGHot := TColor($00202020);
|
||||||
|
FClrBorder := TColor($00444444);
|
||||||
|
FClrText := TColor($00E0E0E0);
|
||||||
|
FClrTextDim := TColor($00666666);
|
||||||
|
FClrSelBG := TColor($000C3010);
|
||||||
|
FClrSelText := TColor($0000FF88);
|
||||||
|
FClrBtnHot := TColor($000C3010);
|
||||||
|
FClrBtnDown := TColor($00183618);
|
||||||
|
FClrBtnText := TColor($0000FF88);
|
||||||
|
Application.AddOnUserInputHandler(@AppUserInput);
|
||||||
|
end;
|
||||||
|
|
||||||
|
destructor TFlatFloatSpinEdit.Destroy;
|
||||||
|
begin
|
||||||
|
Application.RemoveOnUserInputHandler(@AppUserInput);
|
||||||
|
inherited Destroy;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TFlatFloatSpinEdit.DpiScale(V: Integer): Integer;
|
||||||
|
begin
|
||||||
|
Result := MulDiv(V, Screen.PixelsPerInch, 96);
|
||||||
|
if (V > 0) and (Result < 1) then
|
||||||
|
Result := 1;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TFlatFloatSpinEdit.ButtonWidth: Integer;
|
||||||
|
begin
|
||||||
|
Result := DpiScale(BASE_BTN_W);
|
||||||
|
if Result > Width div 2 then
|
||||||
|
Result := Width div 2;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TFlatFloatSpinEdit.EditRect: TRect;
|
||||||
|
begin
|
||||||
|
Result := Rect(0, 0, Width - ButtonWidth, Height);
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TFlatFloatSpinEdit.UpRect: TRect;
|
||||||
|
begin
|
||||||
|
Result := Rect(Width - ButtonWidth, 0, Width, Height div 2);
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TFlatFloatSpinEdit.DownRect: TRect;
|
||||||
|
begin
|
||||||
|
Result := Rect(Width - ButtonWidth, Height div 2, Width, Height);
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TFlatFloatSpinEdit.PartAt(X, Y: Integer): TFlatFloatSpinPart;
|
||||||
|
begin
|
||||||
|
if not PtInRect(ClientRect, Point(X, Y)) then
|
||||||
|
Exit(ffspNone);
|
||||||
|
if PtInRect(UpRect, Point(X, Y)) then
|
||||||
|
Exit(ffspUp);
|
||||||
|
if PtInRect(DownRect, Point(X, Y)) then
|
||||||
|
Exit(ffspDown);
|
||||||
|
Result := ffspEdit;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatFloatSpinEdit.SetAppTheme(const T: TAppTheme);
|
||||||
|
begin
|
||||||
|
FClrOuterBG := T.Panel;
|
||||||
|
FClrBG := T.BtnNorm;
|
||||||
|
FClrBGHot := T.BtnHot;
|
||||||
|
FClrBorder := T.Border;
|
||||||
|
FClrText := T.Text;
|
||||||
|
FClrTextDim := T.TextDim;
|
||||||
|
FClrSelBG := T.BtnActive;
|
||||||
|
FClrSelText := T.BtnTextActive;
|
||||||
|
FClrBtnHot := T.BtnActive;
|
||||||
|
FClrBtnDown := T.SliderTrackFill;
|
||||||
|
FClrBtnText := T.BtnTextActive;
|
||||||
|
Font.Color := T.Text;
|
||||||
|
Invalidate;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TFlatFloatSpinEdit.HasSelection: Boolean;
|
||||||
|
begin
|
||||||
|
Result := FSelLen <> 0;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TFlatFloatSpinEdit.SelFirst: Integer;
|
||||||
|
begin
|
||||||
|
if FSelLen >= 0 then
|
||||||
|
Result := FSelStart
|
||||||
|
else
|
||||||
|
Result := FSelStart + FSelLen;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TFlatFloatSpinEdit.SelAfter: Integer;
|
||||||
|
begin
|
||||||
|
if FSelLen >= 0 then
|
||||||
|
Result := FSelStart + FSelLen
|
||||||
|
else
|
||||||
|
Result := FSelStart;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TFlatFloatSpinEdit.CaretPosAtX(X: Integer): Integer;
|
||||||
|
var
|
||||||
|
I, Pad, RelX, BestDist, Dist: Integer;
|
||||||
|
begin
|
||||||
|
Pad := DpiScale(BASE_PAD_X);
|
||||||
|
RelX := X - Pad;
|
||||||
|
Result := 0;
|
||||||
|
BestDist := Abs(RelX);
|
||||||
|
for I := 1 to Length(FText) do
|
||||||
|
begin
|
||||||
|
Dist := Abs(RelX - Canvas.TextWidth(Copy(FText, 1, I)));
|
||||||
|
if Dist < BestDist then
|
||||||
|
begin
|
||||||
|
BestDist := Dist;
|
||||||
|
Result := I;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TFlatFloatSpinEdit.FormatValue(V: Double): string;
|
||||||
|
var
|
||||||
|
FS: TFormatSettings;
|
||||||
|
begin
|
||||||
|
FS := DefaultFormatSettings;
|
||||||
|
FS.DecimalSeparator := '.';
|
||||||
|
if FDecimalPlaces <= 0 then
|
||||||
|
Result := FormatFloat('0', V, FS)
|
||||||
|
else
|
||||||
|
Result := FormatFloat('0.' + StringOfChar('0', FDecimalPlaces), V, FS);
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TFlatFloatSpinEdit.TryTextToValue(const S: string; out V: Double): Boolean;
|
||||||
|
var
|
||||||
|
FS: TFormatSettings;
|
||||||
|
T: string;
|
||||||
|
begin
|
||||||
|
T := Trim(S);
|
||||||
|
if (T = '') or (T = '-') or (T = '.') or (T = '-.') then
|
||||||
|
Exit(False);
|
||||||
|
T := StringReplace(T, ',', '.', [rfReplaceAll]);
|
||||||
|
FS := DefaultFormatSettings;
|
||||||
|
FS.DecimalSeparator := '.';
|
||||||
|
Result := TryStrToFloat(T, V, FS);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatFloatSpinEdit.SetTextInternal(const S: string);
|
||||||
|
begin
|
||||||
|
FText := S;
|
||||||
|
FCaretPos := EnsureRange(FCaretPos, 0, Length(FText));
|
||||||
|
FSelStart := FCaretPos;
|
||||||
|
FSelLen := 0;
|
||||||
|
Invalidate;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatFloatSpinEdit.SetValue(V: Double);
|
||||||
|
var
|
||||||
|
NewText: string;
|
||||||
|
begin
|
||||||
|
V := EnsureRange(V, FMinValue, FMaxValue);
|
||||||
|
NewText := FormatValue(V);
|
||||||
|
if (Abs(FValue - V) <= VALUE_EPS) and (FText = NewText) then Exit;
|
||||||
|
FValue := V;
|
||||||
|
FText := NewText;
|
||||||
|
FCaretPos := Length(FText);
|
||||||
|
FSelStart := FCaretPos;
|
||||||
|
FSelLen := 0;
|
||||||
|
Invalidate;
|
||||||
|
DoChange;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatFloatSpinEdit.SetMinValue(V: Double);
|
||||||
|
begin
|
||||||
|
FMinValue := V;
|
||||||
|
if FMaxValue < FMinValue then
|
||||||
|
FMaxValue := FMinValue;
|
||||||
|
SetValue(FValue);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatFloatSpinEdit.SetMaxValue(V: Double);
|
||||||
|
begin
|
||||||
|
FMaxValue := V;
|
||||||
|
if FMinValue > FMaxValue then
|
||||||
|
FMinValue := FMaxValue;
|
||||||
|
SetValue(FValue);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatFloatSpinEdit.SetIncrement(V: Double);
|
||||||
|
begin
|
||||||
|
FIncrement := Abs(V);
|
||||||
|
if FIncrement <= 0.0 then
|
||||||
|
FIncrement := 1.0;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatFloatSpinEdit.SetDecimalPlaces(V: Integer);
|
||||||
|
begin
|
||||||
|
FDecimalPlaces := EnsureRange(V, 0, 8);
|
||||||
|
SetTextInternal(FormatValue(FValue));
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatFloatSpinEdit.DeleteSelection;
|
||||||
|
var
|
||||||
|
A, B: Integer;
|
||||||
|
begin
|
||||||
|
if not HasSelection then Exit;
|
||||||
|
A := SelFirst;
|
||||||
|
B := SelAfter;
|
||||||
|
Delete(FText, A + 1, B - A);
|
||||||
|
FCaretPos := A;
|
||||||
|
FSelStart := FCaretPos;
|
||||||
|
FSelLen := 0;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatFloatSpinEdit.InsertText(const S: string);
|
||||||
|
begin
|
||||||
|
if S = '' then Exit;
|
||||||
|
DeleteSelection;
|
||||||
|
Insert(S, FText, FCaretPos + 1);
|
||||||
|
Inc(FCaretPos, Length(S));
|
||||||
|
FSelStart := FCaretPos;
|
||||||
|
FSelLen := 0;
|
||||||
|
UpdateValueFromText;
|
||||||
|
Invalidate;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatFloatSpinEdit.UpdateValueFromText;
|
||||||
|
var
|
||||||
|
V: Double;
|
||||||
|
begin
|
||||||
|
if not TryTextToValue(FText, V) then Exit;
|
||||||
|
if (V < FMinValue) or (V > FMaxValue) then Exit;
|
||||||
|
if Abs(FValue - V) <= VALUE_EPS then Exit;
|
||||||
|
FValue := V;
|
||||||
|
DoChange;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatFloatSpinEdit.CommitText;
|
||||||
|
var
|
||||||
|
V: Double;
|
||||||
|
begin
|
||||||
|
if not TryTextToValue(FText, V) then
|
||||||
|
begin
|
||||||
|
SetValue(FValue);
|
||||||
|
Exit;
|
||||||
|
end;
|
||||||
|
SetValue(V);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatFloatSpinEdit.StepValue(Dir: Integer);
|
||||||
|
begin
|
||||||
|
CommitText;
|
||||||
|
SetValue(FValue + Dir * FIncrement);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatFloatSpinEdit.DoChange;
|
||||||
|
begin
|
||||||
|
if Assigned(FOnChange) then
|
||||||
|
FOnChange(Self);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatFloatSpinEdit.AppUserInput(Sender: TObject; Msg: Cardinal);
|
||||||
|
var
|
||||||
|
P: TPoint;
|
||||||
|
begin
|
||||||
|
if not FEditing then Exit;
|
||||||
|
case Msg of
|
||||||
|
LM_LBUTTONDOWN, LM_RBUTTONDOWN, LM_MBUTTONDOWN: ;
|
||||||
|
else
|
||||||
|
Exit;
|
||||||
|
end;
|
||||||
|
|
||||||
|
P := ScreenToClient(Mouse.CursorPos);
|
||||||
|
if PtInRect(ClientRect, P) then Exit;
|
||||||
|
|
||||||
|
CommitText;
|
||||||
|
FDragging := False;
|
||||||
|
MouseCapture := False;
|
||||||
|
FEditing := False;
|
||||||
|
FHot := False;
|
||||||
|
FHotPart := ffspNone;
|
||||||
|
FDownPart := ffspNone;
|
||||||
|
FSelLen := 0;
|
||||||
|
Invalidate;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatFloatSpinEdit.Paint;
|
||||||
|
var
|
||||||
|
R, ER, UR, DR, TextR, SelR: TRect;
|
||||||
|
Pad, TextY, CaretX, A, B, MidX, MidY, S: Integer;
|
||||||
|
BeforeSel, SelText: string;
|
||||||
|
|
||||||
|
procedure FillPart(const AR: TRect; Part: TFlatFloatSpinPart);
|
||||||
|
begin
|
||||||
|
if not Enabled then
|
||||||
|
Canvas.Brush.Color := FClrBG
|
||||||
|
else if FDownPart = Part then
|
||||||
|
Canvas.Brush.Color := FClrBtnDown
|
||||||
|
else if FHotPart = Part then
|
||||||
|
Canvas.Brush.Color := FClrBtnHot
|
||||||
|
else if FHot or FEditing or Focused then
|
||||||
|
Canvas.Brush.Color := FClrBGHot
|
||||||
|
else
|
||||||
|
Canvas.Brush.Color := FClrBG;
|
||||||
|
Canvas.Pen.Style := psClear;
|
||||||
|
Canvas.Brush.Style := bsSolid;
|
||||||
|
Canvas.FillRect(AR);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure DrawArrow(const AR: TRect; Up: Boolean);
|
||||||
|
var
|
||||||
|
P: array[0..2] of TPoint;
|
||||||
|
Size: Integer;
|
||||||
|
begin
|
||||||
|
Size := Max(3, DpiScale(4));
|
||||||
|
MidX := (AR.Left + AR.Right) div 2;
|
||||||
|
MidY := (AR.Top + AR.Bottom) div 2;
|
||||||
|
if Up then
|
||||||
|
begin
|
||||||
|
P[0] := Point(MidX, MidY - Size div 2);
|
||||||
|
P[1] := Point(MidX - Size, MidY + Size div 2);
|
||||||
|
P[2] := Point(MidX + Size, MidY + Size div 2);
|
||||||
|
end
|
||||||
|
else
|
||||||
|
begin
|
||||||
|
P[0] := Point(MidX - Size, MidY - Size div 2);
|
||||||
|
P[1] := Point(MidX + Size, MidY - Size div 2);
|
||||||
|
P[2] := Point(MidX, MidY + Size div 2);
|
||||||
|
end;
|
||||||
|
if Enabled then
|
||||||
|
Canvas.Brush.Color := FClrBtnText
|
||||||
|
else
|
||||||
|
Canvas.Brush.Color := FClrTextDim;
|
||||||
|
Canvas.Pen.Style := psClear;
|
||||||
|
Canvas.Polygon(P);
|
||||||
|
end;
|
||||||
|
begin
|
||||||
|
R := ClientRect;
|
||||||
|
ER := EditRect;
|
||||||
|
UR := UpRect;
|
||||||
|
DR := DownRect;
|
||||||
|
|
||||||
|
Canvas.Brush.Color := FClrOuterBG;
|
||||||
|
Canvas.Brush.Style := bsSolid;
|
||||||
|
Canvas.Pen.Style := psClear;
|
||||||
|
Canvas.FillRect(R);
|
||||||
|
|
||||||
|
if FHot or FEditing or Focused then
|
||||||
|
Canvas.Brush.Color := FClrBGHot
|
||||||
|
else
|
||||||
|
Canvas.Brush.Color := FClrBG;
|
||||||
|
Canvas.Pen.Color := FClrBorder;
|
||||||
|
Canvas.Pen.Style := psSolid;
|
||||||
|
Canvas.Pen.Width := 1;
|
||||||
|
Canvas.Rectangle(R.Left, R.Top, R.Right, R.Bottom);
|
||||||
|
|
||||||
|
FillPart(UR, ffspUp);
|
||||||
|
FillPart(DR, ffspDown);
|
||||||
|
Canvas.Pen.Color := FClrBorder;
|
||||||
|
Canvas.Pen.Style := psSolid;
|
||||||
|
Canvas.Line(UR.Left, 1, UR.Left, Height - 1);
|
||||||
|
Canvas.Line(UR.Left, DR.Top, Width - 1, DR.Top);
|
||||||
|
DrawArrow(UR, True);
|
||||||
|
DrawArrow(DR, False);
|
||||||
|
|
||||||
|
Canvas.Font.Assign(Font);
|
||||||
|
if Enabled then
|
||||||
|
Canvas.Font.Color := FClrText
|
||||||
|
else
|
||||||
|
Canvas.Font.Color := FClrTextDim;
|
||||||
|
Pad := DpiScale(BASE_PAD_X);
|
||||||
|
TextR := Rect(ER.Left + Pad, ER.Top, ER.Right - DpiScale(3), ER.Bottom);
|
||||||
|
TextY := (Height - Canvas.TextHeight('Ag')) div 2;
|
||||||
|
|
||||||
|
if HasSelection then
|
||||||
|
begin
|
||||||
|
A := SelFirst;
|
||||||
|
B := SelAfter;
|
||||||
|
BeforeSel := Copy(FText, 1, A);
|
||||||
|
SelText := Copy(FText, A + 1, B - A);
|
||||||
|
SelR := Rect(TextR.Left + Canvas.TextWidth(BeforeSel), TextY,
|
||||||
|
TextR.Left + Canvas.TextWidth(BeforeSel + SelText), TextY + Canvas.TextHeight('Ag'));
|
||||||
|
Canvas.Brush.Color := FClrSelBG;
|
||||||
|
Canvas.Pen.Style := psClear;
|
||||||
|
Canvas.FillRect(SelR);
|
||||||
|
end;
|
||||||
|
|
||||||
|
Canvas.Brush.Style := bsClear;
|
||||||
|
Canvas.TextRect(TextR, TextR.Left, TextY, FText);
|
||||||
|
|
||||||
|
if HasSelection then
|
||||||
|
begin
|
||||||
|
A := SelFirst;
|
||||||
|
B := SelAfter;
|
||||||
|
BeforeSel := Copy(FText, 1, A);
|
||||||
|
SelText := Copy(FText, A + 1, B - A);
|
||||||
|
Canvas.Font.Color := FClrSelText;
|
||||||
|
Canvas.TextOut(TextR.Left + Canvas.TextWidth(BeforeSel), TextY, SelText);
|
||||||
|
end;
|
||||||
|
|
||||||
|
if FEditing and not HasSelection then
|
||||||
|
begin
|
||||||
|
S := EnsureRange(FCaretPos, 0, Length(FText));
|
||||||
|
CaretX := TextR.Left + Canvas.TextWidth(Copy(FText, 1, S));
|
||||||
|
Canvas.Pen.Color := FClrText;
|
||||||
|
Canvas.Pen.Style := psSolid;
|
||||||
|
Canvas.Pen.Width := 1;
|
||||||
|
Canvas.Line(CaretX, TextY, CaretX, TextY + Canvas.TextHeight('Ag'));
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatFloatSpinEdit.MouseEnter;
|
||||||
|
begin
|
||||||
|
inherited;
|
||||||
|
FHot := True;
|
||||||
|
Invalidate;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatFloatSpinEdit.MouseLeave;
|
||||||
|
begin
|
||||||
|
inherited;
|
||||||
|
FHot := False;
|
||||||
|
FHotPart := ffspNone;
|
||||||
|
Invalidate;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatFloatSpinEdit.MouseDown(Button: TMouseButton; Shift: TShiftState;
|
||||||
|
X, Y: Integer);
|
||||||
|
var
|
||||||
|
Pos: Integer;
|
||||||
|
begin
|
||||||
|
inherited;
|
||||||
|
if (Button <> mbLeft) or not Enabled then Exit;
|
||||||
|
SetFocus;
|
||||||
|
FEditing := True;
|
||||||
|
FDownPart := PartAt(X, Y);
|
||||||
|
FHotPart := FDownPart;
|
||||||
|
if FDownPart in [ffspUp, ffspDown] then
|
||||||
|
begin
|
||||||
|
FDragging := False;
|
||||||
|
MouseCapture := True;
|
||||||
|
if FDownPart = ffspUp then
|
||||||
|
StepValue(1)
|
||||||
|
else
|
||||||
|
StepValue(-1);
|
||||||
|
end
|
||||||
|
else
|
||||||
|
begin
|
||||||
|
FDragging := True;
|
||||||
|
MouseCapture := True;
|
||||||
|
Pos := CaretPosAtX(X);
|
||||||
|
FCaretPos := Pos;
|
||||||
|
FSelStart := Pos;
|
||||||
|
FSelLen := 0;
|
||||||
|
end;
|
||||||
|
Invalidate;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatFloatSpinEdit.MouseMove(Shift: TShiftState; X, Y: Integer);
|
||||||
|
var
|
||||||
|
Pos: Integer;
|
||||||
|
Part: TFlatFloatSpinPart;
|
||||||
|
begin
|
||||||
|
inherited;
|
||||||
|
Part := PartAt(X, Y);
|
||||||
|
if FHotPart <> Part then
|
||||||
|
begin
|
||||||
|
FHotPart := Part;
|
||||||
|
if Part = ffspEdit then
|
||||||
|
Cursor := crIBeam
|
||||||
|
else
|
||||||
|
Cursor := crDefault;
|
||||||
|
Invalidate;
|
||||||
|
end;
|
||||||
|
|
||||||
|
if not FDragging then Exit;
|
||||||
|
Pos := CaretPosAtX(X);
|
||||||
|
FCaretPos := Pos;
|
||||||
|
FSelLen := Pos - FSelStart;
|
||||||
|
Invalidate;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatFloatSpinEdit.MouseUp(Button: TMouseButton; Shift: TShiftState;
|
||||||
|
X, Y: Integer);
|
||||||
|
begin
|
||||||
|
inherited;
|
||||||
|
if Button <> mbLeft then Exit;
|
||||||
|
FDragging := False;
|
||||||
|
FDownPart := ffspNone;
|
||||||
|
MouseCapture := False;
|
||||||
|
Invalidate;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatFloatSpinEdit.DblClick;
|
||||||
|
begin
|
||||||
|
inherited;
|
||||||
|
if FHotPart <> ffspEdit then Exit;
|
||||||
|
FEditing := True;
|
||||||
|
SelectAll;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatFloatSpinEdit.KeyDown(var Key: Word; Shift: TShiftState);
|
||||||
|
begin
|
||||||
|
inherited;
|
||||||
|
if not Enabled then Exit;
|
||||||
|
FEditing := True;
|
||||||
|
case Key of
|
||||||
|
VK_LEFT:
|
||||||
|
begin
|
||||||
|
FCaretPos := EnsureRange(FCaretPos - 1, 0, Length(FText));
|
||||||
|
FSelStart := FCaretPos;
|
||||||
|
FSelLen := 0;
|
||||||
|
Invalidate;
|
||||||
|
Key := 0;
|
||||||
|
end;
|
||||||
|
VK_RIGHT:
|
||||||
|
begin
|
||||||
|
FCaretPos := EnsureRange(FCaretPos + 1, 0, Length(FText));
|
||||||
|
FSelStart := FCaretPos;
|
||||||
|
FSelLen := 0;
|
||||||
|
Invalidate;
|
||||||
|
Key := 0;
|
||||||
|
end;
|
||||||
|
VK_HOME:
|
||||||
|
begin
|
||||||
|
FCaretPos := 0;
|
||||||
|
FSelStart := FCaretPos;
|
||||||
|
FSelLen := 0;
|
||||||
|
Invalidate;
|
||||||
|
Key := 0;
|
||||||
|
end;
|
||||||
|
VK_END:
|
||||||
|
begin
|
||||||
|
FCaretPos := Length(FText);
|
||||||
|
FSelStart := FCaretPos;
|
||||||
|
FSelLen := 0;
|
||||||
|
Invalidate;
|
||||||
|
Key := 0;
|
||||||
|
end;
|
||||||
|
VK_UP:
|
||||||
|
begin
|
||||||
|
StepValue(1);
|
||||||
|
Key := 0;
|
||||||
|
end;
|
||||||
|
VK_DOWN:
|
||||||
|
begin
|
||||||
|
StepValue(-1);
|
||||||
|
Key := 0;
|
||||||
|
end;
|
||||||
|
VK_RETURN:
|
||||||
|
begin
|
||||||
|
CommitText;
|
||||||
|
SelectAll;
|
||||||
|
Key := 0;
|
||||||
|
end;
|
||||||
|
VK_ESCAPE:
|
||||||
|
begin
|
||||||
|
SetTextInternal(FormatValue(FValue));
|
||||||
|
Key := 0;
|
||||||
|
end;
|
||||||
|
VK_BACK:
|
||||||
|
begin
|
||||||
|
if HasSelection then
|
||||||
|
DeleteSelection
|
||||||
|
else if FCaretPos > 0 then
|
||||||
|
begin
|
||||||
|
Delete(FText, FCaretPos, 1);
|
||||||
|
Dec(FCaretPos);
|
||||||
|
end;
|
||||||
|
FSelStart := FCaretPos;
|
||||||
|
FSelLen := 0;
|
||||||
|
UpdateValueFromText;
|
||||||
|
Invalidate;
|
||||||
|
Key := 0;
|
||||||
|
end;
|
||||||
|
VK_DELETE:
|
||||||
|
begin
|
||||||
|
if HasSelection then
|
||||||
|
DeleteSelection
|
||||||
|
else if FCaretPos < Length(FText) then
|
||||||
|
Delete(FText, FCaretPos + 1, 1);
|
||||||
|
FSelStart := FCaretPos;
|
||||||
|
FSelLen := 0;
|
||||||
|
UpdateValueFromText;
|
||||||
|
Invalidate;
|
||||||
|
Key := 0;
|
||||||
|
end;
|
||||||
|
Ord('A'):
|
||||||
|
if ssCtrl in Shift then
|
||||||
|
begin
|
||||||
|
SelectAll;
|
||||||
|
Key := 0;
|
||||||
|
end;
|
||||||
|
Ord('C'):
|
||||||
|
if (ssCtrl in Shift) and HasSelection then
|
||||||
|
begin
|
||||||
|
Clipboard.AsText := Copy(FText, SelFirst + 1, SelAfter - SelFirst);
|
||||||
|
Key := 0;
|
||||||
|
end;
|
||||||
|
Ord('X'):
|
||||||
|
if (ssCtrl in Shift) and HasSelection then
|
||||||
|
begin
|
||||||
|
Clipboard.AsText := Copy(FText, SelFirst + 1, SelAfter - SelFirst);
|
||||||
|
DeleteSelection;
|
||||||
|
UpdateValueFromText;
|
||||||
|
Invalidate;
|
||||||
|
Key := 0;
|
||||||
|
end;
|
||||||
|
Ord('V'):
|
||||||
|
if ssCtrl in Shift then
|
||||||
|
begin
|
||||||
|
InsertText(Clipboard.AsText);
|
||||||
|
Key := 0;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatFloatSpinEdit.KeyPress(var Key: char);
|
||||||
|
begin
|
||||||
|
inherited;
|
||||||
|
if not Enabled then Exit;
|
||||||
|
FEditing := True;
|
||||||
|
if Key in ['0'..'9'] then
|
||||||
|
begin
|
||||||
|
InsertText(Key);
|
||||||
|
Key := #0;
|
||||||
|
end
|
||||||
|
else if (Key in ['.', ',']) and (Pos('.', FText) = 0) and (Pos(',', FText) = 0) then
|
||||||
|
begin
|
||||||
|
InsertText('.');
|
||||||
|
Key := #0;
|
||||||
|
end
|
||||||
|
else if (Key = '-') and (FMinValue < 0.0) and (FCaretPos = 0) and (Pos('-', FText) = 0) then
|
||||||
|
begin
|
||||||
|
InsertText(Key);
|
||||||
|
Key := #0;
|
||||||
|
end
|
||||||
|
else if Key >= #32 then
|
||||||
|
Key := #0;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatFloatSpinEdit.DoEnter;
|
||||||
|
begin
|
||||||
|
inherited;
|
||||||
|
FEditing := True;
|
||||||
|
Invalidate;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatFloatSpinEdit.DoExit;
|
||||||
|
begin
|
||||||
|
inherited;
|
||||||
|
CommitText;
|
||||||
|
FEditing := False;
|
||||||
|
FHot := False;
|
||||||
|
FHotPart := ffspNone;
|
||||||
|
FDownPart := ffspNone;
|
||||||
|
FSelLen := 0;
|
||||||
|
Invalidate;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TFlatFloatSpinEdit.DoMouseWheelUp(Shift: TShiftState; MousePos: TPoint): Boolean;
|
||||||
|
begin
|
||||||
|
if Enabled then
|
||||||
|
StepValue(1);
|
||||||
|
Result := True;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TFlatFloatSpinEdit.DoMouseWheelDown(Shift: TShiftState; MousePos: TPoint): Boolean;
|
||||||
|
begin
|
||||||
|
if Enabled then
|
||||||
|
StepValue(-1);
|
||||||
|
Result := True;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatFloatSpinEdit.SelectAll;
|
||||||
|
begin
|
||||||
|
FSelStart := 0;
|
||||||
|
FSelLen := Length(FText);
|
||||||
|
FCaretPos := Length(FText);
|
||||||
|
Invalidate;
|
||||||
|
end;
|
||||||
|
|
||||||
|
end.
|
||||||
+382
@@ -0,0 +1,382 @@
|
|||||||
|
unit FlatListBox;
|
||||||
|
|
||||||
|
{ Custom list box with canvas rendering.
|
||||||
|
Use instead of TListBox where native widgetset styling is undesirable. }
|
||||||
|
|
||||||
|
{$mode objfpc}{$H+}
|
||||||
|
|
||||||
|
interface
|
||||||
|
|
||||||
|
uses
|
||||||
|
Classes, SysUtils, Controls, Graphics, Forms, LCLType, LMessages, Types,
|
||||||
|
Math, AppTheme;
|
||||||
|
|
||||||
|
type
|
||||||
|
TFlatListBox = class(TCustomControl)
|
||||||
|
private
|
||||||
|
FItems: TStringList;
|
||||||
|
FItemIndex: Integer;
|
||||||
|
FHotIndex: Integer;
|
||||||
|
FTopIndex: Integer;
|
||||||
|
FMouseDownIndex: Integer;
|
||||||
|
FClrOuterBG: TColor;
|
||||||
|
FClrBG: TColor;
|
||||||
|
FClrBGHot: TColor;
|
||||||
|
FClrBorder: TColor;
|
||||||
|
FClrText: TColor;
|
||||||
|
FClrTextDim: TColor;
|
||||||
|
FClrSelBG: TColor;
|
||||||
|
FClrSelText: TColor;
|
||||||
|
FClrScroll: TColor;
|
||||||
|
function DpiScale(V: Integer): Integer;
|
||||||
|
function ItemHeight: Integer;
|
||||||
|
function VisibleCount: Integer;
|
||||||
|
function ItemAt(Y: Integer): Integer;
|
||||||
|
procedure ItemsChanged(Sender: TObject);
|
||||||
|
procedure SetItemIndex(V: Integer);
|
||||||
|
procedure SetTopIndex(V: Integer);
|
||||||
|
procedure EnsureItemVisible(Idx: Integer);
|
||||||
|
procedure CMTextChanged(var Msg: TLMessage); message CM_TEXTCHANGED;
|
||||||
|
protected
|
||||||
|
procedure Paint; override;
|
||||||
|
procedure MouseEnter; override;
|
||||||
|
procedure MouseLeave; override;
|
||||||
|
procedure MouseDown(Button: TMouseButton; Shift: TShiftState;
|
||||||
|
X, Y: Integer); override;
|
||||||
|
procedure MouseMove(Shift: TShiftState; X, Y: Integer); override;
|
||||||
|
procedure MouseUp(Button: TMouseButton; Shift: TShiftState;
|
||||||
|
X, Y: Integer); override;
|
||||||
|
procedure KeyDown(var Key: Word; Shift: TShiftState); override;
|
||||||
|
function DoMouseWheelUp(Shift: TShiftState; MousePos: TPoint): Boolean; override;
|
||||||
|
function DoMouseWheelDown(Shift: TShiftState; MousePos: TPoint): Boolean; override;
|
||||||
|
public
|
||||||
|
constructor Create(AOwner: TComponent); override;
|
||||||
|
destructor Destroy; override;
|
||||||
|
procedure SetAppTheme(const T: TAppTheme);
|
||||||
|
|
||||||
|
property Items: TStringList read FItems;
|
||||||
|
property ItemIndex: Integer read FItemIndex write SetItemIndex;
|
||||||
|
property TopIndex: Integer read FTopIndex write SetTopIndex;
|
||||||
|
property Align;
|
||||||
|
property Anchors;
|
||||||
|
property Enabled;
|
||||||
|
property Font;
|
||||||
|
property ParentShowHint;
|
||||||
|
property PopupMenu;
|
||||||
|
property ShowHint;
|
||||||
|
property TabOrder;
|
||||||
|
property TabStop;
|
||||||
|
property Tag;
|
||||||
|
property Visible;
|
||||||
|
property OnClick;
|
||||||
|
property OnDblClick;
|
||||||
|
end;
|
||||||
|
|
||||||
|
implementation
|
||||||
|
|
||||||
|
const
|
||||||
|
BASE_ITEM_H = 22;
|
||||||
|
BASE_PAD_X = 8;
|
||||||
|
BASE_SCROLL_W = 3;
|
||||||
|
|
||||||
|
constructor TFlatListBox.Create(AOwner: TComponent);
|
||||||
|
begin
|
||||||
|
inherited Create(AOwner);
|
||||||
|
ControlStyle := ControlStyle + [csOpaque, csCaptureMouse, csClickEvents, csDoubleClicks];
|
||||||
|
FItems := TStringList.Create;
|
||||||
|
FItems.OnChange := @ItemsChanged;
|
||||||
|
FItemIndex := -1;
|
||||||
|
FHotIndex := -1;
|
||||||
|
FTopIndex := 0;
|
||||||
|
FMouseDownIndex := -1;
|
||||||
|
Width := 180;
|
||||||
|
Height := 140;
|
||||||
|
TabStop := True;
|
||||||
|
Cursor := crDefault;
|
||||||
|
|
||||||
|
Font.Name := 'Courier New';
|
||||||
|
Font.Size := 8;
|
||||||
|
|
||||||
|
FClrOuterBG := TColor($00181818);
|
||||||
|
FClrBG := TColor($00101010);
|
||||||
|
FClrBGHot := TColor($000C3010);
|
||||||
|
FClrBorder := TColor($00303030);
|
||||||
|
FClrText := TColor($00CCCCCC);
|
||||||
|
FClrTextDim := TColor($00666666);
|
||||||
|
FClrSelBG := TColor($00183618);
|
||||||
|
FClrSelText := TColor($0000FF88);
|
||||||
|
FClrScroll := TColor($00387838);
|
||||||
|
end;
|
||||||
|
|
||||||
|
destructor TFlatListBox.Destroy;
|
||||||
|
begin
|
||||||
|
FItems.Free;
|
||||||
|
inherited Destroy;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TFlatListBox.DpiScale(V: Integer): Integer;
|
||||||
|
begin
|
||||||
|
Result := MulDiv(V, Screen.PixelsPerInch, 96);
|
||||||
|
if (V > 0) and (Result < 1) then
|
||||||
|
Result := 1;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TFlatListBox.ItemHeight: Integer;
|
||||||
|
begin
|
||||||
|
Canvas.Font.Assign(Font);
|
||||||
|
Result := DpiScale(BASE_ITEM_H);
|
||||||
|
if Result < Canvas.TextHeight('Ag') + DpiScale(7) then
|
||||||
|
Result := Canvas.TextHeight('Ag') + DpiScale(7);
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TFlatListBox.VisibleCount: Integer;
|
||||||
|
begin
|
||||||
|
Result := Max(1, (Height - DpiScale(2)) div ItemHeight);
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TFlatListBox.ItemAt(Y: Integer): Integer;
|
||||||
|
begin
|
||||||
|
Result := FTopIndex + (Y - DpiScale(1)) div ItemHeight;
|
||||||
|
if (Result < 0) or (Result >= FItems.Count) then
|
||||||
|
Result := -1;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatListBox.SetAppTheme(const T: TAppTheme);
|
||||||
|
begin
|
||||||
|
FClrOuterBG := T.Panel;
|
||||||
|
FClrBG := T.BG;
|
||||||
|
FClrBGHot := T.BtnActive;
|
||||||
|
FClrBorder := T.Border;
|
||||||
|
FClrText := T.Text;
|
||||||
|
FClrTextDim := T.TextDim;
|
||||||
|
FClrSelBG := T.SliderTrackFill;
|
||||||
|
FClrSelText := T.BtnTextActive;
|
||||||
|
FClrScroll := T.BtnBorderActive;
|
||||||
|
Font.Color := T.Text;
|
||||||
|
Invalidate;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatListBox.ItemsChanged(Sender: TObject);
|
||||||
|
begin
|
||||||
|
if FItems.Count = 0 then
|
||||||
|
FItemIndex := -1
|
||||||
|
else if FItemIndex >= FItems.Count then
|
||||||
|
FItemIndex := FItems.Count - 1;
|
||||||
|
if FHotIndex >= FItems.Count then
|
||||||
|
FHotIndex := -1;
|
||||||
|
SetTopIndex(FTopIndex);
|
||||||
|
Invalidate;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatListBox.SetItemIndex(V: Integer);
|
||||||
|
begin
|
||||||
|
V := EnsureRange(V, -1, FItems.Count - 1);
|
||||||
|
if FItemIndex = V then
|
||||||
|
begin
|
||||||
|
EnsureItemVisible(FItemIndex);
|
||||||
|
Exit;
|
||||||
|
end;
|
||||||
|
FItemIndex := V;
|
||||||
|
EnsureItemVisible(FItemIndex);
|
||||||
|
Invalidate;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatListBox.SetTopIndex(V: Integer);
|
||||||
|
begin
|
||||||
|
V := EnsureRange(V, 0, Max(0, FItems.Count - VisibleCount));
|
||||||
|
if FTopIndex = V then Exit;
|
||||||
|
FTopIndex := V;
|
||||||
|
Invalidate;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatListBox.EnsureItemVisible(Idx: Integer);
|
||||||
|
var
|
||||||
|
VC: Integer;
|
||||||
|
begin
|
||||||
|
if Idx < 0 then Exit;
|
||||||
|
VC := VisibleCount;
|
||||||
|
if Idx < FTopIndex then
|
||||||
|
SetTopIndex(Idx)
|
||||||
|
else if Idx >= FTopIndex + VC then
|
||||||
|
SetTopIndex(Idx - VC + 1);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatListBox.CMTextChanged(var Msg: TLMessage);
|
||||||
|
begin
|
||||||
|
Invalidate;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatListBox.Paint;
|
||||||
|
var
|
||||||
|
i, ItemIdx, IH, Y, TextY, Pad, ScrollW, ThumbTop, ThumbH: Integer;
|
||||||
|
R, TextR: TRect;
|
||||||
|
begin
|
||||||
|
Canvas.Brush.Color := FClrOuterBG;
|
||||||
|
Canvas.Brush.Style := bsSolid;
|
||||||
|
Canvas.Pen.Style := psClear;
|
||||||
|
Canvas.FillRect(ClientRect);
|
||||||
|
|
||||||
|
Canvas.Brush.Color := FClrBG;
|
||||||
|
Canvas.Brush.Style := bsSolid;
|
||||||
|
Canvas.Pen.Color := FClrBorder;
|
||||||
|
Canvas.Pen.Style := psSolid;
|
||||||
|
Canvas.Pen.Width := 1;
|
||||||
|
Canvas.Rectangle(0, 0, Width, Height);
|
||||||
|
|
||||||
|
Canvas.Font.Assign(Font);
|
||||||
|
IH := ItemHeight;
|
||||||
|
Pad := DpiScale(BASE_PAD_X);
|
||||||
|
ScrollW := DpiScale(BASE_SCROLL_W);
|
||||||
|
for i := 0 to VisibleCount - 1 do
|
||||||
|
begin
|
||||||
|
ItemIdx := FTopIndex + i;
|
||||||
|
if ItemIdx >= FItems.Count then Break;
|
||||||
|
Y := DpiScale(1) + i * IH;
|
||||||
|
R := Rect(DpiScale(1), Y, Width - DpiScale(1), Y + IH);
|
||||||
|
if ItemIdx = FItemIndex then
|
||||||
|
begin
|
||||||
|
Canvas.Brush.Color := FClrSelBG;
|
||||||
|
Canvas.Font.Color := FClrSelText;
|
||||||
|
end
|
||||||
|
else if (ItemIdx = FHotIndex) and Enabled then
|
||||||
|
begin
|
||||||
|
Canvas.Brush.Color := FClrBGHot;
|
||||||
|
Canvas.Font.Color := FClrText;
|
||||||
|
end
|
||||||
|
else
|
||||||
|
begin
|
||||||
|
Canvas.Brush.Color := FClrBG;
|
||||||
|
if Enabled then
|
||||||
|
Canvas.Font.Color := FClrText
|
||||||
|
else
|
||||||
|
Canvas.Font.Color := FClrTextDim;
|
||||||
|
end;
|
||||||
|
Canvas.Pen.Style := psClear;
|
||||||
|
Canvas.Brush.Style := bsSolid;
|
||||||
|
Canvas.FillRect(R);
|
||||||
|
Canvas.Brush.Style := bsClear;
|
||||||
|
TextY := Y + (IH - Canvas.TextHeight('Ag')) div 2;
|
||||||
|
TextR := Rect(Pad, Y, Width - Pad - ScrollW, Y + IH);
|
||||||
|
Canvas.TextRect(TextR, TextR.Left, TextY, FItems[ItemIdx]);
|
||||||
|
end;
|
||||||
|
|
||||||
|
if FItems.Count > VisibleCount then
|
||||||
|
begin
|
||||||
|
ThumbH := Max(DpiScale(16), MulDiv(Height - DpiScale(4), VisibleCount, FItems.Count));
|
||||||
|
ThumbTop := DpiScale(2) + MulDiv(Height - DpiScale(4) - ThumbH,
|
||||||
|
FTopIndex, Max(1, FItems.Count - VisibleCount));
|
||||||
|
Canvas.Brush.Color := FClrScroll;
|
||||||
|
Canvas.Brush.Style := bsSolid;
|
||||||
|
Canvas.Pen.Style := psClear;
|
||||||
|
Canvas.FillRect(Rect(Width - DpiScale(5), ThumbTop, Width - DpiScale(2), ThumbTop + ThumbH));
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatListBox.MouseEnter;
|
||||||
|
begin
|
||||||
|
inherited;
|
||||||
|
Invalidate;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatListBox.MouseLeave;
|
||||||
|
begin
|
||||||
|
inherited;
|
||||||
|
FHotIndex := -1;
|
||||||
|
Invalidate;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatListBox.MouseDown(Button: TMouseButton; Shift: TShiftState;
|
||||||
|
X, Y: Integer);
|
||||||
|
begin
|
||||||
|
inherited;
|
||||||
|
if (Button <> mbLeft) or not Enabled then Exit;
|
||||||
|
SetFocus;
|
||||||
|
FMouseDownIndex := ItemAt(Y);
|
||||||
|
if FMouseDownIndex >= 0 then
|
||||||
|
SetItemIndex(FMouseDownIndex);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatListBox.MouseMove(Shift: TShiftState; X, Y: Integer);
|
||||||
|
var
|
||||||
|
Idx: Integer;
|
||||||
|
begin
|
||||||
|
inherited;
|
||||||
|
if PtInRect(ClientRect, Point(X, Y)) then
|
||||||
|
Idx := ItemAt(Y)
|
||||||
|
else
|
||||||
|
Idx := -1;
|
||||||
|
if FHotIndex = Idx then Exit;
|
||||||
|
FHotIndex := Idx;
|
||||||
|
Invalidate;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatListBox.MouseUp(Button: TMouseButton; Shift: TShiftState;
|
||||||
|
X, Y: Integer);
|
||||||
|
begin
|
||||||
|
inherited;
|
||||||
|
if Button <> mbLeft then Exit;
|
||||||
|
FMouseDownIndex := -1;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatListBox.KeyDown(var Key: Word; Shift: TShiftState);
|
||||||
|
var
|
||||||
|
OldIndex: Integer;
|
||||||
|
begin
|
||||||
|
inherited;
|
||||||
|
if not Enabled then Exit;
|
||||||
|
OldIndex := FItemIndex;
|
||||||
|
case Key of
|
||||||
|
VK_UP:
|
||||||
|
begin
|
||||||
|
if FItems.Count > 0 then
|
||||||
|
SetItemIndex(EnsureRange(FItemIndex - 1, 0, FItems.Count - 1));
|
||||||
|
Key := 0;
|
||||||
|
end;
|
||||||
|
VK_DOWN:
|
||||||
|
begin
|
||||||
|
if FItems.Count > 0 then
|
||||||
|
SetItemIndex(EnsureRange(FItemIndex + 1, 0, FItems.Count - 1));
|
||||||
|
Key := 0;
|
||||||
|
end;
|
||||||
|
VK_PRIOR:
|
||||||
|
begin
|
||||||
|
if FItems.Count > 0 then
|
||||||
|
SetItemIndex(EnsureRange(FItemIndex - VisibleCount, 0, FItems.Count - 1));
|
||||||
|
Key := 0;
|
||||||
|
end;
|
||||||
|
VK_NEXT:
|
||||||
|
begin
|
||||||
|
if FItems.Count > 0 then
|
||||||
|
SetItemIndex(EnsureRange(FItemIndex + VisibleCount, 0, FItems.Count - 1));
|
||||||
|
Key := 0;
|
||||||
|
end;
|
||||||
|
VK_HOME:
|
||||||
|
begin
|
||||||
|
if FItems.Count > 0 then
|
||||||
|
SetItemIndex(0);
|
||||||
|
Key := 0;
|
||||||
|
end;
|
||||||
|
VK_END:
|
||||||
|
begin
|
||||||
|
if FItems.Count > 0 then
|
||||||
|
SetItemIndex(FItems.Count - 1);
|
||||||
|
Key := 0;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
if (Key = 0) and (FItemIndex <> OldIndex) then
|
||||||
|
Click;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TFlatListBox.DoMouseWheelUp(Shift: TShiftState; MousePos: TPoint): Boolean;
|
||||||
|
begin
|
||||||
|
SetTopIndex(FTopIndex - 3);
|
||||||
|
Result := True;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TFlatListBox.DoMouseWheelDown(Shift: TShiftState; MousePos: TPoint): Boolean;
|
||||||
|
begin
|
||||||
|
SetTopIndex(FTopIndex + 3);
|
||||||
|
Result := True;
|
||||||
|
end;
|
||||||
|
|
||||||
|
end.
|
||||||
@@ -0,0 +1,294 @@
|
|||||||
|
unit FlatRadioButton;
|
||||||
|
|
||||||
|
{ Custom radio button with canvas rendering.
|
||||||
|
Use instead of TRadioButton where native widgetset styling is undesirable. }
|
||||||
|
|
||||||
|
{$mode objfpc}{$H+}
|
||||||
|
|
||||||
|
interface
|
||||||
|
|
||||||
|
uses
|
||||||
|
Classes, SysUtils, Controls, Graphics, Forms, LCLType, LMessages, Types, Math,
|
||||||
|
AppTheme;
|
||||||
|
|
||||||
|
type
|
||||||
|
TFlatRadioButton = class(TCustomControl)
|
||||||
|
private
|
||||||
|
FChecked: Boolean;
|
||||||
|
FHot: Boolean;
|
||||||
|
FDown: Boolean;
|
||||||
|
FOnChange: TNotifyEvent;
|
||||||
|
FClrBG: TColor;
|
||||||
|
FClrCircle: TColor;
|
||||||
|
FClrCircleChecked: TColor;
|
||||||
|
FClrCircleHot: TColor;
|
||||||
|
FClrBorder: TColor;
|
||||||
|
FClrBorderHot: TColor;
|
||||||
|
FClrDot: TColor;
|
||||||
|
FClrText: TColor;
|
||||||
|
FClrTextDisabled: TColor;
|
||||||
|
function DpiScale(V: Integer): Integer;
|
||||||
|
procedure SetCheckedSilently(V: Boolean);
|
||||||
|
procedure SetChecked(V: Boolean);
|
||||||
|
procedure DoChange;
|
||||||
|
procedure UncheckSiblings;
|
||||||
|
procedure CMTextChanged(var Msg: TLMessage); message CM_TEXTCHANGED;
|
||||||
|
protected
|
||||||
|
procedure Paint; override;
|
||||||
|
procedure MouseEnter; override;
|
||||||
|
procedure MouseLeave; override;
|
||||||
|
procedure MouseDown(Button: TMouseButton; Shift: TShiftState;
|
||||||
|
X, Y: Integer); override;
|
||||||
|
procedure MouseUp(Button: TMouseButton; Shift: TShiftState;
|
||||||
|
X, Y: Integer); override;
|
||||||
|
procedure KeyDown(var Key: Word; Shift: TShiftState); override;
|
||||||
|
public
|
||||||
|
constructor Create(AOwner: TComponent); override;
|
||||||
|
procedure SetAppTheme(const T: TAppTheme);
|
||||||
|
|
||||||
|
property Checked: Boolean read FChecked write SetChecked;
|
||||||
|
property OnChange: TNotifyEvent read FOnChange write FOnChange;
|
||||||
|
property Align;
|
||||||
|
property Anchors;
|
||||||
|
property Caption;
|
||||||
|
property Enabled;
|
||||||
|
property Font;
|
||||||
|
property ParentShowHint;
|
||||||
|
property PopupMenu;
|
||||||
|
property ShowHint;
|
||||||
|
property TabOrder;
|
||||||
|
property TabStop;
|
||||||
|
property Tag;
|
||||||
|
property Visible;
|
||||||
|
property OnClick;
|
||||||
|
end;
|
||||||
|
|
||||||
|
implementation
|
||||||
|
|
||||||
|
const
|
||||||
|
BASE_CIRCLE_SIZE = 16;
|
||||||
|
BASE_CIRCLE_LEFT = 1;
|
||||||
|
BASE_TEXT_GAP = 8;
|
||||||
|
|
||||||
|
constructor TFlatRadioButton.Create(AOwner: TComponent);
|
||||||
|
begin
|
||||||
|
inherited Create(AOwner);
|
||||||
|
ControlStyle := ControlStyle + [csOpaque, csCaptureMouse, csClickEvents];
|
||||||
|
Width := 160;
|
||||||
|
Height := 22;
|
||||||
|
TabStop := True;
|
||||||
|
Cursor := crHandPoint;
|
||||||
|
|
||||||
|
Font.Name := 'Courier New';
|
||||||
|
Font.Size := 9;
|
||||||
|
|
||||||
|
FClrBG := TColor($00181818);
|
||||||
|
FClrCircle := TColor($001A1A1A);
|
||||||
|
FClrCircleChecked := TColor($000C3010);
|
||||||
|
FClrCircleHot := TColor($00282828);
|
||||||
|
FClrBorder := TColor($00444444);
|
||||||
|
FClrBorderHot := TColor($00387838);
|
||||||
|
FClrDot := TColor($0000FF88);
|
||||||
|
FClrText := TColor($00CCCCCC);
|
||||||
|
FClrTextDisabled := TColor($00666666);
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TFlatRadioButton.DpiScale(V: Integer): Integer;
|
||||||
|
begin
|
||||||
|
Result := MulDiv(V, Screen.PixelsPerInch, 96);
|
||||||
|
if (V > 0) and (Result < 1) then
|
||||||
|
Result := 1;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatRadioButton.SetAppTheme(const T: TAppTheme);
|
||||||
|
begin
|
||||||
|
FClrBG := T.Panel;
|
||||||
|
FClrCircle := T.BtnNorm;
|
||||||
|
FClrCircleChecked := T.BtnActive;
|
||||||
|
FClrCircleHot := T.BtnHot;
|
||||||
|
FClrBorder := T.BtnBorderNorm;
|
||||||
|
FClrBorderHot := T.BtnBorderActive;
|
||||||
|
FClrDot := T.BtnTextActive;
|
||||||
|
FClrText := T.Text;
|
||||||
|
FClrTextDisabled := T.TextDim;
|
||||||
|
Font.Color := T.Text;
|
||||||
|
Invalidate;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatRadioButton.SetChecked(V: Boolean);
|
||||||
|
begin
|
||||||
|
if FChecked = V then Exit;
|
||||||
|
if V then
|
||||||
|
UncheckSiblings;
|
||||||
|
FChecked := V;
|
||||||
|
Invalidate;
|
||||||
|
DoChange;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatRadioButton.SetCheckedSilently(V: Boolean);
|
||||||
|
begin
|
||||||
|
if FChecked = V then Exit;
|
||||||
|
FChecked := V;
|
||||||
|
Invalidate;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatRadioButton.DoChange;
|
||||||
|
begin
|
||||||
|
if Assigned(FOnChange) then
|
||||||
|
FOnChange(Self);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatRadioButton.UncheckSiblings;
|
||||||
|
var
|
||||||
|
i: Integer;
|
||||||
|
Ctrl: TControl;
|
||||||
|
begin
|
||||||
|
if Parent = nil then Exit;
|
||||||
|
for i := 0 to Parent.ControlCount - 1 do
|
||||||
|
begin
|
||||||
|
Ctrl := Parent.Controls[i];
|
||||||
|
if (Ctrl <> Self) and (Ctrl is TFlatRadioButton) then
|
||||||
|
TFlatRadioButton(Ctrl).SetCheckedSilently(False);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatRadioButton.CMTextChanged(var Msg: TLMessage);
|
||||||
|
begin
|
||||||
|
Invalidate;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatRadioButton.Paint;
|
||||||
|
var
|
||||||
|
CircleSize, CircleLeft, CircleTop, DotSize, TextGap, TextTop, TX: Integer;
|
||||||
|
R: TRect;
|
||||||
|
CircleColor, BorderColor, TextColor: TColor;
|
||||||
|
|
||||||
|
procedure FillCircle(CX, CY, Radius: Integer; AColor: TColor);
|
||||||
|
var
|
||||||
|
DY, DX: Integer;
|
||||||
|
begin
|
||||||
|
Canvas.Brush.Color := AColor;
|
||||||
|
Canvas.Brush.Style := bsSolid;
|
||||||
|
Canvas.Pen.Style := psClear;
|
||||||
|
for DY := -Radius to Radius do
|
||||||
|
begin
|
||||||
|
DX := Trunc(Sqrt(Sqr(Radius) - Sqr(DY)));
|
||||||
|
Canvas.FillRect(Rect(CX - DX, CY + DY, CX + DX + 1, CY + DY + 1));
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
begin
|
||||||
|
Canvas.Brush.Color := FClrBG;
|
||||||
|
Canvas.Brush.Style := bsSolid;
|
||||||
|
Canvas.Pen.Style := psClear;
|
||||||
|
Canvas.FillRect(ClientRect);
|
||||||
|
|
||||||
|
CircleSize := DpiScale(BASE_CIRCLE_SIZE);
|
||||||
|
if CircleSize > Height - DpiScale(4) then
|
||||||
|
CircleSize := Height - DpiScale(4);
|
||||||
|
if CircleSize < DpiScale(10) then
|
||||||
|
CircleSize := DpiScale(10);
|
||||||
|
CircleLeft := DpiScale(BASE_CIRCLE_LEFT);
|
||||||
|
CircleTop := (Height - CircleSize) div 2;
|
||||||
|
TextGap := DpiScale(BASE_TEXT_GAP);
|
||||||
|
R := Rect(CircleLeft, CircleTop, CircleLeft + CircleSize, CircleTop + CircleSize);
|
||||||
|
|
||||||
|
if FChecked then
|
||||||
|
CircleColor := FClrCircleChecked
|
||||||
|
else if FHot or Focused then
|
||||||
|
CircleColor := FClrCircleHot
|
||||||
|
else
|
||||||
|
CircleColor := FClrCircle;
|
||||||
|
|
||||||
|
if FHot or Focused then
|
||||||
|
BorderColor := FClrBorderHot
|
||||||
|
else
|
||||||
|
BorderColor := FClrBorder;
|
||||||
|
|
||||||
|
if not Enabled then
|
||||||
|
begin
|
||||||
|
CircleColor := FClrBG;
|
||||||
|
BorderColor := FClrBorder;
|
||||||
|
TextColor := FClrTextDisabled;
|
||||||
|
end
|
||||||
|
else
|
||||||
|
TextColor := FClrText;
|
||||||
|
|
||||||
|
FillCircle((R.Left + R.Right) div 2, (R.Top + R.Bottom) div 2,
|
||||||
|
CircleSize div 2, BorderColor);
|
||||||
|
FillCircle((R.Left + R.Right) div 2, (R.Top + R.Bottom) div 2,
|
||||||
|
Max(1, CircleSize div 2 - DpiScale(1)), CircleColor);
|
||||||
|
|
||||||
|
if FChecked then
|
||||||
|
begin
|
||||||
|
DotSize := Max(DpiScale(5), CircleSize div 2 - DpiScale(2));
|
||||||
|
if DotSize mod 2 = 0 then
|
||||||
|
Dec(DotSize);
|
||||||
|
FillCircle((R.Left + R.Right) div 2, (R.Top + R.Bottom) div 2,
|
||||||
|
DotSize div 2, FClrDot);
|
||||||
|
end;
|
||||||
|
|
||||||
|
if Caption <> '' then
|
||||||
|
begin
|
||||||
|
Canvas.Font.Assign(Font);
|
||||||
|
Canvas.Font.Color := TextColor;
|
||||||
|
Canvas.Brush.Style := bsClear;
|
||||||
|
TX := R.Right + TextGap;
|
||||||
|
TextTop := (Height - Canvas.TextHeight('Ag')) div 2;
|
||||||
|
Canvas.TextOut(TX, TextTop, Caption);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatRadioButton.MouseEnter;
|
||||||
|
begin
|
||||||
|
inherited;
|
||||||
|
FHot := True;
|
||||||
|
Invalidate;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatRadioButton.MouseLeave;
|
||||||
|
begin
|
||||||
|
inherited;
|
||||||
|
FHot := False;
|
||||||
|
FDown := False;
|
||||||
|
Invalidate;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatRadioButton.MouseDown(Button: TMouseButton; Shift: TShiftState;
|
||||||
|
X, Y: Integer);
|
||||||
|
begin
|
||||||
|
inherited;
|
||||||
|
if (Button <> mbLeft) or not Enabled then Exit;
|
||||||
|
SetFocus;
|
||||||
|
FDown := True;
|
||||||
|
Invalidate;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatRadioButton.MouseUp(Button: TMouseButton; Shift: TShiftState;
|
||||||
|
X, Y: Integer);
|
||||||
|
begin
|
||||||
|
inherited;
|
||||||
|
if Button <> mbLeft then Exit;
|
||||||
|
FDown := False;
|
||||||
|
if Enabled and PtInRect(ClientRect, Point(X, Y)) then
|
||||||
|
begin
|
||||||
|
if not Checked then
|
||||||
|
Checked := True;
|
||||||
|
Click;
|
||||||
|
end;
|
||||||
|
Invalidate;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatRadioButton.KeyDown(var Key: Word; Shift: TShiftState);
|
||||||
|
begin
|
||||||
|
inherited;
|
||||||
|
if not Enabled then Exit;
|
||||||
|
if Key in [VK_SPACE, VK_RETURN] then
|
||||||
|
begin
|
||||||
|
if not Checked then
|
||||||
|
Checked := True;
|
||||||
|
Click;
|
||||||
|
Key := 0;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
end.
|
||||||
@@ -0,0 +1,777 @@
|
|||||||
|
unit FlatSpinEdit;
|
||||||
|
|
||||||
|
{ Custom integer spin edit with canvas rendering.
|
||||||
|
Use instead of TSpinEdit where native widgetset styling is undesirable. }
|
||||||
|
|
||||||
|
{$mode objfpc}{$H+}
|
||||||
|
|
||||||
|
interface
|
||||||
|
|
||||||
|
uses
|
||||||
|
Classes, SysUtils, Controls, Graphics, Forms, LCLType, LMessages, Types,
|
||||||
|
Clipbrd, Math, AppTheme;
|
||||||
|
|
||||||
|
type
|
||||||
|
TFlatSpinPart = (fspNone, fspEdit, fspUp, fspDown);
|
||||||
|
|
||||||
|
TFlatSpinEdit = class(TCustomControl)
|
||||||
|
private
|
||||||
|
FValue: Integer;
|
||||||
|
FMinValue: Integer;
|
||||||
|
FMaxValue: Integer;
|
||||||
|
FIncrement: Integer;
|
||||||
|
FText: string;
|
||||||
|
FCaretPos: Integer;
|
||||||
|
FSelStart: Integer;
|
||||||
|
FSelLen: Integer;
|
||||||
|
FHot: Boolean;
|
||||||
|
FEditing: Boolean;
|
||||||
|
FDragging: Boolean;
|
||||||
|
FHotPart: TFlatSpinPart;
|
||||||
|
FDownPart: TFlatSpinPart;
|
||||||
|
FOnChange: TNotifyEvent;
|
||||||
|
FClrOuterBG: TColor;
|
||||||
|
FClrBG: TColor;
|
||||||
|
FClrBGHot: TColor;
|
||||||
|
FClrBorder: TColor;
|
||||||
|
FClrText: TColor;
|
||||||
|
FClrTextDim: TColor;
|
||||||
|
FClrSelBG: TColor;
|
||||||
|
FClrSelText: TColor;
|
||||||
|
FClrBtnHot: TColor;
|
||||||
|
FClrBtnDown: TColor;
|
||||||
|
FClrBtnText: TColor;
|
||||||
|
function DpiScale(V: Integer): Integer;
|
||||||
|
function ButtonWidth: Integer;
|
||||||
|
function EditRect: TRect;
|
||||||
|
function UpRect: TRect;
|
||||||
|
function DownRect: TRect;
|
||||||
|
function PartAt(X, Y: Integer): TFlatSpinPart;
|
||||||
|
function HasSelection: Boolean;
|
||||||
|
function SelFirst: Integer;
|
||||||
|
function SelAfter: Integer;
|
||||||
|
function CaretPosAtX(X: Integer): Integer;
|
||||||
|
procedure SetValue(V: Integer);
|
||||||
|
procedure SetMinValue(V: Integer);
|
||||||
|
procedure SetMaxValue(V: Integer);
|
||||||
|
procedure SetIncrement(V: Integer);
|
||||||
|
procedure SetTextInternal(const S: string; AFireChange: Boolean);
|
||||||
|
procedure DeleteSelection;
|
||||||
|
procedure InsertText(const S: string);
|
||||||
|
procedure UpdateValueFromText;
|
||||||
|
procedure CommitText;
|
||||||
|
procedure StepValue(Dir: Integer);
|
||||||
|
procedure DoChange;
|
||||||
|
procedure AppUserInput(Sender: TObject; Msg: Cardinal);
|
||||||
|
protected
|
||||||
|
procedure Paint; override;
|
||||||
|
procedure MouseEnter; override;
|
||||||
|
procedure MouseLeave; override;
|
||||||
|
procedure MouseDown(Button: TMouseButton; Shift: TShiftState;
|
||||||
|
X, Y: Integer); override;
|
||||||
|
procedure MouseMove(Shift: TShiftState; X, Y: Integer); override;
|
||||||
|
procedure MouseUp(Button: TMouseButton; Shift: TShiftState;
|
||||||
|
X, Y: Integer); override;
|
||||||
|
procedure DblClick; override;
|
||||||
|
procedure KeyDown(var Key: Word; Shift: TShiftState); override;
|
||||||
|
procedure KeyPress(var Key: char); override;
|
||||||
|
procedure DoEnter; override;
|
||||||
|
procedure DoExit; override;
|
||||||
|
function DoMouseWheelUp(Shift: TShiftState; MousePos: TPoint): Boolean; override;
|
||||||
|
function DoMouseWheelDown(Shift: TShiftState; MousePos: TPoint): Boolean; override;
|
||||||
|
public
|
||||||
|
constructor Create(AOwner: TComponent); override;
|
||||||
|
destructor Destroy; override;
|
||||||
|
procedure SetAppTheme(const T: TAppTheme);
|
||||||
|
procedure SelectAll;
|
||||||
|
|
||||||
|
property Value: Integer read FValue write SetValue;
|
||||||
|
property MinValue: Integer read FMinValue write SetMinValue;
|
||||||
|
property MaxValue: Integer read FMaxValue write SetMaxValue;
|
||||||
|
property Increment: Integer read FIncrement write SetIncrement;
|
||||||
|
property OnChange: TNotifyEvent read FOnChange write FOnChange;
|
||||||
|
property Align;
|
||||||
|
property Anchors;
|
||||||
|
property Enabled;
|
||||||
|
property Font;
|
||||||
|
property ParentShowHint;
|
||||||
|
property PopupMenu;
|
||||||
|
property ShowHint;
|
||||||
|
property TabOrder;
|
||||||
|
property TabStop;
|
||||||
|
property Tag;
|
||||||
|
property Visible;
|
||||||
|
property OnEnter;
|
||||||
|
property OnExit;
|
||||||
|
property OnKeyDown;
|
||||||
|
property OnKeyPress;
|
||||||
|
end;
|
||||||
|
|
||||||
|
implementation
|
||||||
|
|
||||||
|
const
|
||||||
|
BASE_PAD_X = 8;
|
||||||
|
BASE_BTN_W = 22;
|
||||||
|
|
||||||
|
constructor TFlatSpinEdit.Create(AOwner: TComponent);
|
||||||
|
begin
|
||||||
|
inherited Create(AOwner);
|
||||||
|
ControlStyle := ControlStyle + [csOpaque, csCaptureMouse, csClickEvents];
|
||||||
|
Width := 96;
|
||||||
|
Height := DpiScale(26);
|
||||||
|
TabStop := True;
|
||||||
|
Cursor := crIBeam;
|
||||||
|
|
||||||
|
Font.Name := 'Courier New';
|
||||||
|
Font.Size := 9;
|
||||||
|
|
||||||
|
FMinValue := 0;
|
||||||
|
FMaxValue := 100;
|
||||||
|
FIncrement := 1;
|
||||||
|
FValue := 0;
|
||||||
|
FText := '0';
|
||||||
|
FCaretPos := Length(FText);
|
||||||
|
FSelStart := FCaretPos;
|
||||||
|
|
||||||
|
FClrOuterBG := TColor($00181818);
|
||||||
|
FClrBG := TColor($001A1A1A);
|
||||||
|
FClrBGHot := TColor($00202020);
|
||||||
|
FClrBorder := TColor($00444444);
|
||||||
|
FClrText := TColor($00E0E0E0);
|
||||||
|
FClrTextDim := TColor($00666666);
|
||||||
|
FClrSelBG := TColor($000C3010);
|
||||||
|
FClrSelText := TColor($0000FF88);
|
||||||
|
FClrBtnHot := TColor($000C3010);
|
||||||
|
FClrBtnDown := TColor($00183618);
|
||||||
|
FClrBtnText := TColor($0000FF88);
|
||||||
|
Application.AddOnUserInputHandler(@AppUserInput);
|
||||||
|
end;
|
||||||
|
|
||||||
|
destructor TFlatSpinEdit.Destroy;
|
||||||
|
begin
|
||||||
|
Application.RemoveOnUserInputHandler(@AppUserInput);
|
||||||
|
inherited Destroy;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TFlatSpinEdit.DpiScale(V: Integer): Integer;
|
||||||
|
begin
|
||||||
|
Result := MulDiv(V, Screen.PixelsPerInch, 96);
|
||||||
|
if (V > 0) and (Result < 1) then
|
||||||
|
Result := 1;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TFlatSpinEdit.ButtonWidth: Integer;
|
||||||
|
begin
|
||||||
|
Result := DpiScale(BASE_BTN_W);
|
||||||
|
if Result > Width div 2 then
|
||||||
|
Result := Width div 2;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TFlatSpinEdit.EditRect: TRect;
|
||||||
|
begin
|
||||||
|
Result := Rect(0, 0, Width - ButtonWidth, Height);
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TFlatSpinEdit.UpRect: TRect;
|
||||||
|
begin
|
||||||
|
Result := Rect(Width - ButtonWidth, 0, Width, Height div 2);
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TFlatSpinEdit.DownRect: TRect;
|
||||||
|
begin
|
||||||
|
Result := Rect(Width - ButtonWidth, Height div 2, Width, Height);
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TFlatSpinEdit.PartAt(X, Y: Integer): TFlatSpinPart;
|
||||||
|
begin
|
||||||
|
if not PtInRect(ClientRect, Point(X, Y)) then
|
||||||
|
Exit(fspNone);
|
||||||
|
if PtInRect(UpRect, Point(X, Y)) then
|
||||||
|
Exit(fspUp);
|
||||||
|
if PtInRect(DownRect, Point(X, Y)) then
|
||||||
|
Exit(fspDown);
|
||||||
|
Result := fspEdit;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatSpinEdit.SetAppTheme(const T: TAppTheme);
|
||||||
|
begin
|
||||||
|
FClrOuterBG := T.Panel;
|
||||||
|
FClrBG := T.BtnNorm;
|
||||||
|
FClrBGHot := T.BtnHot;
|
||||||
|
FClrBorder := T.Border;
|
||||||
|
FClrText := T.Text;
|
||||||
|
FClrTextDim := T.TextDim;
|
||||||
|
FClrSelBG := T.BtnActive;
|
||||||
|
FClrSelText := T.BtnTextActive;
|
||||||
|
FClrBtnHot := T.BtnActive;
|
||||||
|
FClrBtnDown := T.SliderTrackFill;
|
||||||
|
FClrBtnText := T.BtnTextActive;
|
||||||
|
Font.Color := T.Text;
|
||||||
|
Invalidate;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TFlatSpinEdit.HasSelection: Boolean;
|
||||||
|
begin
|
||||||
|
Result := FSelLen <> 0;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TFlatSpinEdit.SelFirst: Integer;
|
||||||
|
begin
|
||||||
|
if FSelLen >= 0 then
|
||||||
|
Result := FSelStart
|
||||||
|
else
|
||||||
|
Result := FSelStart + FSelLen;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TFlatSpinEdit.SelAfter: Integer;
|
||||||
|
begin
|
||||||
|
if FSelLen >= 0 then
|
||||||
|
Result := FSelStart + FSelLen
|
||||||
|
else
|
||||||
|
Result := FSelStart;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TFlatSpinEdit.CaretPosAtX(X: Integer): Integer;
|
||||||
|
var
|
||||||
|
I, Pad, RelX, BestDist, Dist: Integer;
|
||||||
|
begin
|
||||||
|
Pad := DpiScale(BASE_PAD_X);
|
||||||
|
RelX := X - Pad;
|
||||||
|
Result := 0;
|
||||||
|
BestDist := Abs(RelX);
|
||||||
|
for I := 1 to Length(FText) do
|
||||||
|
begin
|
||||||
|
Dist := Abs(RelX - Canvas.TextWidth(Copy(FText, 1, I)));
|
||||||
|
if Dist < BestDist then
|
||||||
|
begin
|
||||||
|
BestDist := Dist;
|
||||||
|
Result := I;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatSpinEdit.SetTextInternal(const S: string; AFireChange: Boolean);
|
||||||
|
begin
|
||||||
|
FText := S;
|
||||||
|
FCaretPos := EnsureRange(FCaretPos, 0, Length(FText));
|
||||||
|
FSelStart := FCaretPos;
|
||||||
|
FSelLen := 0;
|
||||||
|
Invalidate;
|
||||||
|
if AFireChange then
|
||||||
|
DoChange;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatSpinEdit.SetValue(V: Integer);
|
||||||
|
begin
|
||||||
|
V := EnsureRange(V, FMinValue, FMaxValue);
|
||||||
|
if (FValue = V) and (FText = IntToStr(V)) then Exit;
|
||||||
|
FValue := V;
|
||||||
|
FText := IntToStr(FValue);
|
||||||
|
FCaretPos := Length(FText);
|
||||||
|
FSelStart := FCaretPos;
|
||||||
|
FSelLen := 0;
|
||||||
|
Invalidate;
|
||||||
|
DoChange;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatSpinEdit.SetMinValue(V: Integer);
|
||||||
|
begin
|
||||||
|
FMinValue := V;
|
||||||
|
if FMaxValue < FMinValue then
|
||||||
|
FMaxValue := FMinValue;
|
||||||
|
SetValue(FValue);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatSpinEdit.SetMaxValue(V: Integer);
|
||||||
|
begin
|
||||||
|
FMaxValue := V;
|
||||||
|
if FMinValue > FMaxValue then
|
||||||
|
FMinValue := FMaxValue;
|
||||||
|
SetValue(FValue);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatSpinEdit.SetIncrement(V: Integer);
|
||||||
|
begin
|
||||||
|
FIncrement := Max(1, Abs(V));
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatSpinEdit.DeleteSelection;
|
||||||
|
var
|
||||||
|
A, B: Integer;
|
||||||
|
begin
|
||||||
|
if not HasSelection then Exit;
|
||||||
|
A := SelFirst;
|
||||||
|
B := SelAfter;
|
||||||
|
Delete(FText, A + 1, B - A);
|
||||||
|
FCaretPos := A;
|
||||||
|
FSelStart := FCaretPos;
|
||||||
|
FSelLen := 0;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatSpinEdit.InsertText(const S: string);
|
||||||
|
begin
|
||||||
|
if S = '' then Exit;
|
||||||
|
DeleteSelection;
|
||||||
|
Insert(S, FText, FCaretPos + 1);
|
||||||
|
Inc(FCaretPos, Length(S));
|
||||||
|
FSelStart := FCaretPos;
|
||||||
|
FSelLen := 0;
|
||||||
|
UpdateValueFromText;
|
||||||
|
Invalidate;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatSpinEdit.UpdateValueFromText;
|
||||||
|
var
|
||||||
|
Code, V: Integer;
|
||||||
|
begin
|
||||||
|
Val(FText, V, Code);
|
||||||
|
if (Code <> 0) or (V < FMinValue) or (V > FMaxValue) then Exit;
|
||||||
|
if FValue = V then Exit;
|
||||||
|
FValue := V;
|
||||||
|
DoChange;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatSpinEdit.CommitText;
|
||||||
|
var
|
||||||
|
Code, V: Integer;
|
||||||
|
begin
|
||||||
|
if (FText = '') or (FText = '-') then
|
||||||
|
begin
|
||||||
|
SetValue(FValue);
|
||||||
|
Exit;
|
||||||
|
end;
|
||||||
|
Val(FText, V, Code);
|
||||||
|
if Code <> 0 then
|
||||||
|
V := FValue;
|
||||||
|
SetValue(V);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatSpinEdit.StepValue(Dir: Integer);
|
||||||
|
begin
|
||||||
|
CommitText;
|
||||||
|
SetValue(FValue + Dir * FIncrement);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatSpinEdit.DoChange;
|
||||||
|
begin
|
||||||
|
if Assigned(FOnChange) then
|
||||||
|
FOnChange(Self);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatSpinEdit.AppUserInput(Sender: TObject; Msg: Cardinal);
|
||||||
|
var
|
||||||
|
P: TPoint;
|
||||||
|
begin
|
||||||
|
if not FEditing then Exit;
|
||||||
|
case Msg of
|
||||||
|
LM_LBUTTONDOWN, LM_RBUTTONDOWN, LM_MBUTTONDOWN: ;
|
||||||
|
else
|
||||||
|
Exit;
|
||||||
|
end;
|
||||||
|
|
||||||
|
P := ScreenToClient(Mouse.CursorPos);
|
||||||
|
if PtInRect(ClientRect, P) then Exit;
|
||||||
|
|
||||||
|
CommitText;
|
||||||
|
FDragging := False;
|
||||||
|
MouseCapture := False;
|
||||||
|
FEditing := False;
|
||||||
|
FHot := False;
|
||||||
|
FHotPart := fspNone;
|
||||||
|
FDownPart := fspNone;
|
||||||
|
FSelLen := 0;
|
||||||
|
Invalidate;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatSpinEdit.Paint;
|
||||||
|
var
|
||||||
|
R, ER, UR, DR, TextR, SelR: TRect;
|
||||||
|
Pad, TextY, CaretX, A, B, MidX, MidY, S: Integer;
|
||||||
|
BeforeSel, SelText: string;
|
||||||
|
|
||||||
|
procedure FillPart(const AR: TRect; Part: TFlatSpinPart);
|
||||||
|
begin
|
||||||
|
if not Enabled then
|
||||||
|
Canvas.Brush.Color := FClrBG
|
||||||
|
else if FDownPart = Part then
|
||||||
|
Canvas.Brush.Color := FClrBtnDown
|
||||||
|
else if FHotPart = Part then
|
||||||
|
Canvas.Brush.Color := FClrBtnHot
|
||||||
|
else if FHot or FEditing or Focused then
|
||||||
|
Canvas.Brush.Color := FClrBGHot
|
||||||
|
else
|
||||||
|
Canvas.Brush.Color := FClrBG;
|
||||||
|
Canvas.Pen.Style := psClear;
|
||||||
|
Canvas.Brush.Style := bsSolid;
|
||||||
|
Canvas.FillRect(AR);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure DrawArrow(const AR: TRect; Up: Boolean);
|
||||||
|
var
|
||||||
|
P: array[0..2] of TPoint;
|
||||||
|
Size: Integer;
|
||||||
|
begin
|
||||||
|
Size := Max(3, DpiScale(4));
|
||||||
|
MidX := (AR.Left + AR.Right) div 2;
|
||||||
|
MidY := (AR.Top + AR.Bottom) div 2;
|
||||||
|
if Up then
|
||||||
|
begin
|
||||||
|
P[0] := Point(MidX, MidY - Size div 2);
|
||||||
|
P[1] := Point(MidX - Size, MidY + Size div 2);
|
||||||
|
P[2] := Point(MidX + Size, MidY + Size div 2);
|
||||||
|
end
|
||||||
|
else
|
||||||
|
begin
|
||||||
|
P[0] := Point(MidX - Size, MidY - Size div 2);
|
||||||
|
P[1] := Point(MidX + Size, MidY - Size div 2);
|
||||||
|
P[2] := Point(MidX, MidY + Size div 2);
|
||||||
|
end;
|
||||||
|
if Enabled then
|
||||||
|
Canvas.Brush.Color := FClrBtnText
|
||||||
|
else
|
||||||
|
Canvas.Brush.Color := FClrTextDim;
|
||||||
|
Canvas.Pen.Style := psClear;
|
||||||
|
Canvas.Polygon(P);
|
||||||
|
end;
|
||||||
|
begin
|
||||||
|
R := ClientRect;
|
||||||
|
ER := EditRect;
|
||||||
|
UR := UpRect;
|
||||||
|
DR := DownRect;
|
||||||
|
|
||||||
|
Canvas.Brush.Color := FClrOuterBG;
|
||||||
|
Canvas.Brush.Style := bsSolid;
|
||||||
|
Canvas.Pen.Style := psClear;
|
||||||
|
Canvas.FillRect(R);
|
||||||
|
|
||||||
|
if FHot or FEditing or Focused then
|
||||||
|
Canvas.Brush.Color := FClrBGHot
|
||||||
|
else
|
||||||
|
Canvas.Brush.Color := FClrBG;
|
||||||
|
Canvas.Pen.Color := FClrBorder;
|
||||||
|
Canvas.Pen.Style := psSolid;
|
||||||
|
Canvas.Pen.Width := 1;
|
||||||
|
Canvas.Rectangle(R.Left, R.Top, R.Right, R.Bottom);
|
||||||
|
|
||||||
|
FillPart(UR, fspUp);
|
||||||
|
FillPart(DR, fspDown);
|
||||||
|
Canvas.Pen.Color := FClrBorder;
|
||||||
|
Canvas.Pen.Style := psSolid;
|
||||||
|
Canvas.Line(UR.Left, 1, UR.Left, Height - 1);
|
||||||
|
Canvas.Line(UR.Left, DR.Top, Width - 1, DR.Top);
|
||||||
|
DrawArrow(UR, True);
|
||||||
|
DrawArrow(DR, False);
|
||||||
|
|
||||||
|
Canvas.Font.Assign(Font);
|
||||||
|
if Enabled then
|
||||||
|
Canvas.Font.Color := FClrText
|
||||||
|
else
|
||||||
|
Canvas.Font.Color := FClrTextDim;
|
||||||
|
Pad := DpiScale(BASE_PAD_X);
|
||||||
|
TextR := Rect(ER.Left + Pad, ER.Top, ER.Right - DpiScale(3), ER.Bottom);
|
||||||
|
TextY := (Height - Canvas.TextHeight('Ag')) div 2;
|
||||||
|
|
||||||
|
if HasSelection then
|
||||||
|
begin
|
||||||
|
A := SelFirst;
|
||||||
|
B := SelAfter;
|
||||||
|
BeforeSel := Copy(FText, 1, A);
|
||||||
|
SelText := Copy(FText, A + 1, B - A);
|
||||||
|
SelR := Rect(TextR.Left + Canvas.TextWidth(BeforeSel), TextY,
|
||||||
|
TextR.Left + Canvas.TextWidth(BeforeSel + SelText), TextY + Canvas.TextHeight('Ag'));
|
||||||
|
Canvas.Brush.Color := FClrSelBG;
|
||||||
|
Canvas.Pen.Style := psClear;
|
||||||
|
Canvas.FillRect(SelR);
|
||||||
|
end;
|
||||||
|
|
||||||
|
Canvas.Brush.Style := bsClear;
|
||||||
|
Canvas.TextRect(TextR, TextR.Left, TextY, FText);
|
||||||
|
|
||||||
|
if HasSelection then
|
||||||
|
begin
|
||||||
|
A := SelFirst;
|
||||||
|
B := SelAfter;
|
||||||
|
BeforeSel := Copy(FText, 1, A);
|
||||||
|
SelText := Copy(FText, A + 1, B - A);
|
||||||
|
Canvas.Font.Color := FClrSelText;
|
||||||
|
Canvas.TextOut(TextR.Left + Canvas.TextWidth(BeforeSel), TextY, SelText);
|
||||||
|
end;
|
||||||
|
|
||||||
|
if FEditing and not HasSelection then
|
||||||
|
begin
|
||||||
|
S := EnsureRange(FCaretPos, 0, Length(FText));
|
||||||
|
CaretX := TextR.Left + Canvas.TextWidth(Copy(FText, 1, S));
|
||||||
|
Canvas.Pen.Color := FClrText;
|
||||||
|
Canvas.Pen.Style := psSolid;
|
||||||
|
Canvas.Pen.Width := 1;
|
||||||
|
Canvas.Line(CaretX, TextY, CaretX, TextY + Canvas.TextHeight('Ag'));
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatSpinEdit.MouseEnter;
|
||||||
|
begin
|
||||||
|
inherited;
|
||||||
|
FHot := True;
|
||||||
|
Invalidate;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatSpinEdit.MouseLeave;
|
||||||
|
begin
|
||||||
|
inherited;
|
||||||
|
FHot := False;
|
||||||
|
FHotPart := fspNone;
|
||||||
|
Invalidate;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatSpinEdit.MouseDown(Button: TMouseButton; Shift: TShiftState;
|
||||||
|
X, Y: Integer);
|
||||||
|
var
|
||||||
|
Pos: Integer;
|
||||||
|
begin
|
||||||
|
inherited;
|
||||||
|
if (Button <> mbLeft) or not Enabled then Exit;
|
||||||
|
SetFocus;
|
||||||
|
FEditing := True;
|
||||||
|
FDownPart := PartAt(X, Y);
|
||||||
|
FHotPart := FDownPart;
|
||||||
|
if FDownPart in [fspUp, fspDown] then
|
||||||
|
begin
|
||||||
|
FDragging := False;
|
||||||
|
MouseCapture := True;
|
||||||
|
if FDownPart = fspUp then
|
||||||
|
StepValue(1)
|
||||||
|
else
|
||||||
|
StepValue(-1);
|
||||||
|
end
|
||||||
|
else
|
||||||
|
begin
|
||||||
|
FDragging := True;
|
||||||
|
MouseCapture := True;
|
||||||
|
Pos := CaretPosAtX(X);
|
||||||
|
FCaretPos := Pos;
|
||||||
|
FSelStart := Pos;
|
||||||
|
FSelLen := 0;
|
||||||
|
end;
|
||||||
|
Invalidate;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatSpinEdit.MouseMove(Shift: TShiftState; X, Y: Integer);
|
||||||
|
var
|
||||||
|
Pos: Integer;
|
||||||
|
Part: TFlatSpinPart;
|
||||||
|
begin
|
||||||
|
inherited;
|
||||||
|
Part := PartAt(X, Y);
|
||||||
|
if FHotPart <> Part then
|
||||||
|
begin
|
||||||
|
FHotPart := Part;
|
||||||
|
if Part = fspEdit then
|
||||||
|
Cursor := crIBeam
|
||||||
|
else
|
||||||
|
Cursor := crDefault;
|
||||||
|
Invalidate;
|
||||||
|
end;
|
||||||
|
|
||||||
|
if not FDragging then Exit;
|
||||||
|
Pos := CaretPosAtX(X);
|
||||||
|
FCaretPos := Pos;
|
||||||
|
FSelLen := Pos - FSelStart;
|
||||||
|
Invalidate;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatSpinEdit.MouseUp(Button: TMouseButton; Shift: TShiftState;
|
||||||
|
X, Y: Integer);
|
||||||
|
begin
|
||||||
|
inherited;
|
||||||
|
if Button <> mbLeft then Exit;
|
||||||
|
FDragging := False;
|
||||||
|
FDownPart := fspNone;
|
||||||
|
MouseCapture := False;
|
||||||
|
Invalidate;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatSpinEdit.DblClick;
|
||||||
|
begin
|
||||||
|
inherited;
|
||||||
|
if FHotPart <> fspEdit then Exit;
|
||||||
|
FEditing := True;
|
||||||
|
SelectAll;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatSpinEdit.KeyDown(var Key: Word; Shift: TShiftState);
|
||||||
|
begin
|
||||||
|
inherited;
|
||||||
|
if not Enabled then Exit;
|
||||||
|
FEditing := True;
|
||||||
|
case Key of
|
||||||
|
VK_LEFT:
|
||||||
|
begin
|
||||||
|
FCaretPos := EnsureRange(FCaretPos - 1, 0, Length(FText));
|
||||||
|
FSelStart := FCaretPos;
|
||||||
|
FSelLen := 0;
|
||||||
|
UpdateValueFromText;
|
||||||
|
Invalidate;
|
||||||
|
Key := 0;
|
||||||
|
end;
|
||||||
|
VK_RIGHT:
|
||||||
|
begin
|
||||||
|
FCaretPos := EnsureRange(FCaretPos + 1, 0, Length(FText));
|
||||||
|
FSelStart := FCaretPos;
|
||||||
|
FSelLen := 0;
|
||||||
|
UpdateValueFromText;
|
||||||
|
Invalidate;
|
||||||
|
Key := 0;
|
||||||
|
end;
|
||||||
|
VK_HOME:
|
||||||
|
begin
|
||||||
|
FCaretPos := 0;
|
||||||
|
FSelStart := FCaretPos;
|
||||||
|
FSelLen := 0;
|
||||||
|
Invalidate;
|
||||||
|
Key := 0;
|
||||||
|
end;
|
||||||
|
VK_END:
|
||||||
|
begin
|
||||||
|
FCaretPos := Length(FText);
|
||||||
|
FSelStart := FCaretPos;
|
||||||
|
FSelLen := 0;
|
||||||
|
Invalidate;
|
||||||
|
Key := 0;
|
||||||
|
end;
|
||||||
|
VK_UP:
|
||||||
|
begin
|
||||||
|
StepValue(1);
|
||||||
|
Key := 0;
|
||||||
|
end;
|
||||||
|
VK_DOWN:
|
||||||
|
begin
|
||||||
|
StepValue(-1);
|
||||||
|
Key := 0;
|
||||||
|
end;
|
||||||
|
VK_RETURN:
|
||||||
|
begin
|
||||||
|
CommitText;
|
||||||
|
SelectAll;
|
||||||
|
Key := 0;
|
||||||
|
end;
|
||||||
|
VK_ESCAPE:
|
||||||
|
begin
|
||||||
|
SetTextInternal(IntToStr(FValue), False);
|
||||||
|
Key := 0;
|
||||||
|
end;
|
||||||
|
VK_BACK:
|
||||||
|
begin
|
||||||
|
if HasSelection then
|
||||||
|
DeleteSelection
|
||||||
|
else if FCaretPos > 0 then
|
||||||
|
begin
|
||||||
|
Delete(FText, FCaretPos, 1);
|
||||||
|
Dec(FCaretPos);
|
||||||
|
end;
|
||||||
|
FSelStart := FCaretPos;
|
||||||
|
FSelLen := 0;
|
||||||
|
Invalidate;
|
||||||
|
Key := 0;
|
||||||
|
end;
|
||||||
|
VK_DELETE:
|
||||||
|
begin
|
||||||
|
if HasSelection then
|
||||||
|
DeleteSelection
|
||||||
|
else if FCaretPos < Length(FText) then
|
||||||
|
Delete(FText, FCaretPos + 1, 1);
|
||||||
|
FSelStart := FCaretPos;
|
||||||
|
FSelLen := 0;
|
||||||
|
Invalidate;
|
||||||
|
Key := 0;
|
||||||
|
end;
|
||||||
|
Ord('A'):
|
||||||
|
if ssCtrl in Shift then
|
||||||
|
begin
|
||||||
|
SelectAll;
|
||||||
|
Key := 0;
|
||||||
|
end;
|
||||||
|
Ord('C'):
|
||||||
|
if (ssCtrl in Shift) and HasSelection then
|
||||||
|
begin
|
||||||
|
Clipboard.AsText := Copy(FText, SelFirst + 1, SelAfter - SelFirst);
|
||||||
|
Key := 0;
|
||||||
|
end;
|
||||||
|
Ord('X'):
|
||||||
|
if (ssCtrl in Shift) and HasSelection then
|
||||||
|
begin
|
||||||
|
Clipboard.AsText := Copy(FText, SelFirst + 1, SelAfter - SelFirst);
|
||||||
|
DeleteSelection;
|
||||||
|
UpdateValueFromText;
|
||||||
|
Invalidate;
|
||||||
|
Key := 0;
|
||||||
|
end;
|
||||||
|
Ord('V'):
|
||||||
|
if ssCtrl in Shift then
|
||||||
|
begin
|
||||||
|
InsertText(Clipboard.AsText);
|
||||||
|
Key := 0;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatSpinEdit.KeyPress(var Key: char);
|
||||||
|
begin
|
||||||
|
inherited;
|
||||||
|
if not Enabled then Exit;
|
||||||
|
FEditing := True;
|
||||||
|
if Key in ['0'..'9'] then
|
||||||
|
begin
|
||||||
|
InsertText(Key);
|
||||||
|
Key := #0;
|
||||||
|
end
|
||||||
|
else if (Key = '-') and (FMinValue < 0) and (FCaretPos = 0) and (Pos('-', FText) = 0) then
|
||||||
|
begin
|
||||||
|
InsertText(Key);
|
||||||
|
Key := #0;
|
||||||
|
end
|
||||||
|
else if Key >= #32 then
|
||||||
|
Key := #0;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatSpinEdit.DoEnter;
|
||||||
|
begin
|
||||||
|
inherited;
|
||||||
|
FEditing := True;
|
||||||
|
Invalidate;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatSpinEdit.DoExit;
|
||||||
|
begin
|
||||||
|
inherited;
|
||||||
|
CommitText;
|
||||||
|
FEditing := False;
|
||||||
|
FHot := False;
|
||||||
|
FHotPart := fspNone;
|
||||||
|
FDownPart := fspNone;
|
||||||
|
FSelLen := 0;
|
||||||
|
Invalidate;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TFlatSpinEdit.DoMouseWheelUp(Shift: TShiftState; MousePos: TPoint): Boolean;
|
||||||
|
begin
|
||||||
|
if Enabled then
|
||||||
|
StepValue(1);
|
||||||
|
Result := True;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TFlatSpinEdit.DoMouseWheelDown(Shift: TShiftState; MousePos: TPoint): Boolean;
|
||||||
|
begin
|
||||||
|
if Enabled then
|
||||||
|
StepValue(-1);
|
||||||
|
Result := True;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFlatSpinEdit.SelectAll;
|
||||||
|
begin
|
||||||
|
FSelStart := 0;
|
||||||
|
FSelLen := Length(FText);
|
||||||
|
FCaretPos := Length(FText);
|
||||||
|
Invalidate;
|
||||||
|
end;
|
||||||
|
|
||||||
|
end.
|
||||||
+188
-178
@@ -26,8 +26,10 @@ interface
|
|||||||
uses
|
uses
|
||||||
Classes, SysUtils, Math,
|
Classes, SysUtils, Math,
|
||||||
Forms, Controls, Graphics, Dialogs,
|
Forms, Controls, Graphics, Dialogs,
|
||||||
StdCtrls, ExtCtrls, Spin,
|
StdCtrls, ExtCtrls,
|
||||||
FlatButton, EqualizerControl, AudioOutput, AudioInput, AppTheme, Settings;
|
FlatButton, FlatCheckBox, FlatComboBox, FlatEdit, FlatSpinEdit, FlatFloatSpinEdit,
|
||||||
|
FlatRadioButton,
|
||||||
|
EqualizerControl, AudioOutput, AudioInput, AppTheme, Settings;
|
||||||
|
|
||||||
type
|
type
|
||||||
// PA: MaxPower в Вт, BandCal[0..10] — калибровка 38.8..100.0 на диапазон
|
// PA: MaxPower в Вт, BandCal[0..10] — калибровка 38.8..100.0 на диапазон
|
||||||
@@ -89,125 +91,125 @@ type
|
|||||||
FNavTransmit: TFlatButton;
|
FNavTransmit: TFlatButton;
|
||||||
|
|
||||||
// ---- Audio tab controls ----
|
// ---- Audio tab controls ----
|
||||||
FCmbRXDev: TComboBox;
|
FCmbRXDev: TFlatComboBox;
|
||||||
FRXDevIndices: array[0..63] of Integer;
|
FRXDevIndices: array[0..63] of Integer;
|
||||||
FRXDevCount: Integer;
|
FRXDevCount: Integer;
|
||||||
FCmbTXDev: TComboBox;
|
FCmbTXDev: TFlatComboBox;
|
||||||
FTXDevIndices: array[0..63] of Integer;
|
FTXDevIndices: array[0..63] of Integer;
|
||||||
FTXDevCount: Integer;
|
FTXDevCount: Integer;
|
||||||
FCmbAudioBuffer: TComboBox;
|
FCmbAudioBuffer: TFlatComboBox;
|
||||||
|
|
||||||
// ---- RX1 sub-tab controls ----
|
// ---- RX1 sub-tab controls ----
|
||||||
FCmbFFTSize: TComboBox;
|
FCmbFFTSize: TFlatComboBox;
|
||||||
FCmbWindow: TComboBox;
|
FCmbWindow: TFlatComboBox;
|
||||||
FCmbSpecDet: TComboBox;
|
FCmbSpecDet: TFlatComboBox;
|
||||||
FCmbSpecAvg: TComboBox;
|
FCmbSpecAvg: TFlatComboBox;
|
||||||
FSpecAvgTime: TFloatSpinEdit;
|
FSpecAvgTime: TFlatFloatSpinEdit;
|
||||||
FCmbWfDet: TComboBox;
|
FCmbWfDet: TFlatComboBox;
|
||||||
FCmbWfAvg: TComboBox;
|
FCmbWfAvg: TFlatComboBox;
|
||||||
FWfAvgTime: TFloatSpinEdit;
|
FWfAvgTime: TFlatFloatSpinEdit;
|
||||||
FWfHigh: TFloatSpinEdit;
|
FWfHigh: TFlatFloatSpinEdit;
|
||||||
FWfLow: TFloatSpinEdit;
|
FWfLow: TFlatFloatSpinEdit;
|
||||||
FWfAGCOffset: TFloatSpinEdit;
|
FWfAGCOffset: TFlatFloatSpinEdit;
|
||||||
FChkWfAGC: TCheckBox;
|
FChkWfAGC: TFlatCheckBox;
|
||||||
FChkWfNF: TCheckBox;
|
FChkWfNF: TFlatCheckBox;
|
||||||
FEdRefLevel: TEdit;
|
FEdRefLevel: TFlatEdit;
|
||||||
FEdRange: TEdit;
|
FEdRange: TFlatEdit;
|
||||||
FCmbGridStep: TComboBox;
|
FCmbGridStep: TFlatComboBox;
|
||||||
|
|
||||||
// ---- General display tab controls ----
|
// ---- General display tab controls ----
|
||||||
FChkShowSpectrum: TCheckBox;
|
FChkShowSpectrum: TFlatCheckBox;
|
||||||
FChkShowWaterfall: TCheckBox;
|
FChkShowWaterfall: TFlatCheckBox;
|
||||||
FCmbFPS: TComboBox;
|
FCmbFPS: TFlatComboBox;
|
||||||
FChkLightTheme: TCheckBox;
|
FChkLightTheme: TFlatCheckBox;
|
||||||
FCmbFreqMhzDigits: TComboBox;
|
FCmbFreqMhzDigits: TFlatComboBox;
|
||||||
|
|
||||||
// ---- PA Settings tab controls ----
|
// ---- PA Settings tab controls ----
|
||||||
FEdMaxPower: TSpinEdit;
|
FEdMaxPower: TFlatSpinEdit;
|
||||||
FEdBandCal: array[0..10] of TFloatSpinEdit;
|
FEdBandCal: array[0..10] of TFlatFloatSpinEdit;
|
||||||
FEdVHFCal: array[0..CFG_XVTR_COUNT-1] of TFloatSpinEdit;
|
FEdVHFCal: array[0..CFG_XVTR_COUNT-1] of TFlatFloatSpinEdit;
|
||||||
FVHFCalLabels: array[0..CFG_XVTR_COUNT-1] of TLabel;
|
FVHFCalLabels: array[0..CFG_XVTR_COUNT-1] of TLabel;
|
||||||
|
|
||||||
// ---- Transmit tab controls ----
|
// ---- Transmit tab controls ----
|
||||||
// Текущее состояние, отдаётся в OnTXChange целиком при любой правке.
|
// Текущее состояние, отдаётся в OnTXChange целиком при любой правке.
|
||||||
FTX: TTXSettings;
|
FTX: TTXSettings;
|
||||||
FCmbTXMicJack: TComboBox; // "Mic In" / "Line In"
|
FCmbTXMicJack: TFlatComboBox; // "Mic In" / "Line In"
|
||||||
FChkTXMicBoost: TCheckBox; // +20 dB (только Mic In)
|
FChkTXMicBoost: TFlatCheckBox; // +20 dB (только Mic In)
|
||||||
FLblLineInGain: TLabel; // метка "Line In gain" (только Line In)
|
FLblLineInGain: TLabel; // метка "Line In gain" (только Line In)
|
||||||
FEdLineInGain: TFloatSpinEdit; // -34.5..+12 dB, шаг 1.5 (только Line In)
|
FEdLineInGain: TFlatFloatSpinEdit; // -34.5..+12 dB, шаг 1.5 (только Line In)
|
||||||
FChkTXMicBias: TCheckBox;
|
FChkTXMicBias: TFlatCheckBox;
|
||||||
FChkTXMicPTT: TCheckBox;
|
FChkTXMicPTT: TFlatCheckBox;
|
||||||
FCmbTXMicTR: TComboBox; // "Tip" / "Ring" (только Orion)
|
FCmbTXMicTR: TFlatComboBox; // "Tip" / "Ring" (только Orion)
|
||||||
FLblTipRing: TLabel; // метка (только Orion)
|
FLblTipRing: TLabel; // метка (только Orion)
|
||||||
FEdTXMicGain: TFloatSpinEdit;
|
FEdTXMicGain: TFlatFloatSpinEdit;
|
||||||
FEdTXAttOnTX: TSpinEdit;
|
FEdTXAttOnTX: TFlatSpinEdit;
|
||||||
// Tune (TUN button)
|
// Tune (TUN button)
|
||||||
FEdTUNLevel: TSpinEdit;
|
FEdTUNLevel: TFlatSpinEdit;
|
||||||
FEdTUNFreq: TSpinEdit;
|
FEdTUNFreq: TFlatSpinEdit;
|
||||||
// Filter
|
// Filter
|
||||||
FEdTXFiltLow: TSpinEdit;
|
FEdTXFiltLow: TFlatSpinEdit;
|
||||||
FEdTXFiltHigh: TSpinEdit;
|
FEdTXFiltHigh: TFlatSpinEdit;
|
||||||
FCmbTXFiltNC: TComboBox;
|
FCmbTXFiltNC: TFlatComboBox;
|
||||||
FCmbTXFiltMP: TComboBox;
|
FCmbTXFiltMP: TFlatComboBox;
|
||||||
FCmbTXFiltWindow: TComboBox;
|
FCmbTXFiltWindow: TFlatComboBox;
|
||||||
// Compressor
|
// Compressor
|
||||||
FChkTXCompOn: TCheckBox;
|
FChkTXCompOn: TFlatCheckBox;
|
||||||
FEdTXCompGain: TFloatSpinEdit;
|
FEdTXCompGain: TFlatFloatSpinEdit;
|
||||||
// Leveler
|
// Leveler
|
||||||
FChkTXLevOn: TCheckBox;
|
FChkTXLevOn: TFlatCheckBox;
|
||||||
FEdTXLevTop: TFloatSpinEdit;
|
FEdTXLevTop: TFlatFloatSpinEdit;
|
||||||
FEdTXLevDecay: TSpinEdit;
|
FEdTXLevDecay: TFlatSpinEdit;
|
||||||
// ALC
|
// ALC
|
||||||
FChkTXALCOn: TCheckBox;
|
FChkTXALCOn: TFlatCheckBox;
|
||||||
FEdTXALCMaxGain: TFloatSpinEdit;
|
FEdTXALCMaxGain: TFlatFloatSpinEdit;
|
||||||
FEdTXALCDecay: TSpinEdit;
|
FEdTXALCDecay: TFlatSpinEdit;
|
||||||
// Phase Rotator
|
// Phase Rotator
|
||||||
FChkTXPHROn: TCheckBox;
|
FChkTXPHROn: TFlatCheckBox;
|
||||||
FEdTXPHRStages: TSpinEdit;
|
FEdTXPHRStages: TFlatSpinEdit;
|
||||||
FEdTXPHRFreq: TFloatSpinEdit;
|
FEdTXPHRFreq: TFlatFloatSpinEdit;
|
||||||
// EQ
|
// EQ
|
||||||
FTXEQControl: TEqualizerControl;
|
FTXEQControl: TEqualizerControl;
|
||||||
// AM
|
// AM
|
||||||
FEdTXAMCarrier: TFloatSpinEdit;
|
FEdTXAMCarrier: TFlatFloatSpinEdit;
|
||||||
// FM
|
// FM
|
||||||
FEdTXFMDev: TFloatSpinEdit;
|
FEdTXFMDev: TFlatFloatSpinEdit;
|
||||||
FEdTXFMLow: TSpinEdit;
|
FEdTXFMLow: TFlatSpinEdit;
|
||||||
FEdTXFMHigh: TSpinEdit;
|
FEdTXFMHigh: TFlatSpinEdit;
|
||||||
FCmbTXFMEmph: TComboBox;
|
FCmbTXFMEmph: TFlatComboBox;
|
||||||
// CTCSS
|
// CTCSS
|
||||||
FChkTXCTCSSOn: TCheckBox;
|
FChkTXCTCSSOn: TFlatCheckBox;
|
||||||
FEdTXCTCSSFreq: TFloatSpinEdit;
|
FEdTXCTCSSFreq: TFlatFloatSpinEdit;
|
||||||
// TX Display (отдельный analyzer)
|
// TX Display (отдельный analyzer)
|
||||||
FCmbTXFFT: TComboBox;
|
FCmbTXFFT: TFlatComboBox;
|
||||||
FCmbTXWindow: TComboBox;
|
FCmbTXWindow: TFlatComboBox;
|
||||||
FCmbTXSpecDet: TComboBox;
|
FCmbTXSpecDet: TFlatComboBox;
|
||||||
FCmbTXSpecAvg: TComboBox;
|
FCmbTXSpecAvg: TFlatComboBox;
|
||||||
FEdTXSpecAvgTime: TFloatSpinEdit;
|
FEdTXSpecAvgTime: TFlatFloatSpinEdit;
|
||||||
FCmbTXWfDet: TComboBox;
|
FCmbTXWfDet: TFlatComboBox;
|
||||||
FCmbTXWfAvg: TComboBox;
|
FCmbTXWfAvg: TFlatComboBox;
|
||||||
FEdTXWfAvgTime: TFloatSpinEdit;
|
FEdTXWfAvgTime: TFlatFloatSpinEdit;
|
||||||
// TX Grid (отдельные от RX grid в Display)
|
// TX Grid (отдельные от RX grid в Display)
|
||||||
FEdTXRefLevel: TEdit;
|
FEdTXRefLevel: TFlatEdit;
|
||||||
FEdTXRange: TEdit;
|
FEdTXRange: TFlatEdit;
|
||||||
FCmbTXGridStep: TComboBox;
|
FCmbTXGridStep: TFlatComboBox;
|
||||||
|
|
||||||
// ---- Alex / Antenna tab controls ----
|
// ---- Alex / Antenna tab controls ----
|
||||||
FPageAlex: TScrollBox;
|
FPageAlex: TScrollBox;
|
||||||
FNavAlex: TFlatButton;
|
FNavAlex: TFlatButton;
|
||||||
// Per-band antenna radio buttons (bands 0..10 = 160m..6m)
|
// Per-band antenna radio buttons (bands 0..10 = 160m..6m)
|
||||||
FAlexRxAnt: array[0..10, 1..3] of TRadioButton;
|
FAlexRxAnt: array[0..10, 1..3] of TFlatRadioButton;
|
||||||
FAlexTxAnt: array[0..10, 1..3] of TRadioButton;
|
FAlexTxAnt: array[0..10, 1..3] of TFlatRadioButton;
|
||||||
// Per-band secondary RX (взаимно исключающие checkboxes)
|
// Per-band secondary RX (взаимно исключающие checkboxes)
|
||||||
FAlexRxByps: array[0..10] of TCheckBox; // BYPS: Ext1+Bypass
|
FAlexRxByps: array[0..10] of TFlatCheckBox; // BYPS: Ext1+Bypass
|
||||||
FAlexRxExt1: array[0..10] of TCheckBox; // EXT1: Ext2+Bypass
|
FAlexRxExt1: array[0..10] of TFlatCheckBox; // EXT1: Ext2+Bypass
|
||||||
FAlexRxXvtr: array[0..10] of TCheckBox; // XVTR: XvtrDDC+Bypass
|
FAlexRxXvtr: array[0..10] of TFlatCheckBox; // XVTR: XvtrDDC+Bypass
|
||||||
// Per-band Do Not TX
|
// Per-band Do Not TX
|
||||||
FAlexDoNotTx: array[0..10] of TCheckBox;
|
FAlexDoNotTx: array[0..10] of TFlatCheckBox;
|
||||||
// Global TX routing flags
|
// Global TX routing flags
|
||||||
FAlexRxBypassOnTx: TCheckBox;
|
FAlexRxBypassOnTx: TFlatCheckBox;
|
||||||
FAlexExt1OnTx: TCheckBox;
|
FAlexExt1OnTx: TFlatCheckBox;
|
||||||
FAlexGndBpf2OnTx: TCheckBox;
|
FAlexGndBpf2OnTx: TFlatCheckBox;
|
||||||
FAlexEnableXvtrHf: TCheckBox;
|
FAlexEnableXvtrHf: TFlatCheckBox;
|
||||||
// Current Alex config state
|
// Current Alex config state
|
||||||
FAlexConfig: TAlexSettings;
|
FAlexConfig: TAlexSettings;
|
||||||
FAlexBoardType: Integer;
|
FAlexBoardType: Integer;
|
||||||
@@ -217,39 +219,39 @@ type
|
|||||||
// ---- XVTR (Transverter) tab controls ----
|
// ---- XVTR (Transverter) tab controls ----
|
||||||
FPageXvtr: TScrollBox;
|
FPageXvtr: TScrollBox;
|
||||||
FNavXvtr: TFlatButton;
|
FNavXvtr: TFlatButton;
|
||||||
FXvtrEn: array[0..CFG_XVTR_COUNT-1] of TCheckBox;
|
FXvtrEn: array[0..CFG_XVTR_COUNT-1] of TFlatCheckBox;
|
||||||
FXvtrName: array[0..CFG_XVTR_COUNT-1] of TEdit;
|
FXvtrName: array[0..CFG_XVTR_COUNT-1] of TFlatEdit;
|
||||||
FXvtrBegin: array[0..CFG_XVTR_COUNT-1] of TFloatSpinEdit; // MHz
|
FXvtrBegin: array[0..CFG_XVTR_COUNT-1] of TFlatFloatSpinEdit; // MHz
|
||||||
FXvtrEnd: array[0..CFG_XVTR_COUNT-1] of TFloatSpinEdit; // MHz
|
FXvtrEnd: array[0..CFG_XVTR_COUNT-1] of TFlatFloatSpinEdit; // MHz
|
||||||
FXvtrLOOff: array[0..CFG_XVTR_COUNT-1] of TFloatSpinEdit; // MHz
|
FXvtrLOOff: array[0..CFG_XVTR_COUNT-1] of TFlatFloatSpinEdit; // MHz
|
||||||
FXvtrLOErr: array[0..CFG_XVTR_COUNT-1] of TFloatSpinEdit; // Hz (целые)
|
FXvtrLOErr: array[0..CFG_XVTR_COUNT-1] of TFlatFloatSpinEdit; // Hz (целые)
|
||||||
FXvtrRXGain: array[0..CFG_XVTR_COUNT-1] of TFloatSpinEdit; // dB
|
FXvtrRXGain: array[0..CFG_XVTR_COUNT-1] of TFlatFloatSpinEdit; // dB
|
||||||
FXvtrRXOnly: array[0..CFG_XVTR_COUNT-1] of TCheckBox;
|
FXvtrRXOnly: array[0..CFG_XVTR_COUNT-1] of TFlatCheckBox;
|
||||||
FXvtrPower: array[0..CFG_XVTR_COUNT-1] of TSpinEdit; // 0..100
|
FXvtrPower: array[0..CFG_XVTR_COUNT-1] of TFlatSpinEdit; // 0..100
|
||||||
FXvtrDisPA: array[0..CFG_XVTR_COUNT-1] of TCheckBox;
|
FXvtrDisPA: array[0..CFG_XVTR_COUNT-1] of TFlatCheckBox;
|
||||||
FXvtrRXAnt: array[0..CFG_XVTR_COUNT-1] of TComboBox; // 0=def..3=ANT3
|
FXvtrRXAnt: array[0..CFG_XVTR_COUNT-1] of TFlatComboBox; // 0=def..3=ANT3
|
||||||
FChkXvtrTunPwr: TCheckBox;
|
FChkXvtrTunPwr: TFlatCheckBox;
|
||||||
FXvtrConfig: TXvtrSettings;
|
FXvtrConfig: TXvtrSettings;
|
||||||
FOnXvtrChange: TOnXvtrChange;
|
FOnXvtrChange: TOnXvtrChange;
|
||||||
|
|
||||||
// ---- CAT tab controls ----
|
// ---- CAT tab controls ----
|
||||||
FCATSerialEn: array[0..3] of TCheckBox;
|
FCATSerialEn: array[0..3] of TFlatCheckBox;
|
||||||
FCATSerialPort: array[0..3] of TEdit;
|
FCATSerialPort: array[0..3] of TFlatEdit;
|
||||||
FCATSerialBaud: array[0..3] of TComboBox;
|
FCATSerialBaud: array[0..3] of TFlatComboBox;
|
||||||
FCATSerialDataBits: array[0..3] of TComboBox;
|
FCATSerialDataBits: array[0..3] of TFlatComboBox;
|
||||||
FCATSerialStopBits: array[0..3] of TComboBox;
|
FCATSerialStopBits: array[0..3] of TFlatComboBox;
|
||||||
FCATSerialParity: array[0..3] of TComboBox;
|
FCATSerialParity: array[0..3] of TFlatComboBox;
|
||||||
FCATTcpEn: TCheckBox;
|
FCATTcpEn: TFlatCheckBox;
|
||||||
FCATTcpPort: TSpinEdit;
|
FCATTcpPort: TFlatSpinEdit;
|
||||||
|
|
||||||
// ---- Advanced tab controls ----
|
// ---- Advanced tab controls ----
|
||||||
FChkDither: TCheckBox;
|
FChkDither: TFlatCheckBox;
|
||||||
FChkRandom: TCheckBox;
|
FChkRandom: TFlatCheckBox;
|
||||||
FChkWebEnabled: TCheckBox;
|
FChkWebEnabled: TFlatCheckBox;
|
||||||
FEdWebPort: TSpinEdit;
|
FEdWebPort: TFlatSpinEdit;
|
||||||
FEdWebBind: TEdit;
|
FEdWebBind: TFlatEdit;
|
||||||
FEdWebUser: TEdit;
|
FEdWebUser: TFlatEdit;
|
||||||
FEdWebPass: TEdit;
|
FEdWebPass: TFlatEdit;
|
||||||
|
|
||||||
// ---- Close button ----
|
// ---- Close button ----
|
||||||
FBtnClose: TFlatButton;
|
FBtnClose: TFlatButton;
|
||||||
@@ -307,7 +309,7 @@ type
|
|||||||
function MakeLbl(AParent: TWinControl; const Cap: string;
|
function MakeLbl(AParent: TWinControl; const Cap: string;
|
||||||
ALeft, ATop, AW: Integer): TLabel;
|
ALeft, ATop, AW: Integer): TLabel;
|
||||||
function MakeCombo(AParent: TWinControl; ALeft, ATop, AW: Integer;
|
function MakeCombo(AParent: TWinControl; ALeft, ATop, AW: Integer;
|
||||||
OnChange: TNotifyEvent): TComboBox;
|
OnChange: TNotifyEvent): TFlatComboBox;
|
||||||
function MakeGroupPanel(AParent: TWinControl; const Cap: string;
|
function MakeGroupPanel(AParent: TWinControl; const Cap: string;
|
||||||
ALeft, ATop, AW, AH: Integer): TPanel;
|
ALeft, ATop, AW, AH: Integer): TPanel;
|
||||||
function MakeScrollPage: TScrollBox;
|
function MakeScrollPage: TScrollBox;
|
||||||
@@ -497,15 +499,14 @@ begin
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
function TSettingsForm.MakeCombo(AParent: TWinControl; ALeft, ATop, AW: Integer;
|
function TSettingsForm.MakeCombo(AParent: TWinControl; ALeft, ATop, AW: Integer;
|
||||||
OnChange: TNotifyEvent): TComboBox;
|
OnChange: TNotifyEvent): TFlatComboBox;
|
||||||
begin
|
begin
|
||||||
Result := TComboBox.Create(Self);
|
Result := TFlatComboBox.Create(Self);
|
||||||
Result.Parent := AParent;
|
Result.Parent := AParent;
|
||||||
Result.Left := ALeft;
|
Result.Left := ALeft;
|
||||||
Result.Top := ATop;
|
Result.Top := ATop;
|
||||||
Result.Width := AW;
|
Result.Width := AW;
|
||||||
Result.Height := BTN_H;
|
Result.Height := BTN_H;
|
||||||
Result.Style := csDropDownList;
|
|
||||||
Result.Color := CLR_INPUT;
|
Result.Color := CLR_INPUT;
|
||||||
Result.Font.Color := CLR_INPUT_TEXT;
|
Result.Font.Color := CLR_INPUT_TEXT;
|
||||||
Result.Font.Name := UI_FONT;
|
Result.Font.Name := UI_FONT;
|
||||||
@@ -801,7 +802,7 @@ begin
|
|||||||
|
|
||||||
Grp := MakeGroupPanel(FPageDisplay, 'Visible Panes', MARGIN, 86, 650, 86);
|
Grp := MakeGroupPanel(FPageDisplay, 'Visible Panes', MARGIN, 86, 650, 86);
|
||||||
|
|
||||||
FChkShowSpectrum := TCheckBox.Create(Self);
|
FChkShowSpectrum := TFlatCheckBox.Create(Self);
|
||||||
FChkShowSpectrum.Parent := Grp;
|
FChkShowSpectrum.Parent := Grp;
|
||||||
FChkShowSpectrum.Caption := 'Spectrum';
|
FChkShowSpectrum.Caption := 'Spectrum';
|
||||||
FChkShowSpectrum.SetBounds(PAD, R1, CHKW, 22);
|
FChkShowSpectrum.SetBounds(PAD, R1, CHKW, 22);
|
||||||
@@ -811,7 +812,7 @@ begin
|
|||||||
FChkShowSpectrum.Font.Size := 9;
|
FChkShowSpectrum.Font.Size := 9;
|
||||||
FChkShowSpectrum.OnChange := OnVisibilityChkChange;
|
FChkShowSpectrum.OnChange := OnVisibilityChkChange;
|
||||||
|
|
||||||
FChkShowWaterfall := TCheckBox.Create(Self);
|
FChkShowWaterfall := TFlatCheckBox.Create(Self);
|
||||||
FChkShowWaterfall.Parent := Grp;
|
FChkShowWaterfall.Parent := Grp;
|
||||||
FChkShowWaterfall.Caption := 'Waterfall';
|
FChkShowWaterfall.Caption := 'Waterfall';
|
||||||
FChkShowWaterfall.SetBounds(PAD + CHKW + 20, R1, CHKW, 22);
|
FChkShowWaterfall.SetBounds(PAD + CHKW + 20, R1, CHKW, 22);
|
||||||
@@ -831,7 +832,7 @@ begin
|
|||||||
|
|
||||||
Grp := MakeGroupPanel(FPageDisplay, 'Appearance', MARGIN, 292, 650, 68);
|
Grp := MakeGroupPanel(FPageDisplay, 'Appearance', MARGIN, 292, 650, 68);
|
||||||
|
|
||||||
FChkLightTheme := TCheckBox.Create(Self);
|
FChkLightTheme := TFlatCheckBox.Create(Self);
|
||||||
FChkLightTheme.Parent := Grp;
|
FChkLightTheme.Parent := Grp;
|
||||||
FChkLightTheme.Caption := 'Light theme';
|
FChkLightTheme.Caption := 'Light theme';
|
||||||
FChkLightTheme.SetBounds(PAD, R1, CHKW, 22);
|
FChkLightTheme.SetBounds(PAD, R1, CHKW, 22);
|
||||||
@@ -873,9 +874,9 @@ var
|
|||||||
i: Integer;
|
i: Integer;
|
||||||
|
|
||||||
function MkFloat(AX, AY, AW: Integer; AValue, AMin, AMax, AInc: Double;
|
function MkFloat(AX, AY, AW: Integer; AValue, AMin, AMax, AInc: Double;
|
||||||
AOnChange: TNotifyEvent): TFloatSpinEdit;
|
AOnChange: TNotifyEvent): TFlatFloatSpinEdit;
|
||||||
begin
|
begin
|
||||||
Result := TFloatSpinEdit.Create(Self);
|
Result := TFlatFloatSpinEdit.Create(Self);
|
||||||
Result.Parent := Grp;
|
Result.Parent := Grp;
|
||||||
Result.SetBounds(AX, AY, AW, BTN_H);
|
Result.SetBounds(AX, AY, AW, BTN_H);
|
||||||
Result.Value := AValue; Result.MinValue := AMin;
|
Result.Value := AValue; Result.MinValue := AMin;
|
||||||
@@ -887,9 +888,9 @@ var
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
function MkEdit(AX, AY, AW: Integer; const AText: string;
|
function MkEdit(AX, AY, AW: Integer; const AText: string;
|
||||||
AOnExit: TNotifyEvent): TEdit;
|
AOnExit: TNotifyEvent): TFlatEdit;
|
||||||
begin
|
begin
|
||||||
Result := TEdit.Create(Self);
|
Result := TFlatEdit.Create(Self);
|
||||||
Result.Parent := Grp;
|
Result.Parent := Grp;
|
||||||
Result.SetBounds(AX, AY, AW, BTN_H);
|
Result.SetBounds(AX, AY, AW, BTN_H);
|
||||||
Result.Color := CLR_INPUT; Result.Font.Color := CLR_INPUT_TEXT;
|
Result.Color := CLR_INPUT; Result.Font.Color := CLR_INPUT_TEXT;
|
||||||
@@ -987,7 +988,7 @@ begin
|
|||||||
FWfAGCOffset := MkFloat(CX, R1 + 2 * STEP, SPW, 0.0, -40.0, 40.0, 0.5, OnWfThresholdChange);
|
FWfAGCOffset := MkFloat(CX, R1 + 2 * STEP, SPW, 0.0, -40.0, 40.0, 0.5, OnWfThresholdChange);
|
||||||
|
|
||||||
Grp := MakeGroupPanel(FPageWaterfall, 'AGC && Noise Floor', MARGIN, 458, 650, 140);
|
Grp := MakeGroupPanel(FPageWaterfall, 'AGC && Noise Floor', MARGIN, 458, 650, 140);
|
||||||
FChkWfAGC := TCheckBox.Create(Self);
|
FChkWfAGC := TFlatCheckBox.Create(Self);
|
||||||
FChkWfAGC.Parent := Grp;
|
FChkWfAGC.Parent := Grp;
|
||||||
FChkWfAGC.Caption := 'Waterfall AGC';
|
FChkWfAGC.Caption := 'Waterfall AGC';
|
||||||
FChkWfAGC.SetBounds(PAD, R1, 200, 22);
|
FChkWfAGC.SetBounds(PAD, R1, 200, 22);
|
||||||
@@ -996,7 +997,7 @@ begin
|
|||||||
FChkWfAGC.Font.Name := UI_FONT;
|
FChkWfAGC.Font.Name := UI_FONT;
|
||||||
FChkWfAGC.Font.Size := 9;
|
FChkWfAGC.Font.Size := 9;
|
||||||
FChkWfAGC.OnChange := OnWfAGCNFChkChange;
|
FChkWfAGC.OnChange := OnWfAGCNFChkChange;
|
||||||
FChkWfNF := TCheckBox.Create(Self);
|
FChkWfNF := TFlatCheckBox.Create(Self);
|
||||||
FChkWfNF.Parent := Grp;
|
FChkWfNF.Parent := Grp;
|
||||||
FChkWfNF.Caption := 'Waterfall NF';
|
FChkWfNF.Caption := 'Waterfall NF';
|
||||||
FChkWfNF.SetBounds(PAD, R1 + STEP, 200, 22);
|
FChkWfNF.SetBounds(PAD, R1 + STEP, 200, 22);
|
||||||
@@ -1026,9 +1027,9 @@ var
|
|||||||
Grp: TPanel;
|
Grp: TPanel;
|
||||||
Y, i: Integer;
|
Y, i: Integer;
|
||||||
|
|
||||||
function MkSpin(AParent: TWinControl; AX, AY: Integer; AMin, AMax, AVal: Integer): TSpinEdit;
|
function MkSpin(AParent: TWinControl; AX, AY: Integer; AMin, AMax, AVal: Integer): TFlatSpinEdit;
|
||||||
begin
|
begin
|
||||||
Result := TSpinEdit.Create(Self);
|
Result := TFlatSpinEdit.Create(Self);
|
||||||
Result.Parent := AParent;
|
Result.Parent := AParent;
|
||||||
Result.SetBounds(AX, AY, SPW, BTN_H);
|
Result.SetBounds(AX, AY, SPW, BTN_H);
|
||||||
Result.MinValue := AMin;
|
Result.MinValue := AMin;
|
||||||
@@ -1042,9 +1043,9 @@ var
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
function MkFloat(AParent: TWinControl; AX, AY: Integer;
|
function MkFloat(AParent: TWinControl; AX, AY: Integer;
|
||||||
AVal, AMin, AMax, AInc: Double): TFloatSpinEdit;
|
AVal, AMin, AMax, AInc: Double): TFlatFloatSpinEdit;
|
||||||
begin
|
begin
|
||||||
Result := TFloatSpinEdit.Create(Self);
|
Result := TFlatFloatSpinEdit.Create(Self);
|
||||||
Result.Parent := AParent;
|
Result.Parent := AParent;
|
||||||
Result.SetBounds(AX, AY, SPW, BTN_H);
|
Result.SetBounds(AX, AY, SPW, BTN_H);
|
||||||
Result.MinValue := AMin;
|
Result.MinValue := AMin;
|
||||||
@@ -1059,9 +1060,9 @@ var
|
|||||||
Result.OnChange := OnTXAnyChange;
|
Result.OnChange := OnTXAnyChange;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function MkChk(AParent: TWinControl; AX, AY, AW: Integer; const ACap: string): TCheckBox;
|
function MkChk(AParent: TWinControl; AX, AY, AW: Integer; const ACap: string): TFlatCheckBox;
|
||||||
begin
|
begin
|
||||||
Result := TCheckBox.Create(Self);
|
Result := TFlatCheckBox.Create(Self);
|
||||||
Result.Parent := AParent;
|
Result.Parent := AParent;
|
||||||
Result.Caption := ACap;
|
Result.Caption := ACap;
|
||||||
Result.SetBounds(AX, AY, AW, 22);
|
Result.SetBounds(AX, AY, AW, 22);
|
||||||
@@ -1071,7 +1072,7 @@ var
|
|||||||
Result.OnChange := OnTXAnyChange;
|
Result.OnChange := OnTXAnyChange;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function MkCmb(AParent: TWinControl; AX, AY, AW: Integer): TComboBox;
|
function MkCmb(AParent: TWinControl; AX, AY, AW: Integer): TFlatComboBox;
|
||||||
begin
|
begin
|
||||||
Result := MakeCombo(AParent, AX, AY, AW, OnTXAnyChange);
|
Result := MakeCombo(AParent, AX, AY, AW, OnTXAnyChange);
|
||||||
end;
|
end;
|
||||||
@@ -1272,7 +1273,7 @@ begin
|
|||||||
Grp := MakeGroupPanel(FPageTransmit, 'TX Grid', MARGIN, Y, GRP_W, 170);
|
Grp := MakeGroupPanel(FPageTransmit, 'TX Grid', MARGIN, Y, GRP_W, 170);
|
||||||
|
|
||||||
MakeLbl(Grp, 'Reference level', PAD, R1 + 5, LW);
|
MakeLbl(Grp, 'Reference level', PAD, R1 + 5, LW);
|
||||||
FEdTXRefLevel := TEdit.Create(Self);
|
FEdTXRefLevel := TFlatEdit.Create(Self);
|
||||||
FEdTXRefLevel.Parent := Grp;
|
FEdTXRefLevel.Parent := Grp;
|
||||||
FEdTXRefLevel.SetBounds(CX, R1, 96, BTN_H);
|
FEdTXRefLevel.SetBounds(CX, R1, 96, BTN_H);
|
||||||
FEdTXRefLevel.Color := CLR_INPUT;
|
FEdTXRefLevel.Color := CLR_INPUT;
|
||||||
@@ -1283,7 +1284,7 @@ begin
|
|||||||
FEdTXRefLevel.OnExit := OnTXAnyChange;
|
FEdTXRefLevel.OnExit := OnTXAnyChange;
|
||||||
|
|
||||||
MakeLbl(Grp, 'Range (dB)', PAD, R1 + STEP + 5, LW);
|
MakeLbl(Grp, 'Range (dB)', PAD, R1 + STEP + 5, LW);
|
||||||
FEdTXRange := TEdit.Create(Self);
|
FEdTXRange := TFlatEdit.Create(Self);
|
||||||
FEdTXRange.Parent := Grp;
|
FEdTXRange.Parent := Grp;
|
||||||
FEdTXRange.SetBounds(CX, R1 + STEP, 96, BTN_H);
|
FEdTXRange.SetBounds(CX, R1 + STEP, 96, BTN_H);
|
||||||
FEdTXRange.Color := CLR_INPUT;
|
FEdTXRange.Color := CLR_INPUT;
|
||||||
@@ -1537,10 +1538,10 @@ const
|
|||||||
GRP_H = 222;
|
GRP_H = 222;
|
||||||
|
|
||||||
function MakeFloatSpin(AParent: TWinControl; ALeft, ATop: Integer;
|
function MakeFloatSpin(AParent: TWinControl; ALeft, ATop: Integer;
|
||||||
AChange: TNotifyEvent): TFloatSpinEdit;
|
AChange: TNotifyEvent): TFlatFloatSpinEdit;
|
||||||
var E: TFloatSpinEdit;
|
var E: TFlatFloatSpinEdit;
|
||||||
begin
|
begin
|
||||||
E := TFloatSpinEdit.Create(Self);
|
E := TFlatFloatSpinEdit.Create(Self);
|
||||||
E.Parent := AParent;
|
E.Parent := AParent;
|
||||||
E.SetBounds(ALeft, ATop, ED_W, BTN_H + 2);
|
E.SetBounds(ALeft, ATop, ED_W, BTN_H + 2);
|
||||||
E.Color := CLR_INPUT;
|
E.Color := CLR_INPUT;
|
||||||
@@ -1559,7 +1560,7 @@ const
|
|||||||
var
|
var
|
||||||
Grp: TPanel;
|
Grp: TPanel;
|
||||||
Lbl: TLabel;
|
Lbl: TLabel;
|
||||||
Spin: TSpinEdit;
|
Spin: TFlatSpinEdit;
|
||||||
i, r, c, X, Y: Integer;
|
i, r, c, X, Y: Integer;
|
||||||
begin
|
begin
|
||||||
with TLabel.Create(Self) do
|
with TLabel.Create(Self) do
|
||||||
@@ -1594,7 +1595,7 @@ begin
|
|||||||
Lbl.Font.Name := UI_FONT;
|
Lbl.Font.Name := UI_FONT;
|
||||||
Lbl.Font.Size := 9;
|
Lbl.Font.Size := 9;
|
||||||
|
|
||||||
Spin := TSpinEdit.Create(Self);
|
Spin := TFlatSpinEdit.Create(Self);
|
||||||
Spin.Parent := Grp;
|
Spin.Parent := Grp;
|
||||||
Spin.SetBounds(GRP_PAD + 150, R1, ED_W, BTN_H + 2);
|
Spin.SetBounds(GRP_PAD + 150, R1, ED_W, BTN_H + 2);
|
||||||
Spin.Color := CLR_INPUT;
|
Spin.Color := CLR_INPUT;
|
||||||
@@ -1683,9 +1684,9 @@ const
|
|||||||
R1 = 42;
|
R1 = 42;
|
||||||
var
|
var
|
||||||
Grp: TPanel;
|
Grp: TPanel;
|
||||||
Chk: TCheckBox;
|
Chk: TFlatCheckBox;
|
||||||
Ed: TEdit;
|
Ed: TFlatEdit;
|
||||||
Spin: TSpinEdit;
|
Spin: TFlatSpinEdit;
|
||||||
Y: Integer;
|
Y: Integer;
|
||||||
begin
|
begin
|
||||||
with TLabel.Create(Self) do
|
with TLabel.Create(Self) do
|
||||||
@@ -1702,7 +1703,7 @@ begin
|
|||||||
// ── ADC ───────────────────────────────────────────────────────────────────
|
// ── ADC ───────────────────────────────────────────────────────────────────
|
||||||
Grp := MakeGroupPanel(FPageAdvanced, 'ADC', MARGIN, 86, 420, 128);
|
Grp := MakeGroupPanel(FPageAdvanced, 'ADC', MARGIN, 86, 420, 128);
|
||||||
|
|
||||||
Chk := TCheckBox.Create(Self);
|
Chk := TFlatCheckBox.Create(Self);
|
||||||
Chk.Parent := Grp;
|
Chk.Parent := Grp;
|
||||||
Chk.Caption := 'Dither Enabled';
|
Chk.Caption := 'Dither Enabled';
|
||||||
Chk.SetBounds(GRP_PAD, R1, 260, 22);
|
Chk.SetBounds(GRP_PAD, R1, 260, 22);
|
||||||
@@ -1713,7 +1714,7 @@ begin
|
|||||||
Chk.OnChange := OnADCChkChange;
|
Chk.OnChange := OnADCChkChange;
|
||||||
FChkDither := Chk;
|
FChkDither := Chk;
|
||||||
|
|
||||||
Chk := TCheckBox.Create(Self);
|
Chk := TFlatCheckBox.Create(Self);
|
||||||
Chk.Parent := Grp;
|
Chk.Parent := Grp;
|
||||||
Chk.Caption := 'Random Enabled';
|
Chk.Caption := 'Random Enabled';
|
||||||
Chk.SetBounds(GRP_PAD, R1 + ROW_H, 260, 22);
|
Chk.SetBounds(GRP_PAD, R1 + ROW_H, 260, 22);
|
||||||
@@ -1728,7 +1729,7 @@ begin
|
|||||||
Grp := MakeGroupPanel(FPageAdvanced, 'Web Server', MARGIN, 230, 500, 252);
|
Grp := MakeGroupPanel(FPageAdvanced, 'Web Server', MARGIN, 230, 500, 252);
|
||||||
|
|
||||||
Y := R1;
|
Y := R1;
|
||||||
Chk := TCheckBox.Create(Self);
|
Chk := TFlatCheckBox.Create(Self);
|
||||||
Chk.Parent := Grp;
|
Chk.Parent := Grp;
|
||||||
Chk.Caption := 'Enabled';
|
Chk.Caption := 'Enabled';
|
||||||
Chk.SetBounds(GRP_PAD, Y, 260, 22);
|
Chk.SetBounds(GRP_PAD, Y, 260, 22);
|
||||||
@@ -1741,7 +1742,7 @@ begin
|
|||||||
|
|
||||||
Y := Y + ROW_H;
|
Y := Y + ROW_H;
|
||||||
MakeLbl(Grp, 'Port:', GRP_PAD, Y + 4, LBL_W);
|
MakeLbl(Grp, 'Port:', GRP_PAD, Y + 4, LBL_W);
|
||||||
Spin := TSpinEdit.Create(Self);
|
Spin := TFlatSpinEdit.Create(Self);
|
||||||
Spin.Parent := Grp;
|
Spin.Parent := Grp;
|
||||||
Spin.SetBounds(GRP_PAD + LBL_W + 12, Y, 90, BTN_H);
|
Spin.SetBounds(GRP_PAD + LBL_W + 12, Y, 90, BTN_H);
|
||||||
Spin.Color := CLR_INPUT;
|
Spin.Color := CLR_INPUT;
|
||||||
@@ -1756,7 +1757,7 @@ begin
|
|||||||
|
|
||||||
Y := Y + ROW_H;
|
Y := Y + ROW_H;
|
||||||
MakeLbl(Grp, 'Interface:', GRP_PAD, Y + 4, LBL_W);
|
MakeLbl(Grp, 'Interface:', GRP_PAD, Y + 4, LBL_W);
|
||||||
Ed := TEdit.Create(Self);
|
Ed := TFlatEdit.Create(Self);
|
||||||
Ed.Parent := Grp;
|
Ed.Parent := Grp;
|
||||||
Ed.SetBounds(GRP_PAD + LBL_W + 12, Y, ED_W, BTN_H);
|
Ed.SetBounds(GRP_PAD + LBL_W + 12, Y, ED_W, BTN_H);
|
||||||
Ed.Color := CLR_INPUT;
|
Ed.Color := CLR_INPUT;
|
||||||
@@ -1769,7 +1770,7 @@ begin
|
|||||||
|
|
||||||
Y := Y + ROW_H;
|
Y := Y + ROW_H;
|
||||||
MakeLbl(Grp, 'Username:', GRP_PAD, Y + 4, LBL_W);
|
MakeLbl(Grp, 'Username:', GRP_PAD, Y + 4, LBL_W);
|
||||||
Ed := TEdit.Create(Self);
|
Ed := TFlatEdit.Create(Self);
|
||||||
Ed.Parent := Grp;
|
Ed.Parent := Grp;
|
||||||
Ed.SetBounds(GRP_PAD + LBL_W + 12, Y, ED_W, BTN_H);
|
Ed.SetBounds(GRP_PAD + LBL_W + 12, Y, ED_W, BTN_H);
|
||||||
Ed.Color := CLR_INPUT;
|
Ed.Color := CLR_INPUT;
|
||||||
@@ -1782,7 +1783,7 @@ begin
|
|||||||
|
|
||||||
Y := Y + ROW_H;
|
Y := Y + ROW_H;
|
||||||
MakeLbl(Grp, 'Password:', GRP_PAD, Y + 4, LBL_W);
|
MakeLbl(Grp, 'Password:', GRP_PAD, Y + 4, LBL_W);
|
||||||
Ed := TEdit.Create(Self);
|
Ed := TFlatEdit.Create(Self);
|
||||||
Ed.Parent := Grp;
|
Ed.Parent := Grp;
|
||||||
Ed.SetBounds(GRP_PAD + LBL_W + 12, Y, ED_W, BTN_H);
|
Ed.SetBounds(GRP_PAD + LBL_W + 12, Y, ED_W, BTN_H);
|
||||||
Ed.Color := CLR_INPUT;
|
Ed.Color := CLR_INPUT;
|
||||||
@@ -1885,8 +1886,18 @@ procedure TSettingsForm.ApplyTheme(const T: TAppTheme);
|
|||||||
else
|
else
|
||||||
TLabel(Ctrl).Font.Color := T.TextDim;
|
TLabel(Ctrl).Font.Color := T.TextDim;
|
||||||
end
|
end
|
||||||
else if Ctrl is TCheckBox then
|
else if Ctrl is TFlatCheckBox then
|
||||||
TCheckBox(Ctrl).Font.Color := T.Text
|
TFlatCheckBox(Ctrl).SetAppTheme(T)
|
||||||
|
else if Ctrl is TFlatComboBox then
|
||||||
|
TFlatComboBox(Ctrl).SetAppTheme(T)
|
||||||
|
else if Ctrl is TFlatEdit then
|
||||||
|
TFlatEdit(Ctrl).SetAppTheme(T)
|
||||||
|
else if Ctrl is TFlatSpinEdit then
|
||||||
|
TFlatSpinEdit(Ctrl).SetAppTheme(T)
|
||||||
|
else if Ctrl is TFlatFloatSpinEdit then
|
||||||
|
TFlatFloatSpinEdit(Ctrl).SetAppTheme(T)
|
||||||
|
else if Ctrl is TFlatRadioButton then
|
||||||
|
TFlatRadioButton(Ctrl).SetAppTheme(T)
|
||||||
else if Ctrl is TListBox then
|
else if Ctrl is TListBox then
|
||||||
begin
|
begin
|
||||||
TListBox(Ctrl).Color := T.BG;
|
TListBox(Ctrl).Color := T.BG;
|
||||||
@@ -2472,10 +2483,10 @@ const
|
|||||||
var
|
var
|
||||||
i, col, row, gx, gy, j: Integer;
|
i, col, row, gx, gy, j: Integer;
|
||||||
Grp: TPanel;
|
Grp: TPanel;
|
||||||
Chk: TCheckBox;
|
Chk: TFlatCheckBox;
|
||||||
Ed: TEdit;
|
Ed: TFlatEdit;
|
||||||
Cmb: TComboBox;
|
Cmb: TFlatComboBox;
|
||||||
Spin: TSpinEdit;
|
Spin: TFlatSpinEdit;
|
||||||
begin
|
begin
|
||||||
with TLabel.Create(Self) do
|
with TLabel.Create(Self) do
|
||||||
begin
|
begin
|
||||||
@@ -2502,7 +2513,7 @@ begin
|
|||||||
|
|
||||||
Grp := MakeGroupPanel(FPageCAT, 'Serial Port ' + IntToStr(i + 1), gx, gy, GRP_W, GRP_H);
|
Grp := MakeGroupPanel(FPageCAT, 'Serial Port ' + IntToStr(i + 1), gx, gy, GRP_W, GRP_H);
|
||||||
|
|
||||||
Chk := TCheckBox.Create(Self);
|
Chk := TFlatCheckBox.Create(Self);
|
||||||
Chk.Parent := Grp;
|
Chk.Parent := Grp;
|
||||||
Chk.Caption := 'Enable';
|
Chk.Caption := 'Enable';
|
||||||
Chk.SetBounds(PAD, R1, GRP_W - PAD * 2, 22);
|
Chk.SetBounds(PAD, R1, GRP_W - PAD * 2, 22);
|
||||||
@@ -2513,7 +2524,7 @@ begin
|
|||||||
FCATSerialEn[i] := Chk;
|
FCATSerialEn[i] := Chk;
|
||||||
|
|
||||||
MakeLbl(Grp, 'Port:', PAD, R1 + STEP + 6, LW);
|
MakeLbl(Grp, 'Port:', PAD, R1 + STEP + 6, LW);
|
||||||
Ed := TEdit.Create(Self);
|
Ed := TFlatEdit.Create(Self);
|
||||||
Ed.Parent := Grp;
|
Ed.Parent := Grp;
|
||||||
Ed.SetBounds(CX, R1 + STEP, EDT_W, BTN_H);
|
Ed.SetBounds(CX, R1 + STEP, EDT_W, BTN_H);
|
||||||
Ed.Color := CLR_INPUT; Ed.Font.Color := CLR_INPUT_TEXT;
|
Ed.Color := CLR_INPUT; Ed.Font.Color := CLR_INPUT_TEXT;
|
||||||
@@ -2560,7 +2571,7 @@ begin
|
|||||||
gy := 86 + 2 * (GRP_H + GRP_GAP);
|
gy := 86 + 2 * (GRP_H + GRP_GAP);
|
||||||
Grp := MakeGroupPanel(FPageCAT, 'TCP CAT Server', MARGIN, gy, GRP_W * 2 + GRP_GAP, 116);
|
Grp := MakeGroupPanel(FPageCAT, 'TCP CAT Server', MARGIN, gy, GRP_W * 2 + GRP_GAP, 116);
|
||||||
|
|
||||||
FCATTcpEn := TCheckBox.Create(Self);
|
FCATTcpEn := TFlatCheckBox.Create(Self);
|
||||||
FCATTcpEn.Parent := Grp;
|
FCATTcpEn.Parent := Grp;
|
||||||
FCATTcpEn.Caption := 'Enable TCP server (default port 19090)';
|
FCATTcpEn.Caption := 'Enable TCP server (default port 19090)';
|
||||||
FCATTcpEn.SetBounds(PAD, R1, 300, 22);
|
FCATTcpEn.SetBounds(PAD, R1, 300, 22);
|
||||||
@@ -2569,7 +2580,7 @@ begin
|
|||||||
FCATTcpEn.OnChange := OnCATTcpChange;
|
FCATTcpEn.OnChange := OnCATTcpChange;
|
||||||
|
|
||||||
MakeLbl(Grp, 'Port:', PAD, R1 + STEP + 6, LW);
|
MakeLbl(Grp, 'Port:', PAD, R1 + STEP + 6, LW);
|
||||||
Spin := TSpinEdit.Create(Self);
|
Spin := TFlatSpinEdit.Create(Self);
|
||||||
Spin.Parent := Grp;
|
Spin.Parent := Grp;
|
||||||
Spin.SetBounds(CX, R1 + STEP, 100, BTN_H + 2);
|
Spin.SetBounds(CX, R1 + STEP, 100, BTN_H + 2);
|
||||||
Spin.Color := CLR_INPUT; Spin.Font.Color := CLR_INPUT_TEXT;
|
Spin.Color := CLR_INPUT; Spin.Font.Color := CLR_INPUT_TEXT;
|
||||||
@@ -2696,7 +2707,7 @@ var
|
|||||||
Grp, GrpTX: TPanel;
|
Grp, GrpTX: TPanel;
|
||||||
b: Integer;
|
b: Integer;
|
||||||
Y: Integer;
|
Y: Integer;
|
||||||
Chk: TCheckBox;
|
Chk: TFlatCheckBox;
|
||||||
RxGrp, TxGrp: TPanel;
|
RxGrp, TxGrp: TPanel;
|
||||||
|
|
||||||
// In Lazarus, TRadioButton has no GroupIndex; buttons are grouped by parent.
|
// In Lazarus, TRadioButton has no GroupIndex; buttons are grouped by parent.
|
||||||
@@ -2712,9 +2723,9 @@ var
|
|||||||
Result.Caption := '';
|
Result.Caption := '';
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function MkRb(AParent: TWinControl; AX: Integer): TRadioButton;
|
function MkRb(AParent: TWinControl; AX: Integer): TFlatRadioButton;
|
||||||
begin
|
begin
|
||||||
Result := TRadioButton.Create(Self);
|
Result := TFlatRadioButton.Create(Self);
|
||||||
Result.Parent := AParent;
|
Result.Parent := AParent;
|
||||||
Result.Caption := '';
|
Result.Caption := '';
|
||||||
Result.SetBounds(AX, 0, 22, 20);
|
Result.SetBounds(AX, 0, 22, 20);
|
||||||
@@ -2725,9 +2736,9 @@ var
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
function MkCh(AParent: TWinControl; const ACap: string;
|
function MkCh(AParent: TWinControl; const ACap: string;
|
||||||
AX, AY, AW: Integer): TCheckBox;
|
AX, AY, AW: Integer): TFlatCheckBox;
|
||||||
begin
|
begin
|
||||||
Result := TCheckBox.Create(Self);
|
Result := TFlatCheckBox.Create(Self);
|
||||||
Result.Parent := AParent;
|
Result.Parent := AParent;
|
||||||
Result.Caption := ACap;
|
Result.Caption := ACap;
|
||||||
Result.SetBounds(AX, AY, AW, 20);
|
Result.SetBounds(AX, AY, AW, 20);
|
||||||
@@ -2893,11 +2904,11 @@ var
|
|||||||
Tag, b: Integer;
|
Tag, b: Integer;
|
||||||
begin
|
begin
|
||||||
if FLoading then Exit;
|
if FLoading then Exit;
|
||||||
Tag := TCheckBox(Sender).Tag;
|
Tag := TFlatCheckBox(Sender).Tag;
|
||||||
if Tag >= 200 then
|
if Tag >= 200 then
|
||||||
begin
|
begin
|
||||||
b := Tag - 200; // XVTR
|
b := Tag - 200; // XVTR
|
||||||
if TCheckBox(Sender).Checked then
|
if TFlatCheckBox(Sender).Checked then
|
||||||
begin
|
begin
|
||||||
FLoading := True;
|
FLoading := True;
|
||||||
FAlexRxByps[b].Checked := False;
|
FAlexRxByps[b].Checked := False;
|
||||||
@@ -2908,7 +2919,7 @@ begin
|
|||||||
else if Tag >= 100 then
|
else if Tag >= 100 then
|
||||||
begin
|
begin
|
||||||
b := Tag - 100; // EXT1
|
b := Tag - 100; // EXT1
|
||||||
if TCheckBox(Sender).Checked then
|
if TFlatCheckBox(Sender).Checked then
|
||||||
begin
|
begin
|
||||||
FLoading := True;
|
FLoading := True;
|
||||||
FAlexRxByps[b].Checked := False;
|
FAlexRxByps[b].Checked := False;
|
||||||
@@ -2919,7 +2930,7 @@ begin
|
|||||||
else
|
else
|
||||||
begin
|
begin
|
||||||
b := Tag; // BYPS
|
b := Tag; // BYPS
|
||||||
if TCheckBox(Sender).Checked then
|
if TFlatCheckBox(Sender).Checked then
|
||||||
begin
|
begin
|
||||||
FLoading := True;
|
FLoading := True;
|
||||||
FAlexRxExt1[b].Checked := False;
|
FAlexRxExt1[b].Checked := False;
|
||||||
@@ -2988,9 +2999,9 @@ var
|
|||||||
L: TLabel;
|
L: TLabel;
|
||||||
|
|
||||||
function MakeFlt(AParent: TWinControl; AX, AY, AW: Integer;
|
function MakeFlt(AParent: TWinControl; AX, AY, AW: Integer;
|
||||||
AMin, AMax, AStep: Double; ADec: Integer): TFloatSpinEdit;
|
AMin, AMax, AStep: Double; ADec: Integer): TFlatFloatSpinEdit;
|
||||||
begin
|
begin
|
||||||
Result := TFloatSpinEdit.Create(Self);
|
Result := TFlatFloatSpinEdit.Create(Self);
|
||||||
Result.Parent := AParent;
|
Result.Parent := AParent;
|
||||||
Result.SetBounds(AX, AY, AW, BTN_H);
|
Result.SetBounds(AX, AY, AW, BTN_H);
|
||||||
Result.MinValue := AMin;
|
Result.MinValue := AMin;
|
||||||
@@ -3005,9 +3016,9 @@ var
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
function MakeInt(AParent: TWinControl; AX, AY, AW: Integer;
|
function MakeInt(AParent: TWinControl; AX, AY, AW: Integer;
|
||||||
AMin, AMax: Integer): TSpinEdit;
|
AMin, AMax: Integer): TFlatSpinEdit;
|
||||||
begin
|
begin
|
||||||
Result := TSpinEdit.Create(Self);
|
Result := TFlatSpinEdit.Create(Self);
|
||||||
Result.Parent := AParent;
|
Result.Parent := AParent;
|
||||||
Result.SetBounds(AX, AY, AW, BTN_H);
|
Result.SetBounds(AX, AY, AW, BTN_H);
|
||||||
Result.MinValue := AMin;
|
Result.MinValue := AMin;
|
||||||
@@ -3020,9 +3031,9 @@ var
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
// Чекбокс без подписи (только квадрат). Подпись пишем отдельно над колонкой.
|
// Чекбокс без подписи (только квадрат). Подпись пишем отдельно над колонкой.
|
||||||
function MakeChk(AParent: TWinControl; AX, AY: Integer): TCheckBox;
|
function MakeChk(AParent: TWinControl; AX, AY: Integer): TFlatCheckBox;
|
||||||
begin
|
begin
|
||||||
Result := TCheckBox.Create(Self);
|
Result := TFlatCheckBox.Create(Self);
|
||||||
Result.Parent := AParent;
|
Result.Parent := AParent;
|
||||||
Result.Caption := '';
|
Result.Caption := '';
|
||||||
Result.SetBounds(AX, AY, 22, 20);
|
Result.SetBounds(AX, AY, 22, 20);
|
||||||
@@ -3032,9 +3043,9 @@ var
|
|||||||
Result.OnChange := OnXvtrAnyChange;
|
Result.OnChange := OnXvtrAnyChange;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function MakeEdt(AParent: TWinControl; AX, AY, AW: Integer): TEdit;
|
function MakeEdt(AParent: TWinControl; AX, AY, AW: Integer): TFlatEdit;
|
||||||
begin
|
begin
|
||||||
Result := TEdit.Create(Self);
|
Result := TFlatEdit.Create(Self);
|
||||||
Result.Parent := AParent;
|
Result.Parent := AParent;
|
||||||
Result.SetBounds(AX, AY, AW, BTN_H);
|
Result.SetBounds(AX, AY, AW, BTN_H);
|
||||||
Result.Color := CLR_INPUT;
|
Result.Color := CLR_INPUT;
|
||||||
@@ -3045,12 +3056,11 @@ var
|
|||||||
Result.OnChange := OnXvtrAnyChange;
|
Result.OnChange := OnXvtrAnyChange;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function MakeCmb(AParent: TWinControl; AX, AY, AW: Integer): TComboBox;
|
function MakeCmb(AParent: TWinControl; AX, AY, AW: Integer): TFlatComboBox;
|
||||||
begin
|
begin
|
||||||
Result := TComboBox.Create(Self);
|
Result := TFlatComboBox.Create(Self);
|
||||||
Result.Parent := AParent;
|
Result.Parent := AParent;
|
||||||
Result.SetBounds(AX, AY, AW, BTN_H);
|
Result.SetBounds(AX, AY, AW, BTN_H);
|
||||||
Result.Style := csDropDownList;
|
|
||||||
Result.Color := CLR_INPUT;
|
Result.Color := CLR_INPUT;
|
||||||
Result.Font.Color := CLR_INPUT_TEXT;
|
Result.Font.Color := CLR_INPUT_TEXT;
|
||||||
Result.Font.Name := UI_FONT;
|
Result.Font.Name := UI_FONT;
|
||||||
@@ -3144,7 +3154,7 @@ begin
|
|||||||
GrpY := GrpY + GRP_TITLE_H + HDR_H + CFG_XVTR_COUNT * ROW_H + 14 + 18;
|
GrpY := GrpY + GRP_TITLE_H + HDR_H + CFG_XVTR_COUNT * ROW_H + 14 + 18;
|
||||||
GrpOpts := MakeGroupPanel(FPageXvtr, 'Options',
|
GrpOpts := MakeGroupPanel(FPageXvtr, 'Options',
|
||||||
MARGIN, GrpY, GRP_W, GRP_TITLE_H + 36);
|
MARGIN, GrpY, GRP_W, GRP_TITLE_H + 36);
|
||||||
FChkXvtrTunPwr := TCheckBox.Create(Self);
|
FChkXvtrTunPwr := TFlatCheckBox.Create(Self);
|
||||||
FChkXvtrTunPwr.Parent := GrpOpts;
|
FChkXvtrTunPwr.Parent := GrpOpts;
|
||||||
FChkXvtrTunPwr.Caption := 'Use XVTR power for TUN '
|
FChkXvtrTunPwr.Caption := 'Use XVTR power for TUN '
|
||||||
+ '(TUN button uses the per-slot TX Power instead of the global TUN Level)';
|
+ '(TUN button uses the per-slot TX Power instead of the global TUN Level)';
|
||||||
|
|||||||
@@ -131,6 +131,34 @@
|
|||||||
<Filename Value="FlatSlider.pas"/>
|
<Filename Value="FlatSlider.pas"/>
|
||||||
<IsPartOfProject Value="True"/>
|
<IsPartOfProject Value="True"/>
|
||||||
</Unit>
|
</Unit>
|
||||||
|
<Unit>
|
||||||
|
<Filename Value="FlatCheckBox.pas"/>
|
||||||
|
<IsPartOfProject Value="True"/>
|
||||||
|
</Unit>
|
||||||
|
<Unit>
|
||||||
|
<Filename Value="FlatComboBox.pas"/>
|
||||||
|
<IsPartOfProject Value="True"/>
|
||||||
|
</Unit>
|
||||||
|
<Unit>
|
||||||
|
<Filename Value="FlatEdit.pas"/>
|
||||||
|
<IsPartOfProject Value="True"/>
|
||||||
|
</Unit>
|
||||||
|
<Unit>
|
||||||
|
<Filename Value="FlatSpinEdit.pas"/>
|
||||||
|
<IsPartOfProject Value="True"/>
|
||||||
|
</Unit>
|
||||||
|
<Unit>
|
||||||
|
<Filename Value="FlatFloatSpinEdit.pas"/>
|
||||||
|
<IsPartOfProject Value="True"/>
|
||||||
|
</Unit>
|
||||||
|
<Unit>
|
||||||
|
<Filename Value="FlatListBox.pas"/>
|
||||||
|
<IsPartOfProject Value="True"/>
|
||||||
|
</Unit>
|
||||||
|
<Unit>
|
||||||
|
<Filename Value="FlatRadioButton.pas"/>
|
||||||
|
<IsPartOfProject Value="True"/>
|
||||||
|
</Unit>
|
||||||
<Unit>
|
<Unit>
|
||||||
<Filename Value="EqualizerControl.pas"/>
|
<Filename Value="EqualizerControl.pas"/>
|
||||||
<IsPartOfProject Value="True"/>
|
<IsPartOfProject Value="True"/>
|
||||||
|
|||||||
Reference in New Issue
Block a user