Recent

Author Topic: Problem with defining onkeydown on on the fly form.  (Read 22157 times)

wpflum

  • Sr. Member
  • ****
  • Posts: 287
Problem with defining onkeydown on on the fly form.
« on: November 22, 2010, 05:04:08 pm »
I'm making a procedure that I can call like showmessage to produce a popup that mimics a current text based application.  I have it to the point where I can assign the onkeypress from the created form to a procedure in the same code but I don't seem to be receiving the key press. Here is the code for the procedure:


unit Popups;

{$mode objfpc}{$H+}

interface

uses
Classes, SysUtils,Dialogs,Forms,Variables,Controls,StdCtrls,Graphics,ExtCtrls,LCLType;


procedure ShowWarning(DisplayText:string);
procedure ShowWarningFormPaint(ASelf:TForm;Sender :TObject);
Procedure SetColors(Sender:Tform);
procedure OnTimer(ASelf:TForm;Sender :TObject);
procedure WarningKeypress(Sender: TObject; var Key: Word; Shift: TShiftState);


implementation


Procedure SetColors(Sender:Tform);
var
I:integer;
Begin
i :=  Sender.ControlCount;
for i := 0 to Sender.ControlCount - 1 do
begin
if Sender.Controls is TLabel then
begin Sender.Controls.Font.Color := BoldTextColor end;
if Sender.Controls is TLabel then
begin Sender.Controls.Color := BoldTextBackColor end;
if Sender.Controls is TEdit then
begin Sender.Controls.Font.Color := UnderLineTextColor end;
if Sender.Controls is TEdit then
begin Sender.Controls.Color := UnderLineTextBackColor end;
if Sender.Controls is TShape then
begin (Sender.Controls as TShape).Brush.Color  := NormalTextColor end;
if Sender.Controls is TStaticText then
begin Sender.Controls.Font.Color := BoldTextColor end;
if Sender.Controls is TStaticText then
begin Sender.Controls.Color := BoldTextColor end;
end;
Sender.Color := NormalTextBackColor;

end;



procedure ShowWarning(DisplayText:string);
var
Dlg : TForm;
MyMethod:TMethod;
TimerMethod:TMethod;
WarningMethod:TMethod;
OkBtn: TButton;
Title : TLabel;
WarningTxt: TLabel;
WarningOK: TLabel;
Timer1:TTimer;
loop,txtwidth,txtheight:integer;
begin
Dlg := TForm.Create(application);
Title := TLabel.Create(Dlg);
WarningTxt := TLabel.Create(Dlg);
WarningOK := TLabel.Create(Dlg);
Timer1 := TTimer.Create(Dlg);
MyMethod.Code :=@ShowWarningFormPaint;
MyMethod.Data := Dlg;
WarningMethod.Code := @WarningKeypress;
WarningMethod.Data := Dlg;

TimerMethod.Code :=@OnTimer;
TimerMethod.Data := Dlg;


with Dlg do
begin
     OnPaint :=TNotifyEvent(MyMethod);
     OnKeyDown := TKeyEvent(WarningMethod);
     Position := poScreenCenter;
     Caption := '';
     //width := 425;
     width := (dlg.Canvas.TextWidth(DisplayText))*2;
     height := 150;
     Color := NormalTextBackColor;
     Font.Color := NormalTextColor;
     Font.Height:= 17;
     BorderStyle := bsnone;
     Canvas.Refresh ;
end;

with Timer1 do
begin
     OnTimer :=TNotifyEvent(TimerMethod);
end;

with Title do
begin
     Parent := Dlg;
     txtwidth := Canvas.TextWidth(' ERROR ');
     top := TForm(Owner).Top;
     Left := TForm(Owner).Left + round((dlg.Width / 2)- (txtwidth / 2));
     Caption := ' Error ';
     WordWrap := false;
     Width:= 60;
     height := 40;
     Visible := true;
end;
with WarningTxt do
begin
     Parent := Dlg;
     txtwidth := Canvas.TextWidth(DisplayText);
     top := TForm(Owner).Top+50;
     Left := TForm(Owner).Left + round((dlg.Width / 2)- (txtwidth / 2));
     Caption := DisplayText;
     WordWrap := false;
     //Width:= 50;
     height := 40;
     Visible := true;
end;
with WarningOK do
begin
     Parent := Dlg;
     txtwidth := Canvas.TextWidth('OK');
     top := TForm(Owner).Top+100;
     Left := TForm(Owner).Left + round((dlg.Width / 2)- (txtwidth / 2));
     Caption := 'OK';
     WordWrap := false;
     //Width:= 50;
     height := 40;
     Visible := true;
     Name:='WarningOK';

