Recent

Author Topic: [SOLVED] TEdit key press events in customdrawn LCLWidgetType  (Read 2920 times)

lagprogramming

  • Sr. Member
  • ****
  • Posts: 406
[SOLVED] TEdit key press events in customdrawn LCLWidgetType
« on: February 08, 2023, 04:38:38 pm »
Using Linux, add a standard TEdit on a Form and fill the OnKeyPress with the following code:
Code: Pascal  [Select][+][-]
  1. procedure TForm1.Edit1KeyPress(Sender: TObject; var Key: char);
  2. begin
  3.   if key=chr(13) then form1.Caption:=form1.Caption+'>';
  4. end;

The above event is triggered when the project is running Linux using default gtk2 LCLWidgetType.
Now go to "Project/Project options..." menu, "Additions and Overrides", set the LCLWidgetType value to "customdrawn". Run the project again and you might see that the key press event is not triggered at all. I've looked at OnUTF8KeyPress event and this one is triggered for both gtk2 and customdrawn. Gtk2 triggers OnUTF8KeyPress before OnKeyPress, customdrawn triggers OnUTF8KeyPress and doesn't trigger OnKeyPress at all.

Maybe it's a bug.
« Last Edit: April 21, 2023, 12:23:13 pm by lagprogramming »

winni

  • Hero Member
  • *****
  • Posts: 3197
Re: TEdit key press events in customdrawn LCLWidgetType
« Reply #1 on: February 08, 2023, 04:54:50 pm »
Hi!

Never call inside a TForm1 procedure Form1!!!!
That might be dangerous.

Do it that way:

Code: Pascal  [Select][+][-]
  1. if key=#13 then self.Caption:=self.Caption+'>';

or even shorter:

Code: Pascal  [Select][+][-]
  1. if key=#13 then Caption:=Caption+'>';

Winni

lagprogramming

  • Sr. Member
  • ****
  • Posts: 406
Re: TEdit key press events in customdrawn LCLWidgetType
« Reply #2 on: February 09, 2023, 02:55:40 pm »
@winni For sure that's not the main problem here.
In the meantime I've done an additional test. You don't need a TEdit, TForm class has the same problem. The OnKeyPress event of a form is never triggerd, only the OnUTF8KeyPress.

lagprogramming

  • Sr. Member
  • ****
  • Posts: 406
Re: TEdit key press events in customdrawn LCLWidgetType
« Reply #3 on: April 13, 2023, 05:03:06 pm »
When running an empty form in QT, are both OnFormUTF8KeyPress and OnFormKeyPress triggered when pressing a key? If the answer is yes, is OnFormUTF8KeyPress triggered before OnFormKeyPress?
When running an empty form in win32, are both OnFormUTF8KeyPress and OnFormKeyPress triggered when pressing a key? If the answer is yes, is OnFormUTF8KeyPress triggered before OnFormKeyPress?

KodeZwerg

  • Hero Member
  • *****
  • Posts: 2269
  • Fifty shades of code.
    • Delphi & FreePascal
Re: TEdit key press events in customdrawn LCLWidgetType
« Reply #4 on: April 13, 2023, 05:27:55 pm »
1. When running an empty form in win32, are both OnFormUTF8KeyPress and OnFormKeyPress triggered when pressing a key?
2. If the answer is yes, is OnFormUTF8KeyPress triggered before OnFormKeyPress?
1. Yes (if Win64 counts too)
2. Yes (if Win64 counts too)
Lazarus 2.3.0 (rev main-2_3-3193-g3bdbedd91b) FPC 3.2.2 x86_64-win64-win32/win64
(I only can test for win64 and activated "KeyPreview" if that makes a difference.)
« Last Edit: Tomorrow at 31:76:97 xm by KodeZwerg »

zeljko

  • Hero Member
  • *****
  • Posts: 1602
    • http://wiki.lazarus.freepascal.org/User:Zeljan
Re: TEdit key press events in customdrawn LCLWidgetType
« Reply #5 on: April 14, 2023, 02:42:49 pm »
When running an empty form in QT, are both OnFormUTF8KeyPress and OnFormKeyPress triggered when pressing a key? If the answer is yes, is OnFormUTF8KeyPress triggered before OnFormKeyPress?

1.Yes
2.Yes

Applies to Qt, Qt5 and Qt6 widgetsets.

lagprogramming

  • Sr. Member
  • ****
  • Posts: 406
Re: TEdit key press events in customdrawn LCLWidgetType
« Reply #6 on: April 19, 2023, 10:59:23 am »
Follow up to commit fa8f6ffa: LCL: Make AUTF8Char a var parameter in function LCLSendUTF8KeyPress. Issue...

