Recent

Author Topic: How to detect which arrow of a TSpinEdit was clicked?  (Read 8007 times)

CM630

  • Hero Member
  • *****
  • Posts: 1082
  • Не съм сигурен, че те разбирам.
    • http://sourceforge.net/u/cm630/profile/
How to detect which arrow of a TSpinEdit was clicked?
« on: February 23, 2017, 09:43:04 am »

Is there a straight-froward way to detect which arrow of a TSpinEdit was clicked?
Oddly, none of the events returns the direction.
I would not like to use a TUpDown since it is too ugly.
I will have to use plenty of TSpinEdit so a recomendation for a decently looking replacement of  TSpinEdit is welcome, if it will spend me repetitive coding.
« Last Edit: February 23, 2017, 10:44:40 am by CM630 »
Лазар 3,2 32 bit (sometimes 64 bit); FPC3,2,2; rev: Lazarus_3_0 on Win10 64bit.

ASerge

  • Hero Member
  • *****
  • Posts: 2223
Re: How to detect which arrow of a TSpinEdit was clicked?
« Reply #1 on: February 23, 2017, 11:16:19 am »
Did you want from SpinEdit only arrows and events by its click?

CM630

  • Hero Member
  • *****
  • Posts: 1082
  • Не съм сигурен, че те разбирам.
    • http://sourceforge.net/u/cm630/profile/
Re: How to detect which arrow of a TSpinEdit was clicked?
« Reply #2 on: February 23, 2017, 11:21:41 am »
I want to be notified when an arrow is clicked and to know which one was clicked (up or down).
Other events (OnChange, OnEditDone I know how to handle).
Лазар 3,2 32 bit (sometimes 64 bit); FPC3,2,2; rev: Lazarus_3_0 on Win10 64bit.

minesadorada

  • Sr. Member
  • ****
  • Posts: 452
  • Retired
Re: How to detect which arrow of a TSpinEdit was clicked?
« Reply #3 on: February 23, 2017, 11:37:28 am »
Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, Forms, Controls, Spin;
  9.  
  10. type
  11.  
  12.   { TForm1 }
  13.  
  14.   TForm1 = class(TForm)
  15.     SpinEdit1: TSpinEdit;
  16.     procedure FormCreate(Sender: TObject);
  17.     procedure SpinEdit1Click(Sender: TObject);
  18.   private
  19.      iStoredSpinEditValue:Integer;
  20.   public
  21.  
  22.   end;
  23.  
  24. var
  25.   Form1: TForm1;
  26.  
  27. implementation
  28.  
  29. {$R *.lfm}
  30.  
  31. { TForm1 }
  32.  
  33. procedure TForm1.FormCreate(Sender: TObject);
  34. begin
  35.   iStoredSpinEditValue:=SpinEdit1.Value;
  36. end;
  37.  
  38. procedure TForm1.SpinEdit1Click(Sender: TObject);
  39. begin
  40.   if SpinEdit1.Value > iStoredSpinEditValue then Caption:='up';
  41.   if SpinEdit1.Value < iStoredSpinEditValue then Caption:='down';
  42.   iStoredSpinEditValue:=SpinEdit1.Value;
  43.  
  44. end;
  45.  
  46. end.
                             
GPL Apps: Health MonitorRetro Ski Run
OnlinePackageManager Components: LazAutoUpdate, LongTimer, PoweredBy, ScrollText, PlaySound, CryptINI

ASerge

  • Hero Member
  • *****
  • Posts: 2223
Re: How to detect which arrow of a TSpinEdit was clicked?
« Reply #4 on: February 23, 2017, 11:38:22 am »
I want to be notified when an arrow is clicked and to know which one was clicked (up or down).
Other events (OnChange, OnEditDone I know how to handle).
Depending on the purpose can be different solutions. In the common case, what exactly has changed value of TSpinEdit useless. Therefore, please clarify why you need it.

