123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700 |
- unit UpDownEdit;
- interface
- uses
- Windows, ComCtrls, Controls, ExtCtrls, Classes, Graphics, Messages, Forms,
- StdCtrls, Menus, SysUtils;
- { TUpDownEdit }
- type
- TValueType = (vtInt, vtFloat, vtHex);
- TUpDownEditGetValue = procedure(Sender: TObject; Text: string;
- var Value: Extended; var Handled: Boolean) of object;
- TUpDownEditSetValue = procedure(Sender: TObject; Value: Extended;
- var Text: string; var Handled: Boolean) of object;
- TUpDownEdit = class(TCustomEdit)
- private
- FAlignment: TAlignment;
- FMinValue: Extended;
- FMaxValue: Extended;
- FIncrement: Extended;
- FDecimal: Byte;
- FChanging: Boolean;
- FEditorEnabled: Boolean;
- FValueType: TValueType;
- FArrowKeys: Boolean;
- FButtonsVisible: Boolean;
- FDarkMode: Boolean;
- FOnTopClick: TNotifyEvent;
- FOnBottomClick: TNotifyEvent;
- FUpDown: TCustomUpDown;
- FOnGetValue: TUpDownEditGetValue;
- FOnSetValue: TUpDownEditSetValue;
- procedure UpDownClick(Sender: TObject; Button: TUDBtnType);
- function GetMinHeight: Integer;
- procedure GetTextHeight(var SysHeight, Height: Integer);
- function GetValue: Extended;
- function CheckValue(NewValue: Extended): Extended;
- function GetAsInteger: Longint;
- function IsIncrementStored: Boolean;
- function IsMaxStored: Boolean;
- function IsMinStored: Boolean;
- function IsValueStored: Boolean;
- procedure SetArrowKeys(Value: Boolean);
- procedure SetAsInteger(NewValue: Longint);
- procedure SetValue(NewValue: Extended);
- procedure SetValueType(NewType: TValueType);
- procedure SetDecimal(NewValue: Byte);
- function GetButtonWidth: Integer;
- procedure RecreateButton;
- procedure ResizeButton;
- procedure SetEditRect;
- procedure SetAlignment(Value: TAlignment);
- procedure SetButtonsVisible(Value: Boolean);
- procedure SetDarkMode(Value: Boolean);
- procedure WMSize(var Message: TWMSize); message WM_SIZE;
- procedure CMEnter(var Message: TMessage); message CM_ENTER;
- procedure CMExit(var Message: TCMExit); message CM_EXIT;
- procedure WMPaste(var Message: TWMPaste); message WM_PASTE;
- procedure WMCut(var Message: TWMCut); message WM_CUT;
- procedure CMCtl3DChanged(var Message: TMessage); message CM_CTL3DCHANGED;
- procedure CMEnabledChanged(var Message: TMessage); message CM_ENABLEDCHANGED;
- procedure CMFontChanged(var Message: TMessage); message CM_FONTCHANGED;
- procedure CMBiDiModeChanged(var Message: TMessage); message CM_BIDIMODECHANGED;
- protected
- procedure Change; override;
- function IsValidChar(Key: Char): Boolean; virtual;
- procedure UpClick(Sender: TObject); virtual;
- procedure DownClick(Sender: TObject); virtual;
- procedure KeyDown(var Key: Word; Shift: TShiftState); override;
- procedure KeyPress(var Key: Char); override;
- procedure CreateParams(var Params: TCreateParams); override;
- procedure CreateWnd; override;
- function DefBtnWidth: Integer;
- public
- constructor Create(AOwner: TComponent); override;
- destructor Destroy; override;
- property AsInteger: Longint read GetAsInteger write SetAsInteger default 0;
- property Text;
- published
- property Alignment: TAlignment read FAlignment write SetAlignment
- default taLeftJustify;
- property ArrowKeys: Boolean read FArrowKeys write SetArrowKeys default True;
- property Decimal: Byte read FDecimal write SetDecimal default 2;
- property EditorEnabled: Boolean read FEditorEnabled write FEditorEnabled default True;
- property Increment: Extended read FIncrement write FIncrement stored IsIncrementStored;
- property MaxValue: Extended read FMaxValue write FMaxValue stored IsMaxStored;
- property MinValue: Extended read FMinValue write FMinValue stored IsMinStored;
- property ValueType: TValueType read FValueType write SetValueType default vtInt;
- property Value: Extended read GetValue write SetValue stored IsValueStored;
- property ButtonsVisible: Boolean read FButtonsVisible write SetButtonsVisible default True;
- property DarkMode: Boolean read FDarkMode write SetDarkMode default False;
- property AutoSelect;
- property AutoSize;
- property BorderStyle;
- property Color;
- property Ctl3D;
- property DragCursor;
- property DragMode;
- property Enabled;
- property Font;
- property Anchors;
- property BiDiMode;
- property Constraints;
- property DragKind;
- property ParentBiDiMode;
- property ImeMode;
- property ImeName;
- property MaxLength;
- property ParentColor;
- property ParentCtl3D;
- property ParentFont;
- property ParentShowHint;
- property PopupMenu;
- property ReadOnly;
- property ShowHint;
- property TabOrder;
- property TabStop;
- property Visible;
- property OnBottomClick: TNotifyEvent read FOnBottomClick write FOnBottomClick;
- property OnTopClick: TNotifyEvent read FOnTopClick write FOnTopClick;
- property OnGetValue: TUpDownEditGetValue read FOnGetValue write FOnGetValue;
- property OnSetValue: TUpDownEditSetValue read FOnSetValue write FOnSetValue;
- property OnChange;
- property OnClick;
- property OnDblClick;
- property OnDragDrop;
- property OnDragOver;
- property OnEndDrag;
- property OnEnter;
- property OnExit;
- property OnKeyDown;
- property OnKeyPress;
- property OnKeyUp;
- property OnMouseDown;
- property OnMouseMove;
- property OnMouseUp;
- property OnStartDrag;
- property OnContextPopup;
- property OnMouseWheelDown;
- property OnMouseWheelUp;
- property OnEndDock;
- property OnStartDock;
- end;
- procedure Register;
- implementation
- uses
- CommCtrl, PasTools, Math;
- procedure Register;
- begin
- RegisterComponents('Martin', [TUpDownEdit]);
- end;
- type
- TEmbededUpDown = class(TCustomUpDown)
- private
- FChanging: Boolean;
- procedure ScrollMessage(var Message: TWMVScroll);
- procedure WMHScroll(var Message: TWMHScroll); message CN_HSCROLL;
- procedure WMVScroll(var Message: TWMVScroll); message CN_VSCROLL;
- procedure WMSize(var Message: TWMSize); message WM_SIZE;
- protected
- procedure CreateWnd; override;
- public
- constructor Create(AOwner: TComponent); override;
- destructor Destroy; override;
- published
- property OnClick;
- end;
- constructor TEmbededUpDown.Create(AOwner: TComponent);
- begin
- inherited Create(AOwner);
- Orientation := udVertical;
- Min := -1;
- Max := 1;
- Position := 0;
- end;
- destructor TEmbededUpDown.Destroy;
- begin
- OnClick := nil;
- inherited Destroy;
- end;
- procedure TEmbededUpDown.CreateWnd;
- var
- UpDownEdit: TUpDownEdit;
- begin
- inherited;
- UpDownEdit := Owner as TUpDownEdit;
- if Assigned(UpDownEdit) then
- begin
- // Theme style for "Spin" found using msstylEditor in aero.msstyles
- if UpDownEdit.DarkMode then
- SetDarkModeTheme(Self, 'Explorer');
- // edit rect is reset while changing the theme, calling always for consistency
- UpDownEdit.SetEditRect;
- end;
- end;
- procedure TEmbededUpDown.ScrollMessage(var Message: TWMVScroll);
- begin
- if Message.ScrollCode = SB_THUMBPOSITION then begin
- if not FChanging then begin
- FChanging := True;
- try
- if Message.Pos > 0 then Click(btNext)
- else if Message.Pos < 0 then Click(btPrev);
- if HandleAllocated then
- SendMessage(Handle, UDM_SETPOS, 0, 0);
- finally
- FChanging := False;
- end;
- end;
- end;
- end;
- procedure TEmbededUpDown.WMHScroll(var Message: TWMHScroll);
- begin
- ScrollMessage(TWMVScroll(Message));
- end;
- procedure TEmbededUpDown.WMVScroll(var Message: TWMVScroll);
- begin
- ScrollMessage(Message);
- end;
- procedure TEmbededUpDown.WMSize(var Message: TWMSize);
- var
- Def: Integer;
- begin
- inherited;
- Def := TUpDownEdit(Parent).DefBtnWidth;
- if Width <> Def then Width := Def;
- end;
- { TUpDownEdit }
- constructor TUpDownEdit.Create(AOwner: TComponent);
- begin
- inherited Create(AOwner);
- Text := '0';
- ControlStyle := ControlStyle - [csSetCaption];
- FIncrement := 1.0;
- FDecimal := 2;
- FEditorEnabled := True;
- FArrowKeys := True;
- FButtonsVisible := True;
- FDarkMode := False;
- RecreateButton;
- end;
- destructor TUpDownEdit.Destroy;
- begin
- Destroying;
- FChanging := True;
- if FUpDown <> nil then
- begin
- FUpDown.Free;
- FUpDown := nil;
- end;
- inherited Destroy;
- end;
- procedure TUpDownEdit.RecreateButton;
- begin
- if (csDestroying in ComponentState) then Exit;
- FUpDown.Free;
- FUpDown := nil;
- FUpDown := TEmbededUpDown.Create(Self);
- with TEmbededUpDown(FUpDown) do begin
- Visible := True;
- SetBounds(0, 0, DefBtnWidth, Self.Height);
- if (BiDiMode = bdRightToLeft) then Align := alLeft
- else Align := alRight;
- Parent := Self;
- OnClick := UpDownClick;
- end;
- end;
- procedure TUpDownEdit.SetArrowKeys(Value: Boolean);
- begin
- FArrowKeys := Value;
- ResizeButton;
- end;
- procedure TUpDownEdit.UpDownClick(Sender: TObject; Button: TUDBtnType);
- begin
- if TabStop and CanFocus then SetFocus;
- case Button of
- btNext: UpClick(Sender);
- btPrev: DownClick(Sender);
- end;
- end;
- function TUpDownEdit.GetButtonWidth: Integer;
- begin
- if FUpDown.Visible then Result := FUpDown.Width
- else Result := 0;
- end;
- function TUpDownEdit.DefBtnWidth: Integer;
- begin
- Result := 15;
- if Parent <> nil then
- begin
- Result := ScaleByPixelsPerInch(Result, Self);
- Result := Math.Min(GetSystemMetricsForControl(Self, SM_CXVSCROLL), Result);
- end;
- end;
- procedure TUpDownEdit.ResizeButton;
- begin
- if FUpDown <> nil then
- begin
- FUpDown.Width := DefBtnWidth;
- if (BiDiMode = bdRightToLeft) then FUpDown.Align := alLeft
- else FUpDown.Align := alRight;
- FUpDown.Visible := ButtonsVisible;
- end
- end;
- procedure TUpDownEdit.KeyDown(var Key: Word; Shift: TShiftState);
- begin
- inherited KeyDown(Key, Shift);
- if ArrowKeys and (Key in [VK_UP, VK_DOWN]) then
- begin
- if Key = VK_UP then UpClick(Self)
- else
- if Key = VK_DOWN then DownClick(Self);
- Key := 0;
- end;
- end;
- procedure TUpDownEdit.Change;
- begin
- if not FChanging then inherited Change;
- end;
- procedure TUpDownEdit.KeyPress(var Key: Char);
- begin
- if not IsValidChar(Key) then
- begin
- Key := #0;
- MessageBeep(MB_ICONHAND)
- end;
- if Key <> #0 then
- begin
- inherited KeyPress(Key);
- if (Key = Char(VK_RETURN)) or (Key = Char(VK_ESCAPE)) then
- begin
- { must catch and remove this, since is actually multi-line }
- GetParentForm(Self).Perform(CM_DIALOGKEY, Byte(Key), 0);
- if Key = Char(VK_RETURN) then Key := #0;
- end;
- end;
- end;
- function TUpDownEdit.IsValidChar(Key: Char): Boolean;
- var
- ValidChars: TSysCharSet;
- begin
- ValidChars := ['+', '-', '0'..'9'];
- if ValueType = vtFloat then
- begin
- if Pos(FormatSettings.DecimalSeparator, Text) = 0 then
- ValidChars := ValidChars + [FormatSettings.DecimalSeparator];
- if Pos('E', AnsiUpperCase(Text)) = 0 then
- ValidChars := ValidChars + ['e', 'E'];
- end
- else
- if ValueType = vtHex then
- begin
- ValidChars := ValidChars + ['A'..'F', 'a'..'f'];
- end;
- Result := CharInSet(Key, ValidChars) or (Key < #32);
- if not FEditorEnabled and Result and ((Key >= #32) or
- (Key = Char(VK_BACK)) or (Key = Char(VK_DELETE))) then Result := False;
- end;
- procedure TUpDownEdit.CreateParams(var Params: TCreateParams);
- const
- Alignments: array[Boolean, TAlignment] of DWORD =
- ((ES_LEFT, ES_RIGHT, ES_CENTER), (ES_RIGHT, ES_LEFT, ES_CENTER));
- begin
- inherited CreateParams(Params);
- Params.Style := Params.Style or ES_MULTILINE or WS_CLIPCHILDREN or
- Alignments[UseRightToLeftAlignment, FAlignment];
- end;
- procedure TUpDownEdit.CreateWnd;
- begin
- inherited CreateWnd;
- ResizeButton; // now we know the scaling factor
- SetEditRect;
- SetValue(Value);
- if DarkMode then
- SetDarkModeTheme(Self, 'CFD');
- end;
- procedure TUpDownEdit.SetEditRect;
- var
- Loc: TRect;
- begin
- if (BiDiMode = bdRightToLeft) then
- SetRect(Loc, GetButtonWidth + 1, 0, ClientWidth - 1, ClientHeight + 1)
- else
- SetRect(Loc, 0, 0, ClientWidth - GetButtonWidth - 2, ClientHeight + 1);
- SendMessage(Handle, EM_SETRECTNP, 0, Longint(@Loc));
- end;
- procedure TUpDownEdit.SetAlignment(Value: TAlignment);
- begin
- if FAlignment <> Value then
- begin
- FAlignment := Value;
- RecreateWnd;
- end;
- end;
- procedure TUpDownEdit.WMSize(var Message: TWMSize);
- var
- MinHeight: Integer;
- begin
- inherited;
- MinHeight := GetMinHeight;
- { text edit bug: if size to less than minheight, then edit ctrl does
- not display the text }
- if Height < MinHeight then
- Height := MinHeight
- else begin
- ResizeButton;
- SetEditRect;
- end;
- end;
- procedure TUpDownEdit.GetTextHeight(var SysHeight, Height: Integer);
- var
- DC: HDC;
- SaveFont: HFont;
- SysMetrics, Metrics: TTextMetric;
- begin
- DC := GetDC(0);
- GetTextMetrics(DC, SysMetrics);
- SaveFont := SelectObject(DC, Font.Handle);
- GetTextMetrics(DC, Metrics);
- SelectObject(DC, SaveFont);
- ReleaseDC(0, DC);
- SysHeight := SysMetrics.tmHeight;
- Height := Metrics.tmHeight;
- end;
- function TUpDownEdit.GetMinHeight: Integer;
- var
- I, H: Integer;
- begin
- GetTextHeight(I, H);
- if I > H then I := H;
- Result := H + (GetSystemMetricsForControl(Self, SM_CYBORDER) * 4) + 1;
- end;
- procedure TUpDownEdit.UpClick(Sender: TObject);
- var
- OldText: string;
- begin
- if ReadOnly then MessageBeep(MB_ICONHAND)
- else begin
- FChanging := True;
- try
- OldText := inherited Text;
- Value := Value + FIncrement;
- finally
- FChanging := False;
- end;
- if CompareText(inherited Text, OldText) <> 0 then
- begin
- Modified := True;
- Change;
- end;
- if Assigned(FOnTopClick) then FOnTopClick(Self);
- end;
- end;
- procedure TUpDownEdit.DownClick(Sender: TObject);
- var
- OldText: string;
- begin
- if ReadOnly then MessageBeep(MB_ICONHAND)
- else begin
- FChanging := True;
- try
- OldText := inherited Text;
- Value := Value - FIncrement;
- finally
- FChanging := False;
- end;
- if CompareText(inherited Text, OldText) <> 0 then
- begin
- Modified := True;
- Change;
- end;
- if Assigned(FOnBottomClick) then FOnBottomClick(Self);
- end;
- end;
- procedure TUpDownEdit.CMBiDiModeChanged(var Message: TMessage);
- begin
- inherited;
- ResizeButton;
- SetEditRect;
- end;
- procedure TUpDownEdit.CMFontChanged(var Message: TMessage);
- begin
- inherited;
- ResizeButton;
- SetEditRect;
- end;
- procedure TUpDownEdit.CMCtl3DChanged(var Message: TMessage);
- begin
- inherited;
- ResizeButton;
- SetEditRect;
- end;
- procedure TUpDownEdit.CMEnabledChanged(var Message: TMessage);
- begin
- inherited;
- if FUpDown <> nil then
- begin
- FUpDown.Enabled := Enabled;
- ResizeButton;
- end;
- end;
- procedure TUpDownEdit.WMPaste(var Message: TWMPaste);
- begin
- if not FEditorEnabled or ReadOnly then Exit;
- inherited;
- end;
- procedure TUpDownEdit.WMCut(var Message: TWMCut);
- begin
- if not FEditorEnabled or ReadOnly then Exit;
- inherited;
- end;
- procedure TUpDownEdit.CMExit(var Message: TCMExit);
- begin
- inherited;
- SetValue(Value);
- end;
- procedure TUpDownEdit.CMEnter(var Message: TMessage);
- begin
- if AutoSelect and not (csLButtonDown in ControlState) then SelectAll;
- inherited;
- end;
- function TUpDownEdit.GetValue: Extended;
- var
- Handled: Boolean;
- begin
- Handled := False;
- if Assigned(FOnGetValue) then FOnGetValue(Self, Text, Result, Handled);
- if not Handled then
- begin
- try
- if ValueType = vtFloat then Result := StrToFloat(Text)
- else if ValueType = vtHex then Result := StrToInt('$' + Text)
- else Result := StrToInt(Text);
- except
- if ValueType = vtFloat then Result := FMinValue
- else Result := Trunc(FMinValue);
- end;
- end;
- end;
- procedure TUpDownEdit.SetValue(NewValue: Extended);
- var
- Handled: Boolean;
- AText: string;
- begin
- NewValue := CheckValue(NewValue);
- Handled := False;
- if Assigned(FOnSetValue) then
- begin
- AText := Text;
- FOnSetValue(Self, NewValue, AText, Handled);
- if Handled then Text := AText;
- end;
- if not Handled then
- begin
- if ValueType = vtFloat then
- Text := FloatToStrF(NewValue, ffFixed, 15, FDecimal)
- else if ValueType = vtHex then
- Text := IntToHex(Round(NewValue), 1)
- else
- Text := IntToStr(Round(NewValue));
- end;
- end;
- function TUpDownEdit.GetAsInteger: Longint;
- begin
- Result := Trunc(GetValue);
- end;
- procedure TUpDownEdit.SetAsInteger(NewValue: Longint);
- begin
- SetValue(NewValue);
- end;
- procedure TUpDownEdit.SetValueType(NewType: TValueType);
- begin
- if FValueType <> NewType then
- begin
- FValueType := NewType;
- Value := GetValue;
- if FValueType in [vtInt, vtHex] then
- begin
- FIncrement := Round(FIncrement);
- if FIncrement = 0 then FIncrement := 1;
- end;
- end;
- end;
- function TUpDownEdit.IsIncrementStored: Boolean;
- begin
- Result := FIncrement <> 1.0;
- end;
- function TUpDownEdit.IsMaxStored: Boolean;
- begin
- Result := (MaxValue <> 0.0);
- end;
- function TUpDownEdit.IsMinStored: Boolean;
- begin
- Result := (MinValue <> 0.0);
- end;
- function TUpDownEdit.IsValueStored: Boolean;
- begin
- Result := (GetValue <> MinValue);
- end;
- procedure TUpDownEdit.SetDecimal(NewValue: Byte);
- begin
- if FDecimal <> NewValue then
- begin
- FDecimal := NewValue;
- Value := GetValue;
- end;
- end;
- function TUpDownEdit.CheckValue(NewValue: Extended): Extended;
- begin
- Result := NewValue;
- if (FMaxValue <> FMinValue) then
- begin
- if NewValue < FMinValue then
- Result := FMinValue
- else if NewValue > FMaxValue then
- Result := FMaxValue;
- end;
- end;
- procedure TUpDownEdit.SetButtonsVisible(Value: Boolean);
- begin
- if ButtonsVisible <> Value then
- begin
- FButtonsVisible := Value;
- ResizeButton;
- SetEditRect;
- end;
- end;
- procedure TUpDownEdit.SetDarkMode(Value: Boolean);
- begin
- if DarkMode <> Value then
- begin
- FDarkMode := Value;
- RecreateWnd;
- end;
- end;
- initialization
- end.
|