lcl/interfaces/customdrawn/customdrawnprivate.pas has procedure CallbackKeyChar(AWindowHandle: TCDForm; AKeyData: Word; AChar: TUTF8Char);
The following code fixes the key events:
Code: Pascal  [Select][+][-]
  1. procedure CallbackKeyChar(AWindowHandle: TCDForm; AKeyData: Word; AChar: TUTF8Char);
  2. var
  3.   lTarget: TWinControl;
  4.   lCharCode: Word;
  5. begin
  6.   lTarget := AWindowHandle.GetFocusedControl();
  7.   {$ifdef VerboseCDEvents}
  8.    DebugLn(Format('CallbackKeyChar FocusedControl=%s:%s', [lTarget.Name, lTarget.ClassName]));
  9.   {$endif}
  10.  
  11.   if lTarget = nil then Exit; // Fixes a crash
  12.   if Length(AChar) = 1 then
  13.     lCharCode := Byte(AChar[1])
  14.   else
  15.     lCharCode := 0;
  16.  
  17.   if AChar<>'' then LCLSendUTF8KeyPress(lTarget, AChar, False);
  18.   if lCharCode <> 0 then LCLSendCharEvent(lTarget, lCharCode, AKeyData, True, False, True);
  19.  
  20.   // If this is a interface control, send the message to the main LCL control too
  21.   if IsIntfControl(lTarget) then
  22.   begin
  23.     lTarget := lTarget.Parent;
  24.     if AChar<>'' then LCLSendUTF8KeyPress(lTarget, AChar, False);
  25.     if lCharCode <> 0 then LCLSendCharEvent(lTarget, lCharCode, AKeyData, True, False, True);
  26.   end;
  27. end;
But before providing a patch I have a question. lcl/include/wincontrol.inc contains the following procedures:
Code: Pascal  [Select][+][-]
  1. procedure TWinControl.KeyDown(var Key: Word; Shift: TShiftState);
  2. begin
  3.   if Assigned(FOnKeyDown) then FOnKeyDown(Self, Key, Shift);
  4.   if Key <> 0 then
  5.     DoCallKeyEventHandler(chtOnKeyDown, Key, Shift);
  6. end;
Code: Pascal  [Select][+][-]
  1. procedure TWinControl.KeyPress(var Key: char);
  2. begin
  3.   if Assigned(FOnKeyPress) then FOnKeyPress(Self, Key);
  4. end;
Code: Pascal  [Select][+][-]
  1. procedure TWinControl.KeyUp(var Key: Word; Shift : TShiftState);
  2. begin
  3.   if Assigned(FOnKeyUp) then FOnKeyUp(Self, Key, Shift);
  4. end;
Code: Pascal  [Select][+][-]
  1. procedure TWinControl.UTF8KeyPress(var UTF8Key: TUTF8Char);
  2. begin
  3.   if Assigned(FOnUTF8KeyPress) then FOnUTF8KeyPress(Self, UTF8Key);
  4. end;
   Regarding the modified procedure CallbackKeyChar, before the comment line "// If this is a interface control, send the message to the main LCL control too" the value of AChar can be modified at line "if AChar<>'' then LCLSendUTF8KeyPress(lTarget, AChar, False);". Also the value of lCharCode can be modified too at line "if lCharCode <> 0 then LCLSendCharEvent(lTarget, lCharCode, AKeyData, True, False, True);".
   After the comment line the message is sent to the main LCL control only if AChar<>'' for LCLSendUTF8KeyPress and only if lCharCode <> 0 for LCLSendCharEvent. Is this the right behavior? This means that you can cancel sending the messages further to the LCL controls by modifying in one of the OnKey* events the value of AChar to '' or the value of lCharCode to 0. Look at the content of procedure TWinControl.KeyDown(var Key: Word; Shift: TShiftState); as it has that "if Key <> 0 then DoCallKeyEventHandler(chtOnKeyDown, Key, Shift);" line. Also, function TWinControl.DoKeyPress(var Message : TLMKey): Boolean; has an "if Char(CharCode) = #0 then Exit;" line.

lagprogramming

  • Sr. Member
  • ****
  • Posts: 406
