Recent

Author Topic: Are keystrokes automatically bubbled up the parent hierarchy?  (Read 773 times)

vfclists

  • Hero Member
  • *****
  • Posts: 1146
    • HowTos Considered Harmful?
Are keystrokes automatically bubbled up the parent hierarchy?
« on: September 25, 2024, 11:14:57 am »
When you enter a key which isn't handled by an object that receives key strokes, such as a button, a button or label's hotkey or a TEdit control, will all the items in the parent hierarchy receive the keystroke whether they respond to keys or not?

For instance if a TPanel contains a TEdit which doesn't respond to a key, will the TPanel react to it if the necessary event handlers are in place?

It is it also possible for a parent component to prevent a child component from receiving the event or vice versa?

PS. I just noticed this thread - https://forum.lazarus.freepascal.org/index.php/topic,66621.0.html

Does it have a bearing on this question?
« Last Edit: September 25, 2024, 11:19:05 am by vfclists »
Lazarus 3.0/FPC 3.2.2

Thaddy

  • Hero Member
  • *****
  • Posts: 16158
  • Censorship about opinions does not belong here.
Re: Are keystrokes automatically bubbled up the parent hierarchy?
« Reply #1 on: September 25, 2024, 02:15:58 pm »
It is the other way around: the parent window dispatches to the children. So, basically, you can intercept at the parent level and prevent it going to the children.
If I smell bad code it usually is bad code and that includes my own code.

vfclists

  • Hero Member
  • *****
  • Posts: 1146
    • HowTos Considered Harmful?
Re: Are keystrokes automatically bubbled up the parent hierarchy?
« Reply #2 on: September 25, 2024, 06:23:28 pm »
It is the other way around: the parent window dispatches to the children. So, basically, you can intercept at the parent level and prevent it going to the children.

Does this mean that although a TPanel on its own doesn't respond to character entry strokes, it can block character entry strokes from getting to a TEdit contained in it?

It doesn't have any OnKeyXXXX events in the properties pane. Does it mean it would have to be cast to an ancestor component which deals with those events before being able to handle them?
Lazarus 3.0/FPC 3.2.2

rvk

  • Hero Member
  • *****
  • Posts: 6575
Re: Are keystrokes automatically bubbled up the parent hierarchy?
« Reply #3 on: September 25, 2024, 06:53:09 pm »
It's probably easier to just set TForm.KeyPreview to true and use TForm.OnKeyDown.
Set the parameter Key to 0 if you want to block it from going to any other control on the form.

Curt Carpenter

  • Hero Member
  • *****
  • Posts: 559
Re: Are keystrokes automatically bubbled up the parent hierarchy?
« Reply #4 on: September 25, 2024, 07:00:01 pm »
I did not know that.  Good to know.

Thaddy

  • Hero Member
  • *****
  • Posts: 16158
  • Censorship about opinions does not belong here.
Re: Are keystrokes automatically bubbled up the parent hierarchy?
« Reply #5 on: September 26, 2024, 07:14:53 am »
Rvk's answer is a practical way to implement my answer: You will see that the messages for the edit are first seen by the form.
If I smell bad code it usually is bad code and that includes my own code.

vfclists

  • Hero Member
  • *****
  • Posts: 1146
    • HowTos Considered Harmful?
Re: Are keystrokes automatically bubbled up the parent hierarchy?
« Reply #6 on: October 03, 2024, 03:36:46 pm »
It is the other way around: the parent window dispatches to the children. So, basically, you can intercept at the parent level and prevent it going to the children.

I assume that the parent window must be the form itself. So if the form does not intercept the key does the event handling return to the active control that is responsive to key strokes, or does the key-handling go from the outermost parent inwards to the active control?

Eg an Tedit control is placed on the innermost of a set of containers nested four deep, will each container from outside inwards be able to receive the event and block it from processing by the TEdit?

Is there some other TPanel replacement that responds to key events, or can TPanel be rejigged to accept key events?
Lazarus 3.0/FPC 3.2.2

rvk

  • Hero Member
  • *****
  • Posts: 6575
Re: Are keystrokes automatically bubbled up the parent hierarchy?
« Reply #7 on: October 15, 2024, 12:51:03 pm »
I assume that the parent window must be the form itself. So if the form does not intercept the key does the event handling return to the active control that is responsive to key strokes, or does the key-handling go from the outermost parent inwards to the active control?
The key goes to the actual component, not to the parent.
So if KeyPreview is true, it will go to the TForm first, otherwise it will arrive directly at the control.

Also see https://wiki.freepascal.org/LCL_Key_Handling

Is there some other TPanel replacement that responds to key events, or can TPanel be rejigged to accept key events?
Well, you can just expose OnKeyDown for the TPanel and use that.

Create a new project.
Put a TButton and TPanel on the form (in that order).
Double click the form (outside the TPanel) to create the TForm1.FormCreate() event.