end;
     SetColors(Dlg);
     Dlg.ShowModal;
     Dlg.Free;
     Title.Free;
     WarningTxt.Free ;
     WarningOK.Free;
end;
procedure ShowWarningFormPaint(ASelf:TForm;Sender :TObject);
var
BoxHeight,BoxWidth,MenuHeight,MenuWidth,Margin,FrameOffset : integer;
begin
Margin := 10;
FrameOffset := 2;
BoxHeight := ASelf.Height - Margin;
BoxWidth := ASelf.Width - Margin;

ASelf.Canvas.Pen.Color := clwhite;
ASelf.Canvas.Pen.Width :=1;
ASelf.Canvas.Line(Margin + FrameOffset,Margin + FrameOffset,BoxWidth-FrameOffset,Margin+FrameOffset);
ASelf.Canvas.Pen.Color := cldkgray;
ASelf.Canvas.LineTo(BoxWidth-FrameOffset,BoxHeight-FrameOffset);
ASelf.Canvas.LineTo(Margin+FrameOffset,BoxHeight-FrameOffset);
ASelf.Canvas.Pen.Color := clwhite;
ASelf.Canvas.LineTo(Margin + FrameOffset,Margin + FrameOffset);
ASelf.Canvas.Pen.Color := cldkgray;
ASelf.Canvas.Line(Margin ,Margin,BoxWidth,Margin);
ASelf.Canvas.Pen.Color := clwhite;
ASelf.Canvas.LineTo(BoxWidth,BoxHeight);
ASelf.Canvas.LineTo(Margin,BoxHeight);
ASelf.Canvas.Pen.Color := cldkgray;
ASelf.Canvas.LineTo(Margin,Margin);
end;

procedure OnTimer(ASelf:TForm;Sender :TObject);
begin
 if (aself.FindComponent('WarningOK') as TLabel).Visible  then
    begin;
    (aself.FindComponent('WarningOK') as TLabel).Visible := false;
    end
    else
    begin
    (aself.FindComponent('WarningOK') as TLabel).Visible := true;
    end;
end;

procedure WarningKeypress(Sender: TObject; var Key: Word; Shift: TShiftState);
var
MyMessage:String;
begin
if key = VK_END  then showmessage('END Pressed');
showmessage('Got Here!');
end;

end.                                   
---------------------------------------------------------------------------------------------

I get the onkeydown event but the key variable does not seem to be the same as a normal onkeydown event.  I pulled the entire onkeydown header off a form to make sure I was using the variables in the same order in case that was messing things up but while I get a 'key' that is a word It doesn't seem to correspond to things like VK_END that I need to compare it against.

Ideas???

wpflum

  • Sr. Member
  • ****
  • Posts: 287
Re: Problem with defining onkeydown on on the fly form.
« Reply #1 on: November 22, 2010, 05:16:28 pm »
Here's a bit more info.  I might be getting the right key word but even though I've enabled keypreview on the 'on the fly' form I'm not getting the event happening when I press any control keys like 'end' or the cursor keys.  Any other key hits the show message line in the event handler. 


Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 9877
  • Debugger - SynEdit - and more
    • wiki
Re: Problem with defining onkeydown on on the fly form.
« Reply #2 on: November 22, 2010, 05:38:08 pm »
You forgot there is a hidden param

procedure WarningKeypress(Sender: TObject; var Key: Word; Shift: TShiftState);

is a plain procedure

but expected is a "procedure of object"
procedure TSomeClass.WarningKeypress(Sender: TObject; var Key: Word; Shift: TShiftState);

the 2nd one has an extra param (Self is in the list of params, even so you do not see it)

And I do not know if there are any guarantees about the position or count of extra params....

You shouldnt use the whole TMethod typecasting stuff. That is deep internals.

Create an object as receiver of the event .

TFooClass = class
  FDlg: TObject;
  procedure WarningKeypress(Sender: TObject; var Key: Word; Shift: TShiftState);
  property DLg: TObject read FDLg write FDlg;
end

foo := TFooClass.Create
foo.Dlg := Dlg;

xxx.OnKeyDown := @foo.WarningKeyPressed;

wpflum

  • Sr. Member
  • ****
  • Posts: 287
Re: Problem with defining onkeydown on on the fly form.
« Reply #3 on: November 22, 2010, 05:44:46 pm »
You forgot there is a hidden param