CM630

  • Hero Member
  • *****
  • Posts: 1082
  • Не съм сигурен, че те разбирам.
    • http://sourceforge.net/u/cm630/profile/
Re: How to detect which arrow of a TSpinEdit was clicked?
« Reply #5 on: February 23, 2017, 12:20:29 pm »
@minesadorada, Thanks for the proposal, I have already thought of that solution, but:
1. It will require extra code per each SpinEdit.
2. It will not be able to handle the cases when the UP arrow is clicked after the MaxVal is reached.


@ASerge, for me it is important what exactly have changed the TSpinEdit. There is direction property, which sends that info internally, but I could not see it output to to top level (TSpinEdit itself[/size][size=78%]).[/size] :o
Лазар 3,2 32 bit (sometimes 64 bit); FPC3,2,2; rev: Lazarus_3_0 on Win10 64bit.

balazsszekely

  • Guest
Re: How to detect which arrow of a TSpinEdit was clicked?
« Reply #6 on: February 23, 2017, 12:45:33 pm »
@minesadorada
When you click on up/down button Onclick is fired?

@CMD630
The problem is the Up/Down buttons are created by the operating system(class name: msctls_updown32). This is why it's so hard to catch those event. Since you're under windows it is possible, I can make a small example if you like.
« Last Edit: February 23, 2017, 12:48:35 pm by GetMem »

ASerge

  • Hero Member
  • *****
  • Posts: 2223
Re: How to detect which arrow of a TSpinEdit was clicked?
« Reply #7 on: February 23, 2017, 12:54:24 pm »
...but I could not see it output to to top level...
Because it is not necessary. Well, the purpose is not clear. I thought you needed help. Sorry, I haven't time to offer all possible variants until it's fit for you.

balazsszekely

  • Guest
Re: How to detect which arrow of a TSpinEdit was clicked?
« Reply #8 on: February 23, 2017, 01:24:51 pm »
Ok. Here you go(attachment). Please note: every single SpinEdit in your application is linked to a single event namely "OnButtonClick". The event has two parameters, besides the sender:
1. The clicked SpinEdit
2. The button type: btLower or btUpper
All you have to do is assign the event on FormCreate . That's it. Tested on XP, Win7, Win10. Hope you like it  :D

