Recent

Author Topic: [Solved] Focus Tracker (component tracking focus changes and cause)  (Read 12962 times)

dot.not

  • New Member
  • *
  • Posts: 22
  • The answer is 42
Greetings everyone
I have a drawgrid descendant and I need a way to make it aware of how it got activated and which control was active before

For example it needs to distinguish if it got focus by tab or shift+tab or click and the control (if any) that lost focus

Thanks in advance


Edit

i.
Changed the thread header to better reflect the subject.
The old header was:
Control that is aware of how it got activated ( CLICK or TAB or SHIFT+TAB )

ii.
The complete unit code for this component is in Reply #24 on Page #2
« Last Edit: August 27, 2017, 11:17:52 pm by dot.not »
There's 10 kinds of people

howardpc

  • Hero Member
  • *****
  • Posts: 4144
I don't see how a grid, unaided, could possibly have knowledge of  events from past history.
I suppose to pass a grid that sort of information you would need some sort of keypress-mouse manager class that tracked such events and focus changes, and stored recent ones in a way that could be queried by your grid.

dot.not

  • New Member
  • *
  • Posts: 22
  • The answer is 42

Hmm... excuse me, I dont realy need the the grid to be able to mine the knowledge itself, it just needs to ne made aware of, or served with, the prev focus info.

Anyhow, here is as far as I got:

I wrote a FocusTracker component that maintains an internal list of objects, one for every tWinControl on the form.
Each of those objects hooks into the WindowProc queue of said tWincontrol and so can monitor CM_ENTER messages and report them back to the FocusTracker, who exposes the last activated control (the one that was active bofore the current ActiveControl) and fires a notification event.

All this works just fine and so I can keep track where the focus came from.

Now I'm missing how to get the Keyboard key and ShiftState and/or Mouse click that resulted in said focus change.
I'm not advanced enough to know how to decode the message wparams and lparams etc to get said info.
If anyone can help out with an idea or an example, it would be very much appreciated.

Thanks
There's 10 kinds of people

RAW

  • Hero Member
  • *****
  • Posts: 868
crazy shit... I like the profile name...  :D :D :D :D
Windows 7 Pro (x64 Sp1) & Windows XP Pro (x86 Sp3).

dot.not

  • New Member
  • *
  • Posts: 22
  • The answer is 42