procedure WarningKeypress(Sender: TObject; var Key: Word; Shift: TShiftState);

is a plain procedure

but expected is a "procedure of object"
procedure TSomeClass.WarningKeypress(Sender: TObject; var Key: Word; Shift: TShiftState);

the 2nd one has an extra param (Self is in the list of params, even so you do not see it)

And I do not know if there are any guarantees about the position or count of extra params....

You shouldnt use the whole TMethod typecasting stuff. That is deep internals.

Create an object as receiver of the event .

TFooClass = class
  FDlg: TObject;
  procedure WarningKeypress(Sender: TObject; var Key: Word; Shift: TShiftState);
  property DLg: TObject read FDLg write FDlg;
end

foo := TFooClass.Create
foo.Dlg := Dlg;

xxx.OnKeyDown := @foo.WarningKeyPressed;


Did you miss my second post or am I just missing your point??   The actual onkeydown seems to be working but it is not being activated by control keys.  Will what you suggested solve that issue???  One more question, you said I shouldn't use the TMethod typecasting because it is deep internals, why is that a problem???

Bart

  • Hero Member
  • *****
  • Posts: 5290
    • Bart en Mariska's Webstek
Re: Problem with defining onkeydown on on the fly form.
« Reply #4 on: November 22, 2010, 09:59:33 pm »
Aren't you overcomplicating things?

If your WarningKeypress procedure is a method of a class code like this works like a charm:

Code: [Select]
procedure Tform1.WarningKeypress(Sender: TObject; var Key: Word; Shift: TShiftState);
//KeyDown would be a better name
begin
  if Key = VK_END then form1.memo1.lines.add('VK_END');
end;

procedure TForm1.Button4Click(Sender: TObject);
var f: tform;
    e: tedit;
begin
  f := tform.create(nil);
  try
    e := tedit.create(f);
    e.parent := f;
    f.keypreview := true;
    f.OnKeyDown:= @WarningKeypress;
    f.ShowModal;
  finally
    f.free;
  end;
end;

I can see VK_END if I press the End key when f has focus.

Also I do not really understand why you have to construct the whole form in code.
Can't you design it in in the IDE as a separate form?

If the form needs 2 or more different looks, depending on the context of the calling procedure, you can maybe use frames for that?

Bart

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 9877
  • Debugger - SynEdit - and more
    • wiki
Re: Problem with defining onkeydown on on the fly form.
« Reply #5 on: November 22, 2010, 11:13:40 pm »
You forgot there is a hidden param

procedure WarningKeypress(Sender: TObject; var Key: Word; Shift: TShiftState);

is a plain procedure

but expected is a "procedure of object"
procedure TSomeClass.WarningKeypress(Sender: TObject; var Key: Word; Shift: TShiftState);

the 2nd one has an extra param (Self is in the list of params, even so you do not see it)

And I do not know if there are any guarantees about the position or count of extra params....

You shouldnt use the whole TMethod typecasting stuff. That is deep internals.


Did you miss my second post or am I just missing your point??   The actual onkeydown seems to be working but it is not being activated by control keys.  Will what you suggested solve that issue???  One more question, you said I shouldn't use the TMethod typecasting because it is deep internals, why is that a problem???

Guess yes, I'll have to admit, I missed the 2nd part of your post... I take the blame, but I will still reply with an advice: If you post that much code, zip it,and attach it, and only put a few lines in your code.  It makes it easier for others... (just my opinion / otherwise nothing wrong with putting all source in the code)


Look at
http://bugs.freepascal.org/view.php?id=17775

Are you on gtk2? (Maybe you said, but I am too lazy to scan all your msg again)

About TMethod. Nothing wrong with it. So for once you add a lot of typecasting, overriding the build-in type checking. So if you happen to make an error, you will not get a warning from the compiler.

"deep internals" is execerated, true. But:
"And I do not know if there are any guarantees about the position or count of extra params...."
is something you should check if it is documented. otherwhise one day fpc could apply optimizations that make a procedure and a method totally incompatible....

wpflum

  • Sr. Member
  • ****
  • Posts: 287
Re: Problem with defining onkeydown on on the fly form.
« Reply #6 on: November 23, 2010, 01:34:54 pm »
Aren't you overcomplicating things?

If your WarningKeypress procedure is a method of a class code like this works like a charm:

Code: [Select]
procedure Tform1.WarningKeypress(Sender: TObject; var Key: Word; Shift: TShiftState);
//KeyDown would be a better name
begin
  if Key = VK_END then form1.memo1.lines.add('VK_END');
end;