Code: Pascal  [Select][+][-]
  1. unit uSpinEditWatch;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Windows, Classes, SysUtils, Spin, Forms, Controls, ExtCtrls;
  9.  
  10. type
  11.   TButtonType = (btLower, btUpper);
  12.  
  13.   TOnButtonClick = procedure(Sender: TObject; SpinEdit: TSpinEdit; AButtonType: TButtonType) of object;
  14.  
  15.   { TSpinEditWatch }
  16.  
  17.   TSpinEditWatch = class
  18.   private
  19.     FNeedToBreak: Boolean;
  20.     FSpinEdit: TSpinEdit;
  21.     FButtonType: TButtonType;
  22.     FTimer: TTimer;
  23.     FOnButtonClick: TOnButtonClick;
  24.     function Start: Boolean;
  25.     function Stop: Boolean;
  26.     function IsStarted: Boolean;
  27.     function IsSpinEditButton(AHandle: THandle): Boolean;
  28.     function GetFormFromHandle(AHandle: THandle): TForm;
  29.     procedure DoTheMath(AP: TPoint);
  30.     procedure FindSpinEdit(const AControl: TControl; const AP: TPoint);
  31.     procedure OnTimer(Sender: TObject);
  32.   public
  33.     constructor Create;
  34.     destructor Destroy; override;
  35.     property OnButtonClick: TOnButtonClick read FOnButtonClick write FOnButtonClick;
  36.   end;
  37.  
  38. var
  39.   SpinEditWatch: TSpinEditWatch = nil;
  40.  
  41. implementation
  42.  
  43. const
  44.   WH_MOUSE_LL = 14;
  45.  
  46. var
  47.   llMouseHook: HHOOK = 0;
  48.  
  49. function LowLevelMouseHook(nCode: Integer; wParam: WPARAM; lParam: LPARAM): HRESULT; stdcall;
  50. var
  51.   PMHS: PMOUSEHOOKSTRUCT;
  52. begin
  53.   PMHS := PMOUSEHOOKSTRUCT({%H-}Pointer(lParam));
  54.   if nCode = HC_ACTION then
  55.   begin
  56.     if wParam = WM_LBUTTONUP then
  57.       SpinEditWatch.DoTheMath(PMHS^.pt);
  58.   end;
  59.   Result := CallNextHookEx(llMouseHook, nCode, wParam, lParam);
  60. end;
  61.  
  62. function TSpinEditWatch.Start: Boolean;
  63. begin
  64.   if llMouseHook = 0 then
  65.     llMouseHook := SetWindowsHookEx(WH_MOUSE_LL, @LowLevelMouseHook, HInstance, 0);
  66.   Result := (llMouseHook <> 0)
  67. end;
  68.  
  69. function TSpinEditWatch.Stop: Boolean;
  70. begin
  71.   Result := False;
  72.   if (llMouseHook <> 0) and UnhookWindowsHookEx(llMouseHook) then
  73.   begin
  74.     llMouseHook := 0;
  75.     Result := True;
  76.   end;
  77. end;
  78.  
  79. function TSpinEditWatch.IsStarted: Boolean;
  80. begin
  81.   Result := (llMouseHook <> 0)
  82. end;
  83.  
  84. function TSpinEditWatch.IsSpinEditButton(AHandle: THandle): Boolean;
  85. var
  86.   Buffer: array[0..MAX_PATH] of AnsiChar;
  87. begin
  88.   GetClassName(AHandle, @Buffer, MAX_PATH);
  89.   Result := AnsiString(Buffer) = 'msctls_updown32'
  90. end;
  91.  
  92. function TSpinEditWatch.GetFormFromHandle(AHandle: THandle): TForm;
  93. var
  94.   I: Integer;
  95. begin
  96.   Result := nil;
  97.   for I := 0 to Screen.FormCount - 1 do
  98.   begin
  99.     if Screen.Forms[I].Handle = AHandle then
  100.     begin
  101.       Result := Screen.Forms[I];
  102.       Break;
  103.     end;
  104.   end;
  105. end;
  106.  
  107. procedure TSpinEditWatch.FindSpinEdit(const AControl: TControl; const AP: TPoint);
  108. var
  109.   I: Integer;
  110. begin
  111.   if (AControl = nil) or (FNeedToBreak) then
  112.     Exit;
  113.   if AControl is TWinControl then
  114.   begin
  115.     if (TWinControl(AControl) is TSpinEdit) then
  116.     begin
  117.       if PtInRect(TWinControl(AControl).BoundsRect, AP) then
  118.       begin
  119.         FSpinEdit := (TWinControl(AControl) as TSpinEdit);
  120.         if AP.Y > FSpinEdit.Top + (FSpinEdit.Height div 2) then
  121.           FButtonType := btLower
  122.         else
  123.           FButtonType := btUpper;
  124.         FNeedToBreak := True;
  125.       end;
  126.     end;
  127.     for I := 0 to TWinControl(AControl).ControlCount - 1 do
  128.       FindSpinEdit(TWinControl(AControl).Controls[I], AP);
  129.   end;
  130. end;
  131.  
  132. procedure TSpinEditWatch.DoTheMath(AP: TPoint);
  133. var
  134.   hWnd: THandle;
  135.   PID: DWord;
  136.   TheForm: TForm;
  137. begin
  138.   hWnd := WindowFromPoint(AP);
  139.   if not IsSpinEditButton(hWnd) then
  140.     Exit;
  141.  
  142.   PID := 0;
  143.   GetWindowThreadProcessId(hWnd, PID);
  144.   if not PID = GetCurrentProcessId then
  145.     Exit;
  146.  
  147.   TheForm := GetFormFromHandle(GetForegroundWindow);
  148.   if TheForm = nil then
  149.     Exit;
  150.  
  151.   FSpinEdit := nil;
  152.   FNeedToBreak := False;
  153.   AP := TheForm.ScreenToClient(AP);
  154.   FindSpinEdit(TheForm, AP);
  155.   if FSpinEdit <> nil then
  156.     if Assigned(FOnButtonClick) then
  157.       FTimer.Enabled := True;
  158. end;
  159.  
  160. procedure TSpinEditWatch.OnTimer(Sender: TObject);
  161. begin
  162.   FTimer.Enabled := False;
  163.   FOnButtonClick(Self, FSpinEdit, FButtonType);
  164. end;
  165.  
  166. constructor TSpinEditWatch.Create;
  167. begin
  168.   FTimer := TTimer.Create(nil);
  169.   FTimer.Enabled := False;
  170.   FTimer.OnTimer := @OnTimer;
  171.   FTimer.Interval := 10;
  172.   Start;
  173. end;
  174.  
  175. destructor TSpinEditWatch.Destroy;
  176. begin
  177.   if IsStarted then
  178.     Stop;
  179.   FTimer.Free;
  180.   inherited destroy;
  181. end;
  182.  
  183. initialization
  184.   SpinEditWatch := TSpinEditWatch.Create;
  185.  
  186. finalization;
  187.   SpinEditWatch.Free;
  188.  
  189. end.
  190.  

