Add custom flat form controls

This commit is contained in:
2026-05-25 14:43:05 +03:00
parent 685370c91f
commit b02f81750a
10 changed files with 3906 additions and 206 deletions
+13 -28
View File
@@ -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;
+285
View File
@@ -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.
+532
View File
@@ -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
View File
@@ -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.
+821
View File
@@ -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
View File
@@ -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.
+294
View File
@@ -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.
+777
View File
@@ -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
View File
@@ -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)';
+28
View File
@@ -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"/>