procedure TForm1.Button4Click(Sender: TObject);
var f: tform;
    e: tedit;
begin
  f := tform.create(nil);
  try
    e := tedit.create(f);
    e.parent := f;
    f.keypreview := true;
    f.OnKeyDown:= @WarningKeypress;
    f.ShowModal;
  finally
    f.free;
  end;
end;

I can see VK_END if I press the End key when f has focus.

Also I do not really understand why you have to construct the whole form in code.
Can't you design it in in the IDE as a separate form?

If the form needs 2 or more different looks, depending on the context of the calling procedure, you can maybe use frames for that?

Bart

I prefer all code when I'm making something that will be used in other programs rather than having multiple files to copy across and I'm used to 'old school' programming so I'm more comfortable with it this way.

Exactly how does your code work??  I tried to copy it into an empty project and got some errors so I figured I did something wrong. 


wpflum

  • Sr. Member
  • ****
  • Posts: 287
Re: Problem with defining onkeydown on on the fly form.
« Reply #7 on: November 23, 2010, 01:43:27 pm »
You forgot there is a hidden param

procedure WarningKeypress(Sender: TObject; var Key: Word; Shift: TShiftState);

is a plain procedure

but expected is a "procedure of object"
procedure TSomeClass.WarningKeypress(Sender: TObject; var Key: Word; Shift: TShiftState);

the 2nd one has an extra param (Self is in the list of params, even so you do not see it)

And I do not know if there are any guarantees about the position or count of extra params....

You shouldnt use the whole TMethod typecasting stuff. That is deep internals.


Did you miss my second post or am I just missing your point??   The actual onkeydown seems to be working but it is not being activated by control keys.  Will what you suggested solve that issue???  One more question, you said I shouldn't use the TMethod typecasting because it is deep internals, why is that a problem???

Guess yes, I'll have to admit, I missed the 2nd part of your post... I take the blame, but I will still reply with an advice: If you post that much code, zip it,and attach it, and only put a few lines in your code.  It makes it easier for others... (just my opinion / otherwise nothing wrong with putting all source in the code)


Look at
http://bugs.freepascal.org/view.php?id=17775

Are you on gtk2? (Maybe you said, but I am too lazy to scan all your msg again)

About TMethod. Nothing wrong with it. So for once you add a lot of typecasting, overriding the build-in type checking. So if you happen to make an error, you will not get a warning from the compiler.

"deep internals" is execerated, true. But:
"And I do not know if there are any guarantees about the position or count of extra params...."
is something you should check if it is documented. otherwhise one day fpc could apply optimizations that make a procedure and a method totally incompatible....


Actually I did cut out a bunch of lines, I guess I could have cut more so next time I will either zip or REALLY hack the code to pieces.

I'm not sure what Ubuntu is running, how do I check if I'm using gtk2???  I'm going to drop it onto a windows machine and see if it runs correctly there.  Wouldn't be a huge problem since the only Linux box this would run on is mine since the target PCs are all Windows.  I just find it easier to build this on my Linux box, work out the bugs and then port it to a windows machine for final tweaking and compilation.

So you are saying that the TMethod type is something the system uses and that it might change in the future or did I get that wrong??  I don't mind changing to something else, this was the first thing I found that seemed to work, but I'm not sure exactly what I should be doing.  I prefer to stay with an on-the-fly created form rather than a prebuilt form just as a personal programming preference.

wpflum

  • Sr. Member
  • ****
  • Posts: 287
Re: Problem with defining onkeydown on on the fly form.
« Reply #8 on: November 23, 2010, 01:51:34 pm »
Well, it must not be related to the gtk2 issue.  I tried it on a windows machine and got the same results, I can press any key but a control key, like del or cursor keys, and get the popup message but the end or cursor keys are just ignored.

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 9877
  • Debugger - SynEdit - and more
    • wiki
Re: Problem with defining onkeydown on on the fly form.
« Reply #9 on: November 23, 2010, 01:55:13 pm »
Ubuntu: probably gtk2
could be QT, or if older could be gtk1

TMethod:
should not change. But it requires you do do typecasting. so the compiler will not warn you.
Using TMethod is in itself not an issue. But very rarely needed. Probably not needed in your case (from what I can see ...)...

Which is actually what you do: your procedure is not a method, so normally the compiler would not let you assign it to the OnKeyDown/Press property.

----
Because (part2 of what I was saying):

procedure WarningKeypress(Sender: TObject; var Key: Word; Shift: TShiftState);

procedure TSomeClass.WarningKeypress(Sender: TObject; var Key: Word; Shift: TShiftState);