minesadorada

  • Sr. Member
  • ****
  • Posts: 452
  • Retired
Re: How to detect which arrow of a TSpinEdit was clicked?
« Reply #9 on: February 23, 2017, 02:19:10 pm »
@minesadorada
When you click on up/down button Onclick is fired?
Yes.  I suppose one could assign the OnClick event elsewhere in a controls loop, but the O.P. isn't interested in that solution.
GPL Apps: Health MonitorRetro Ski Run
OnlinePackageManager Components: LazAutoUpdate, LongTimer, PoweredBy, ScrollText, PlaySound, CryptINI

creaothceann

  • Full Member
  • ***
  • Posts: 117
Re: How to detect which arrow of a TSpinEdit was clicked?
« Reply #10 on: February 23, 2017, 07:43:24 pm »
It will not be able to handle the cases when the UP arrow is clicked after the MaxVal is reached

In an old program of mine I handled that by setting Min to -1 and Max to 1, and then checking the current value in the handler before updating a private value and resetting the control's value back to zero.

CM630

  • Hero Member
  • *****
  • Posts: 1082
  • Не съм сигурен, че те разбирам.
    • http://sourceforge.net/u/cm630/profile/
Re: How to detect which arrow of a TSpinEdit was clicked?
« Reply #11 on: February 24, 2017, 06:58:13 am »
It will not be able to handle the cases when the UP arrow is clicked after the MaxVal is reached

In an old program of mine I handled that by setting Min to -1 and Max to 1, and then checking the current value in the handler before updating a private value and resetting the control's value back to zero.

If I get you right, you store the maximum values in separate variables and if they are exceeded, you return them back? That should do.
Thanks for the help to all of you. I am surprised that something so simple should take some much coding and efforts.
I will try to modify the Lazarus tSpinEdit. FloatSpinEdit also needs some improvent, soefforts seem reasonable.
« Last Edit: February 24, 2017, 11:43:40 am by CM630 »
Лазар 3,2 32 bit (sometimes 64 bit); FPC3,2,2; rev: Lazarus_3_0 on Win10 64bit.

howardpc

  • Hero Member
  • *****
  • Posts: 4144