Now paste in the following code (over the whole unit1.pas)

Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls, ExtCtrls;
  9.  
  10. type
  11.   TPanel = class(ExtCtrls.TPanel)
  12.   published
  13.     property OnKeyDown;
  14.   end;
  15.  
  16. type
  17.   { TForm1 }
  18.   TForm1 = class(TForm)
  19.     Button1: TButton;
  20.     Panel1: TPanel;
  21.     procedure FormCreate(Sender: TObject);
  22.   private
  23.  
  24.   public
  25.     procedure MyNewKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
  26.  
  27.   end;
  28.  
  29. var
  30.   Form1: TForm1;
  31.  
  32. implementation
  33.  
  34. {$R *.lfm}
  35.  
  36. { TForm1 }
  37.  
  38. procedure TForm1.FormCreate(Sender: TObject);
  39. begin
  40.   Panel1.TabStop := true;
  41.   Panel1.OnKeyDown := @Self.MyNewKeyDown;
  42. end;
  43.  
  44. procedure TForm1.MyNewKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
  45. begin
  46.   Showmessage(chr(Key));
  47. end;
  48.  
  49. end.

If you run this, the Button1 will have focus. If you press Tab then the TPanel has focus (we set the Panel1.TabStop to true in FormCreate.
If you now press a key, the MyNewKeyDown will be executed.

(Although it's not visible now that Panel1 has focus but that can be done too, of course.)

vfclists

  • Hero Member
  • *****
  • Posts: 1146
    • HowTos Considered Harmful?
Re: Are keystrokes automatically bubbled up the parent hierarchy?
« Reply #8 on: October 15, 2024, 01:00:53 pm »
I assume that the parent window must be the form itself. So if the form does not intercept the key does the event handling return to the active control that is responsive to key strokes, or does the key-handling go from the outermost parent inwards to the active control?
The key goes to the actual component, not to the parent.
So if KeyPreview is true, it will go to the TForm first, otherwise it will arrive directly at the control.

Also see https://wiki.freepascal.org/LCL_Key_Handling

Is there some other TPanel replacement that responds to key events, or can TPanel be rejigged to accept key events?
Well, you can just expose OnKeyDown for the TPanel and use that.

Create a new project.
Put a TButton and TPanel on the form (in that order).
Double click the form (outside the TPanel) to create the TForm1.FormCreate() event.

Now paste in the following code (over the whole unit1.pas)

Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls, ExtCtrls;
  9.  
  10. type
  11.   TPanel = class(ExtCtrls.TPanel)
  12.   published
  13.     property OnKeyDown;
  14.   end;
  15.  
  16. type
  17.   { TForm1 }
  18.   TForm1 = class(TForm)
  19.     Button1: TButton;
  20.     Panel1: TPanel;
  21.     procedure FormCreate(Sender: TObject);
  22.   private
  23.  
  24.   public
  25.     procedure MyNewKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
  26.  
  27.   end;
  28.  
  29. var
  30.   Form1: TForm1;
  31.  
  32. implementation
  33.  
  34. {$R *.lfm}
  35.  
  36. { TForm1 }
  37.  
  38. procedure TForm1.FormCreate(Sender: TObject);
  39. begin
  40.   Panel1.TabStop := true;
  41.   Panel1.OnKeyDown := @Self.MyNewKeyDown;
  42. end;
  43.  
  44. procedure TForm1.MyNewKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
  45. begin
  46.   Showmessage(chr(Key));
  47. end;
  48.  
  49. end.

If you run this, the Button1 will have focus. If you press Tab then the TPanel has focus (we set the Panel1.TabStop to true in FormCreate.
If you now press a key, the MyNewKeyDown will be executed.

(Although it's not visible now that Panel1 has focus but that can be done too, of course.)

Does this need to be done on every form which needs the functionality?

The only way I can see to avoid doing this in every form that needs it is to use an {$i keydownform.inc} in every unit that needs it.

In any case I subclassed the form component, but this method will be useful when I don't need it design in at runtime.
Lazarus 3.0/FPC 3.2.2

rvk

  • Hero Member
  • *****
  • Posts: 6575
Re: Are keystrokes automatically bubbled up the parent hierarchy?
« Reply #9 on: October 15, 2024, 01:11:16 pm »
Does this need to be done on every form which needs the functionality?
No, you could put this in a unit, in a procedure, and call it from FormCreate;
( for instance HackMyPanel() )

But YIKES... I now see that OnkeyDown is not private for TPanel. It's just not exposed as property. But it is still an event  :-[

So you can still do this without redeclaring the TPanel itself.
Code: Pascal  [Select][+][-]
  1.   Panel1.OnKeyDown := @Self.MyNewKeyDown;

You can still create a procedure like HackMyPanel() but then you still need to call that in FormCreate.

So... either create a complete new visible component class from TPanel where you can use it in the Object Inspector... (like you did)
or just add Panel1.OnKeyDown := @Self.MyNewKeyDown; to your FormCreate where you need it.


 

TinyPortal © 2005-2018