are taking a *different* list of parameters. Yes the params you see in the pascal code declaration are identical.
If you debug it you will see that the 2nd one (the method) has one more argument than the first one.

At current fpc just passes the extra arg at the end of the list. So it works.

But I do not know if that is guaranteed (or even if that currently applies to all platforms/cpu).

If it is not guaranteed, then in future it can change.

If it changes your code still compiles.
Because your type-cast tells fpc, that it should ignore the issue.
But in that case,  at runtime it would crash.

wpflum

  • Sr. Member
  • ****
  • Posts: 287
Re: Problem with defining onkeydown on on the fly form.
« Reply #10 on: November 23, 2010, 02:10:25 pm »
Ok, how do I make a 'method' out of my procedure so that I won't have to do it the way I currently am??

I think I'm at an 'AAAHAAAA' moment in that I think currently I'm missing some basic info on methods and procedures that I really need to know.

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 9877
  • Debugger - SynEdit - and more
    • wiki
Re: Problem with defining onkeydown on on the fly form.
« Reply #11 on: November 23, 2010, 02:19:57 pm »
I tired the following on win and it worked fine:
Code: [Select]

  { TEventsHolder }

  TEventsHolder = class
  public
    procedure FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
  end;

{ TEventsHolder }

procedure TEventsHolder.FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
begin
  // do not access self, it is nil
  // you can access sender, which is the form
  MessageDlg('x','x',mtInformation, [mbOK],0);
end;

procedure SetEvent(AForm: TForm);
begin
  AForm.KeyPreview := True;
  AForm.OnKeyDown := @TEventsHolder(nil).FormKeyDown;
end;


Note that I use a typecast myself (TEventsHolder(nil))

This simply means I do not have to create an instance of the object, and do not need to care about destroying it later.

So that could easily be done in the Initialization/Finalization sections.
(as long as finalization ensures there will be no further calls to the object once destroyed...., but OnKeyPress should no longer happen during finalization..

Anyway
TEventsHolder(nil)

means essentially making it like a class method...
It does mean that I can not access the object in the method (because the object doesn't exist / is nil)


Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 9877
  • Debugger - SynEdit - and more
    • wiki
Re: Problem with defining onkeydown on on the fly form.
« Reply #12 on: November 23, 2010, 02:34:16 pm »
The more advanced version, with a true object, could hold extra data:
Code: [Select]
{ TEventsHolder }

  TEventsHolder = class
  public
    Flag: Boolean;
    Data:Integer;
    procedure FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
  end;

{ TEventsHolder }

procedure TEventsHolder.FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
begin
  // you can access sender, which is the form
  if flag then
  MessageDlg('x','x'+inttostr(data),mtInformation, [mbYes],0)
  else
  MessageDlg('x','x'+inttostr(data),mtInformation, [mbOK],0);
end;

procedure SetEvent(AForm: TForm; Flag: Boolean; Data: Integer);
var O: TEventsHolder
begin
  O:= TEventsHolder.create;
  o.Flag := Flag;
  o.Data := Data;
  AForm.KeyPreview := True;
  AForm.OnKeyDown := @O.FormKeyDown;
end;


But you must free the object at some point.

Like using FreeNotification from the form.... (if the form is destroyed)

wpflum

  • Sr. Member
  • ****
  • Posts: 287
Re: Problem with defining onkeydown on on the fly form.
« Reply #13 on: November 23, 2010, 02:55:20 pm »
I told you it was an AHA moment :)  I'm starting to understand a bit more of this and was able to get both the repaint and the keypress routines functioning this way.  I tried to add the ontimer routine to the TEVentsHolder but I'm getting an error when it runs now.  I'm pretty sure the I'm getting a timer event as the error (RunError(219)) does not happen immediately when run, it pauses just like the original code did before the first time it flashes the 'OK' text.

In case it isn't obvious I'm creating a TTimer on the form and setting the ontimer event to hide and unhide a TLabel to get a flashing effect.

wpflum

  • Sr. Member
  • ****
  • Posts: 287
Re: Problem with defining onkeydown on on the fly form.
« Reply #14 on: November 23, 2010, 03:05:09 pm »
Ok, I think I figured out why the ontimer doesn't work, it does work it just doesn't get a Sender Variable with the form in it.  I dug into the declaration of OnTimer in the ExtCtrls code and I see there is nothing sent with the OnTimer event (I think).  How can I get it to include the Sender object like the keydown and repaint events?

 

TinyPortal © 2005-2018