Re: How to detect which arrow of a TSpinEdit was clicked?
« Reply #12 on: February 24, 2017, 10:01:51 am »
To do this (indirectly) in a cross-platform way you can do the following:

Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4. {$AppType console}
  5.  
  6. interface
  7.  
  8. uses
  9.   Classes, Forms, Controls, Spin, typinfo;
  10.  
  11. type
  12.  
  13.   TButtonType = (btLower, btUpper);
  14.  
  15.   TOnButtonClick = procedure(Sender: TObject; aButtonType: TButtonType) of object;
  16.  
  17.   { TSpinEditEx }
  18.  
  19.   TSpinEditEx = class(TSpinEdit)
  20.   private
  21.     FLastValue: integer;
  22.     FButtonClick: TOnButtonClick;
  23.     procedure DoOnButtonClick;
  24.   protected
  25.     procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); override;
  26.     procedure MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); override;
  27.   public
  28.     property ButtonClick: TOnButtonClick read FButtonClick write FButtonClick;
  29.   end;
  30.  
  31.   { TForm1 }
  32.  
  33.   TForm1 = class(TForm)
  34.     procedure FormCreate(Sender: TObject);
  35.   private
  36.     FSpin: TSpinEditEx;
  37.     procedure SpinOnButtonClick(Sender: TObject; aButtonType: TButtonType);
  38.   end;
  39.  
  40.  
  41. var
  42.   Form1: TForm1;
  43.  
  44. implementation
  45.  
  46. {$R *.lfm}
  47.  
  48. { TForm1 }
  49.  
  50. procedure TForm1.FormCreate(Sender: TObject);
  51. begin
  52.   FSpin:=TSpinEditEx.Create(Self);
  53.   with FSpin.GetControlClassDefaultSize do
  54.     FSpin.SetInitialBounds(10, 10, cx, cy);
  55.   FSpin.FButtonClick:=@SpinOnButtonClick;
  56.   FSpin.Parent:=Self;
  57. end;
  58.  
  59. procedure TForm1.SpinOnButtonClick(Sender: TObject; aButtonType: TButtonType);
  60. begin
  61.   WriteLn('TForm1.SpinOnButtonClick, aButtonType = ',GetEnumName(TypeInfo(TButtonType),Ord(aButtonType)));
  62. end;
  63.  
  64. { TSpinEditEx }
  65.  
  66. procedure TSpinEditEx.DoOnButtonClick;
  67. begin
  68.   if not Assigned(FButtonClick) then
  69.     Exit;
  70.   case (Value > FLastValue) of
  71.     True: FButtonClick(Self, btUpper);
  72.     False: if (Value < MaxValue) then
  73.              FButtonClick(Self, btLower)
  74.            else FButtonClick(Self, btUpper);
  75.   end;
  76. end;
  77.  
  78. procedure TSpinEditEx.MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
  79. begin
  80.   inherited MouseDown(Button, Shift, X, Y);
  81.   FLastValue:=Value;
  82. end;
  83.  
  84. procedure TSpinEditEx.MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
  85. begin
  86.   inherited MouseUp(Button, Shift, X, Y);
  87.   DoOnButtonClick;
  88. end;
  89.  
  90. end.
« Last Edit: February 24, 2017, 01:04:29 pm by howardpc »

CM630

  • Hero Member
  • *****
  • Posts: 1082
  • Не съм сигурен, че те разбирам.
    • http://sourceforge.net/u/cm630/profile/
Re: How to detect which arrow of a TSpinEdit was clicked?
« Reply #13 on: February 28, 2017, 10:39:44 am »
@Howard, I tried you example on Friday but for some reason it did not work and I did not try much further.
But I have seccesfully added an OnUpDown event in spin edit (so far for Win only).
I will try to add an offset parameter and change the way in which step is used, to provide adequate support for discrete values.
Also TSPinEdit does not seem to support thousands separators.
Лазар 3,2 32 bit (sometimes 64 bit); FPC3,2,2; rev: Lazarus_3_0 on Win10 64bit.

 

TinyPortal © 2005-2018