Re: TEdit key press events in customdrawn LCLWidgetType
« Reply #7 on: April 21, 2023, 09:14:02 am »
The following patch fixes the bug.
Code: Pascal  [Select][+][-]
  1. diff --git a/lcl/interfaces/customdrawn/customdrawnprivate.pas b/lcl/interfaces/customdrawn/customdrawnprivate.pas
  2. index bcf33be060..7a95139583 100644
  3. --- a/lcl/interfaces/customdrawn/customdrawnprivate.pas
  4. +++ b/lcl/interfaces/customdrawn/customdrawnprivate.pas
  5. @@ -288,7 +288,7 @@ end;
  6.  procedure CallbackKeyChar(AWindowHandle: TCDForm; AKeyData: Word; AChar: TUTF8Char);
  7.  var
  8.    lTarget: TWinControl;
  9. -  lCharCode: Word = 0;
  10. +  lCharCode: Word;
  11.  begin
  12.    lTarget := AWindowHandle.GetFocusedControl();
  13.    {$ifdef VerboseCDEvents}
  14. @@ -296,17 +296,20 @@ begin
  15.    {$endif}
  16.  
  17.    if lTarget = nil then Exit; // Fixes a crash
  18. -  if Length(AChar) = 1 then lCharCode := Word(AChar[1]);
  19. +  if Length(AChar) = 1 then
  20. +    lCharCode := Byte(AChar[1])
  21. +  else
  22. +    lCharCode:=0;
  23.  
  24. -//  if lCharCode <> 0 then LCLSendCharEvent(lTarget, lCharCode, AKeyData, True, False, True);
  25. -  LCLSendUTF8KeyPress(lTarget, AChar, False);
  26. +  if AChar<>'' then LCLSendUTF8KeyPress(lTarget, AChar, False);
  27. +  if lCharCode <> 0 then LCLSendCharEvent(lTarget, lCharCode, AKeyData, True, False, True);
  28.  
  29.    // If this is a interface control, send the message to the main LCL control too
  30.    if IsIntfControl(lTarget) then
  31.    begin
  32.      lTarget := lTarget.Parent;
  33. -//    if lCharCode <> 0 then LCLSendCharEvent(lTarget, lCharCode, AKeyData, True, False, True);
  34. -    LCLSendUTF8KeyPress(lTarget, AChar, False);
  35. +    if AChar<>'' then LCLSendUTF8KeyPress(lTarget, AChar, False);
  36. +    if lCharCode <> 0 then LCLSendCharEvent(lTarget, lCharCode, AKeyData, True, False, True);
  37.    end;
  38.  end;

AlexTP

  • Hero Member
  • *****
  • Posts: 2421
    • UVviewsoft

Josh

  • Hero Member
  • *****
  • Posts: 1305
Re: [SOLVED] TEdit key press events in customdrawn LCLWidgetType
« Reply #9 on: April 21, 2023, 01:21:09 pm »
Hi

Am i missing something, maybe its optimization.

You remove =0 in  lCharCode: Word = 0;

then set it to zero in
 if Length(AChar) = 1 then
    lCharCode := Byte(AChar[1])
  else
    lCharCode:=0;

Also why casting a word to byte?
« Last Edit: April 21, 2023, 01:22:53 pm by Josh »
The best way to get accurate information on the forum is to post something wrong and wait for corrections.

AlexTP

  • Hero Member
  • *****
  • Posts: 2421
    • UVviewsoft
Re: [SOLVED] TEdit key press events in customdrawn LCLWidgetType
« Reply #10 on: April 21, 2023, 05:41:02 pm »
>Also why casting a word to byte?

E.g. russian char will be Word with high bits set, and you cast it to Byte?

dsiders

  • Hero Member
  • *****
  • Posts: 1112
Re: [SOLVED] TEdit key press events in customdrawn LCLWidgetType
« Reply #11 on: April 21, 2023, 07:04:56 pm »
>Also why casting a word to byte?

E.g. russian char will be Word with high bits set, and you cast it to Byte?

Let's summarize...

Patches on forum instead of bug tracker (check).
No test case (check).
Submitted as a patch to bug tracker (check).
Patch applied without testing (check).

Given all of that... what could possibly go wrong?
Preview Lazarus 3.99 documentation at: https://dsiders.gitlab.io/lazdocsnext

lagprogramming

  • Sr. Member
  • ****
  • Posts: 406
Re: [SOLVED] TEdit key press events in customdrawn LCLWidgetType
« Reply #12 on: April 22, 2023, 09:45:06 am »
Code: Pascal  [Select][+][-]
  1. procedure TForm1.Button1Click(Sender: TObject);
  2. //add lcltype to uses
  3. var
  4.   AChar: TUTF8Char;
  5. begin
  6.   AChar:='Њ';//Cyrillic capital letter nje
  7.   Application.MainForm.Caption:=inttostr(length(AChar));
  8.   //Now the caption of the form will be "2" IT'S 2, NOT 1!
  9.   AChar:='x';
  10.   Application.MainForm.Caption:=Application.MainForm.Caption+inttostr(length(AChar));
  11.   //Now the caption of the form will become "21"
  12.   //Notice that the length is 2 the first time and the second time the length is 1
  13. end;
  Regarding the patch, I don't see a new bug inserted in the code by modifying "if Length(AChar) = 1 then lCharCode := Word(AChar[1])" with "if Length(AChar) = 1 then lCharCode := Byte(AChar[1])". Knowing that the significant data of AChar is one byte size there is no need to try to read two bytes(word type).
https://gitlab.com/freepascal.org/lazarus/lazarus/-/issues/40221 can be closed.

 

TinyPortal © 2005-2018