cheers RAW \o/
 >:(  and where are my pointers mr. senior member?  >:D

seriously tho (and back on topic) I could realy use a hint or an example how to get the key/click
pretty please
There's 10 kinds of people

lainz

  • Hero Member
  • *****
  • Posts: 4470
    • https://lainz.github.io/
cheers RAW \o/
 >:(  and where are my pointers mr. senior member?  >:D

seriously tho (and back on topic) I could realy use a hint or an example how to get the key/click
pretty please

Maybe you need to record every single click, key press. When the OnEnter event (Handler for control receiving the focus.) fires read the latest event registered by yourself (key / mouse)

dot.not

  • New Member
  • *
  • Posts: 22
  • The answer is 42

How would I go about doing exactly that, thats is my question.
In other words, how can I monitor the clicks and keys?
(without attaching onKeyXXX and onClick to each control of course)

I know there is a CN_KEYDOWN message but idk what the structure of that message holds
(in other words: how to get the key and state info from that structure)
or if thats even the right message I should be monitoring.

And the only ones I can imagine that could be for the mouse are CM_MOUSEACTIVATE which sadly is unimplemented
and CM_BUTTONPRESSED, for wich, again, I have no info for.
There's 10 kinds of people

jamie

  • Hero Member
  • *****
  • Posts: 6130
The TApplication.OnMessage can be used to see all messages sent to the app.

that only one's you won't see are those that were sent via a SendMesasage instead of
the PostMessage. SendMessage is a direct root to the control.


 Tab order is set in each control so when ever you see the TAB control messages you
can test the current control that has focus, get its TabPos and scan the Controls Array
for controls with less or more TabPos values.

The only true wisdom is knowing you know nothing

RAW

  • Hero Member
  • *****
  • Posts: 868
Quote
>:(  and where are my pointers mr. senior member?  >:D
That doesn't mean that I know something about programming... it's just a fancy counter ...
Of course I can program the best "Hello World" program in the galaxy...  :P

OK, my 2 cents...

First I would probably play around with ...
Code: Pascal  [Select][+][-]
  1. Function GetControl: TControl;
  2.  Begin
  3.   Result:= FindDragTarget(Mouse.CursorPos, True);
  4.  End;
  5.  
  6. Procedure TForm1.TimerTimer(Sender: TObject);
  7.   Var
  8.    p  : TPoint;
  9.    c  : TControl;
  10.    str: String;
  11.  Begin
  12.   str:= Screen.ActiveControl.Name;
  13.    If str <> Label1.Caption
  14.    Then
  15.     Begin
  16.      Label2.Caption:= Label1.Caption; // last Focus
  17.      Label1.Caption:= str;            // curr Focus
  18.     End;
  19.  
  20.   p:= ScreenToClient(Mouse.CursorPos);
  21.  
  22.   If PtInRect(Self.ClientRect, p)
  23.   Then
  24.    Begin
  25.     c:= GetControl;
  26.  
  27.     If c <> Nil
  28.     Then Memo1.Text:= c.Name;
  29.    End;
  30.  End;

Or maybe this... // btw: what about WM_SetFocus and WM_KillFocus
Code: Pascal  [Select][+][-]
  1. UNIT Unit1;
  2. {$MODE OBJFPC}{$H+}{$J-}
  3.  
  4. Interface
  5.  USES
  6.   Classes, SysUtils, Forms, Controls, ExtCtrls, StdCtrls;
  7.  
  8.  TYPE
  9.   TForm1 = Class(TForm)
  10.  
  11.    Timer  : TTimer;
  12.  
  13.    Label1 : TLabel;
  14.    Label2 : TLabel;
  15.  
  16.    Button1: TButton;
  17.    Button2: TButton;
  18.    Button3: TButton;
  19.  
  20.    Edit1  : TEdit;
  21.    Memo1  : TMemo;
  22.  
  23.    Procedure TimerTimer (Sender: TObject);
  24.  
  25.     PRIVATE
  26.      wc, wcCurr, wcLast: TWinControl;
  27.   End;
  28.  
  29.  VAR
  30.   Form1: TForm1;
  31.  
  32. Implementation
  33. {$R *.LFM}
  34.  
  35.  
  36. Procedure TForm1.TimerTimer(Sender: TObject);
  37.  Begin
  38.   wc:= Screen.ActiveControl;
  39.    If wc <> wcCurr
  40.    Then
  41.     Begin
  42.      wcLast:= wcCurr; // last Focus
  43.      wcCurr:= wc;     // curr Focus
  44.     End;
  45.  
  46.   If wcCurr <> Nil
  47.   Then Label1.Caption:= 'CurrFocus = '+ wcCurr.Name;
  48.  
  49.   If wcLast <> Nil
  50.   Then Label2.Caption:= 'LastFocus = '+ wcLast.Name;
  51.  End;
  52.  
  53. END.

Then I would probably play around with Application.OnMessage - WM_LBUTTONDOWN / WM_LBUTTONUP..
or wPARAM and MK_Control or GetKeyState...

What should I say... I never needed this, so I have no idea, but you probably find a way to solve this...  :)
« Last Edit: August 23, 2017, 05:22:52 am by RAW »
Windows 7 Pro (x64 Sp1) & Windows XP Pro (x86 Sp3).

Zath

  • Sr. Member
  • ****
  • Posts: 391
Can you incorporate the Tag property of each component in your test ?
Give each component a unique Tag integer ID, create a list of them and test for that value.

Thaddy

  • Hero Member
  • *****
  • Posts: 14380
  • Sensorship about opinions does not belong here.
Can you incorporate the Tag property of each component in your test ?
Give each component a unique Tag integer ID, create a list of them and test for that value.
Using tag when you write a descendant is really bad coding....Introduce a field.
Multiple working answers already given.
Object Pascal programmers should get rid of their "component fetish" especially with the non-visuals.

dot.not

  • New Member
  • *
  • Posts: 22
  • The answer is 42
i: I want to thank everyone who took the time to read my posts and especially those who posted.
Thank you one and all.

ii (small off-topic excursion):
@RAW you know you can even write the bestest ever "Hello Solar System" app, I'm still way above. I won the 42-annually award of last "42 and beyond" for writing the best "Hello Multiverse and all Alternative Timelines" app. It will be included in the next revision of the hitchhiker's guide. Wanna see you beating that one ;p
Seriously tho, thanks for your vote of confidence, truth be told I'm close, but I have some issues yet to solve.

iii (and back on-topic) (sigh):
So I'm building a component. Therefore I will refrain from using any members/methods/handlers exposed (published) by other components for design-time use.
I will, for example, never use Tag or OnClick from inside a component, since those are there to be used by component users (devs) and can, and at times will, change at runtime by the application.

For example, lets say im writing a tStringGrid descendant and I want to catch the new control's PageUp and PageDown keys and change their behavior.
It would be wrong to assign a handler to the OnKeyDown notification event. That one is exposed to be used by the people who will use my new component.
Instead I should (and would) override my ancestor's KeyDown virtual method, where I do what needs doing and also make sure to call the inherited KeyDown to maintain the method's execution chain.

Now here, in this component, I need a way to monitor a plethora of other controls, which I am not a descendant of.
IMHO the only reasonable way of doing that is to hook into each control's messaging procedure.
(NOTE: if anyone has a better approach plase by all means, speak up and thank you kindly)
This is the basic handler every TControl + descendats have and the most centralized way of intercepting requests that controls have to respond/react to, be it PostMessage or SendMessage etc....
AFAIK all messages are routed though this method (except perhaps those issued through Perform? Q.Q)

I wrote a FocusTracker component that maintains an internal list of objects, one for every tWinControl on the form.
Each of those objects hooks into the WindowProc queue of said tWincontrol and so can monitor CM_ENTER messages and report them back to the FocusTracker, who exposes the last activated control (the one that was active bofore the current ActiveControl) and fires a notification event.

Let me introduce the WndProc or WindowProc.

Now, for all (me included) who feel the academic approach to be vastly inferior to a hands-on code example, here's a striped down to bare essentials (for simplicity) of the trackedItem's constructor and destructor methods:

Code: Pascal  [Select][+][-]
  1.  
  2. constructor TndFocusTrackedItem.Create(p_FocusTracker:TndFocusTracker;p_TrackedControl:TWinControl);
  3. begin
  4.   focus_tracker:=p_FocusTracker;                      // remember whom I report to: this is the owner of the list I am an item of .
  5.   tracked_control:=p_TrackedControl;                  // the tWinControl I am tracking
  6.   origiinalWindowProc:=tracked_control.WindowProc;    // backup my tracked tWinControl's original WindowProc
  7.   tracked_control.WindowProc:=@TrackedWindowProc;     // set a new WindowProc to my tracked tWinControl
  8. end;
  9.  
  10. destructor TndFocusTrackedItem.Destroy;
  11. begin
  12.   if assigned(tracked_control) then tracked_control.WindowProc:=origiinalWindowProc;     // reset original WindowProc setting
  13.   inherited;
  14. end;
  15.  

The trackedItem has only 1 more method: The actual WindowProc it has set it's tracked tWinControl to point to, and here's an example:

Code: Pascal  [Select][+][-]
  1.  
  2. procedure TndFocusTrackedItem.TrackedWindowProc(var p_Message:TLMessage);
  3. begin
  4.   if p_Message.msg=CM_ENTER then ;                    // report to my focusTracker master: a control just received focus
  5.   //
  6.   if p_Message.msg=LM_LBUTTONDOWN then ;              // report to my focusTracker master: a control got clicked on
  7.   //
  8.   if p_Message.msg=LM_KEYDOWN then ;                  // report to my focusTracker master: a Key just got pressed
  9.   //
  10.   origiinalWindowProc(p_Message);                     // this was the original WindowProc of the TWinControl I'm tracking .
  11.                                                       // before I highjacked it and set it to point to TrackedWindowProc
  12.                                                       // so make sure I call the original
  13. end;
  14.  

And that mostly outlines the premise.

As stated before, I already had the focus shifting solved.

(!) LM_LBUTTONDOWN may cover the mouse clicks, or may not, since I get both a CM_ENTER followed by a LM_LBUTTONDOWN in that order, whereas I would love to know the click before the focus shift. I may have to find a way to delay the focus shifting until I register the click??

(!) Another tricky part is to decode the TLMessage fields to get the actuall keyboard states.

I'm fairly certain there's a better, more sophisticated way of handling the current task, but alas this is as far as my knowledge can carry me.

And that mostly outlines my current demons.
Also whoever made it through the entire monster-of-a-post, you rock \o/
There's 10 kinds of people

RAW

  • Hero Member
  • *****
  • Posts: 868
Quote
@RAW you know you can even write the bestest ever "Hello Solar System" app, I'm still way above. I won the 42-annually award of last "42 and beyond" for writing the best "Hello Multiverse and all Alternative Timelines" app. It will be included in the next revision of the hitchhiker's guide. Wanna see you beating that one ;p
Seriously tho, thanks for your vote of confidence, truth be told I'm close, but I have some issues yet to solve.
:D :D :D  One day I write you a "Hello Omniverse" program that kicks your butt out of this cosmic-quanta-foam...  so you better fasten your seatbelts right now...

BTW: Thanks for sharing... maybe one day I will use this...

Quote
There's 10 kinds of people
Yeah... I guess I am one of the outsiders (outside the binary circle...).
Windows 7 Pro (x64 Sp1) & Windows XP Pro (x86 Sp3).

molly

  • Hero Member
  • *****
  • Posts: 2330
Yeah... I guess I am one of the outsiders (outside the binary circle...).
Ah, you're a qubit :D

RAW

  • Hero Member
  • *****
  • Posts: 868
Quote
Ah, you're a qubit :D
Of course I'm all the intermediate values and pretty much far and beyond everything... maybe one day I'll find out who I am... I'll send you a postcard from paradise... just in case you wanna make a little vacation...  :D
Windows 7 Pro (x64 Sp1) & Windows XP Pro (x86 Sp3).

 

TinyPortal © 2005-2018