how can I link Button's OnClick event to my own OnMyButtonClick? I need my own OnClick event that "reacts" to Button's OnClick.
You would need to create a destroy event cleaning up the TLabel and TButton but that's better than to let the caller do this.He only needs to explicitly destroy the components if the owner is nil, otherwise the owner will auto destroy them during its destruction.
Ah, yes. Missed that.You would need to create a destroy event cleaning up the TLabel and TButton but that's better than to let the caller do this.He only needs to explicitly destroy the components if the owner is nil, otherwise the owner will auto destroy them during its destruction.
@Handoko thanks for the links, great article but I think it's a little bit overwhelming for me, at least for now :D
I was hoping for solution like Btn.OnClick:=@MyButtonClick; and from your code I see it's doable this way. Thanks a lot!
I will take care of the "freeing" issue.
Thanks everyone, it works as I needed.
And what would change if I used TImage instead of TPanel? TImage can't be "a parent", right? I'm trying to use TImage but I can't seem to have my components visible (since I can't assign a parent).
I need something that I can draw with BGRABitmap to have a transparent background. I used TBCPanel on Linux (QT5) but it's not transparent on Windows (for some reason) and I can't figure out how to draw on TPanel with BGRABitmap.
@HeavyUser thanks for your answer, this is already solved but does you solution have some advantages over Handoko's solution (Btn.OnClick:=@MyButtonClick)?I made the event public so it can be assigned from outside the control it self that's it.
However, for learning purposes and better understanding... is it possible to replace TPanel with TImage?
It's be cool to get rid of TPanel since it's useless but I don't know how to use TImage as a parent for the other components.It doesn't have to be a TPanel from which you inherit from.
In contrast to TGraphicControl, a TCustomControl can accept keyboard input (get the Focus) and can have child controls.
Is it visual?But... the question is... do you need to paint your component? Because you already have a TImage, TLabel and TButtonm, which paint themselves, you don't actually need to paint anything else, do you??? So TWinControl would be sufficient in that case, I think :D
- If no, use TComponent.
- if yes, does it need its own HWND (input focus, window messages, etc)?
- If no, use TGraphicControl.
- If yes, does it need to custom paint itself?
- if yes, use TCustomControl.
- if no, use TWinControl.
And afterwards you are going to do self.stretch and some other changes to self.good point. I took .Stretch out of the procedure. I set this property after .Create, but it's still 100% CPU.
This will trigger another Paint in which case you get a loop.
Why are you calling inherited in Paint?That's a good question. I removed it but now, for some reason, the background image doesn't load, I only have the text painted.
You can also just use TCanvas as base and paint everything on that.I don't really need TImage. I will give TCanvas a try.
(Or do you really jeed TImage?)
It's not only the stretch line.QuoteAnd afterwards you are going to do self.stretch and some other changes to self.good point. I took .Stretch out of the procedure. I set this property after .Create, but it's still 100% CPU.
This will trigger another Paint in which case you get a loop.
sq_pas.pas(555,56) Error: Incompatible types: got "<address of procedure(TObject;TShiftState;LongInt;LongInt) of object;Register>" expected "<procedure variable type of procedure(TObject;TShiftState;LongInt;LongInt) of object;Register>"
You are using a direct class (TChip) event.
MyChip.OnMouseMove:=@TChip.MouseMove; //I'm getting the error here
You probably meant to do this:yes, of course... silly me :o
MyChip.OnMouseMove:=@MyChip.MouseMove; // <== note the MyChip instance
Project sq raised exception class 'External: SIGSEGV'.
At address 465960
it's giving me sq_pas.pas(444,88) Error: identifier idents no member "MouseMove"Maybe MouseMove is not defined as public function.
it wasn't public... now I canActually, for a component, I would prefer to remove the OnMouseMove entirely and implement my own MouseMove.without any issues but I actually prefer the other way, MouseMove assigned right in constructor.
Game.sack[Length(Game.sack)-1].OnMouseMove:=@Game.sack[Length(Game.sack)-1].MouseMove;
Anyway after making it public, I was still getting SIGSEGV. I undid all changes related to the class, events etc and it didn't stop giving me SIGSEGV ...If you debug things, you should be able to pinpoint the exact line on which the SIGSEGV occurs.
Actually, for a component, I would prefer to remove the OnMouseMove entirely and implement my own MouseMove.That was actually my first approach but I couldn't figure out if it's in protected, public or private. I dropped a TImage on the form and peeked on how it's done and I got
If you debug things, you should be able to pinpoint the exact line on which the SIGSEGV occurs.How could I do it? I tried to set a breakpoint on MainForm.Create and go step by step but SIGSEGV happens before that.
But now it works so that's for next timeyes, so far everything works as intended. You've been really helpful, thank you!
Just for learning purposes and better understanding, why is it different?The one with sender is an OnSomethingEvent. It's called by an action and therefore has a Sender parameter. You can assign your own events to it (like you normally do with OnSomething).
If I use OnMouseEvent on desing time, it'sand your solution is
MouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);I mean the number of parameters.
MouseMove(Shift: TShiftState; X, Y: Integer);
If an error occurs in your component, it's best to test this by creating an instance in code (for example a empty testform and create it in the formcreate) and putting a breakpoint on the creation line and stepping into it during debugging.QuoteIf you debug things, you should be able to pinpoint the exact line on which the SIGSEGV occurs.How could I do it? I tried to set a breakpoint on MainForm.Create and go step by step but SIGSEGV happens before that.
cib_pas.pas(83,29) Error: Incompatible types: got "<procedure variable type of procedure of object;Register>" expected "<procedure variable type of procedure(TObject;TShiftState;TPoint;var Boolean) of object;Register>"
If you have the unit in DELPHI mode, you can not use the @ in front.No, that's not it. The problem is that you need a procedure like
Otherwise, you need to use the @ in fpc mode.
Since I'm inheriting from other class, as @rvk mention in previous post, I'd rather override some procedure like MouseWheelown (as I did with Click and Paint) but I have no clue how to approach it. Any piece of advice?Ok, I'm behind my computer... so...
but will get back to you with a more complete example and explanation how to find the correct procedure name to override.I'd appreciate it a lot!
Put your cursor on the text .OnMouseWheelThat's how I was actually doing this but I'd just click on OnMouseWheel, I wouldn't go further to TMouseWheelEvent. That's why I couldn't find the parameters needed for any procedure. Now I tried and I could find the procedure and parameters it takes.
Press Alt + Arrow up
If you are at a line with "property OnMouseWheel"
Press Alt + Arrow up again
Now if you are at the line with "property OnMouseWheel: TMouseWheelEvent"
you are at the correct place.
Put the cursor on TMouseWheelEvent
Press Alt + Arrow up
Now you see the definition of the Wheel event
And yes, sometimes it is Do instead of On and sometimes its without the On.Indeed I found functions and procedures with "Do" and some without. I even found DoMouseWheel while desperately searching the source and I tried to override it the way you showed me but first, I did it without parameters and second ,I did it as procedure, not a function so I ended up with "There is no method in an ancestor class to be overridden".
So you could find Paint as main procedure but sometimes it is DoPaint.
Just for the sake of better understanding... why Result := inherited; and not just inherited? Is it because it's a function, not a procedure?Yes. Normally such event report if the action (wheel in this case) is handled.
Maybe I could pick your brain to solve another issue I recently faced, it's also class-related (I think) and also resulting in SIGSEGV.Without seeing more of the code there is nothing to say for certain.
Is Game at the point of DrawMe a valid pointer?I think so, Game is created in onClick event:
Is Game.Pic filled with something valid at the moment of DrawMe.Game.Pic is created within it constructor.
Did you try the writeln also in that DrawMe to see if Game.Pic was correctly filled?touching Game.Pic from DrawMe alsways end up in SIGSEGV ;/
Otherwise show more codeok, let me put everything in order and write comments to see what's missing.
I'm not sure what (T)Puzzles is voor why didn't you make/pass Game (or Self) as owner of TPuzzle?(T)Puzzles is the main form so...
You are calling DrawMe in the create of TGame.ok, I think I get this part. As far sa I understand it, it's what I mentioned as possible culprit (point [6])?
But during that point the global Game variable/instance isn't assigned its value yet. That comes after the return of constructor TGame.Create.
So at the point of DrawMe the variable Game is still nil.
(You could have checked that with a simple "if not assigned(Game) then etc or etc.").
you want to pass the TGame variable (Self in this case) along to TPuzzle via its constructor and save it there as it's owner. Via that owner you can get to the Pic.If I got it right, you're saying that if TPuzzle.Owner=TGame, I'll be able to "see" TGame.Pic from TPuzzle.DrawMe ?
I can't create an example at the momentTake it easy, it's not an emergency so whenever you have time, I'd appreciate any example.
I'm affraid I'm not even able to answer the second part of the question. As I needed Parent to be Puzzles so I think I jest kept right on and mindlessly did the same with the Owner :-[Why does the form need to be the parent? I thought you were drawing on TGame, not on parent.
If I got it right, you're saying that if TPuzzle.Owner=TGame, I'll be able to "see" TGame.Pic from TPuzzle.DrawMe ?Yes, through Owner you can reach the full owner class with TGame(owner).
TGame isn't a TComponent.but it spat out: Error: Incompatible type for arg no. 1: Got "TGame", expected "TComponent"
BoardArray[j,i]:=TPuzzle.Create(Game); //I also tried Self
Why does the form need to be the parent? I thought you were drawing on TGame, not on parent.I'm not drawing on TGame, I need TPuzzle to be "displayed" on the form. But you might just give some idea.
And how are you displaying the TGame on the form?TGame itself doesn't display anything, nor is displayed anywhere, it just contains some game parameters, and some methods to manage the TPuzzle items.
TGame isn't a TComponent.I did this:
You can make TGame a TComponent (bij adding (TComponent) after class and implementing the Create correctly (with inherited etc).
Or you can change the TPuzzle.Create to take TGame as Owner.
What I'm also wondering... You have a TPuzzle which inherits from TImage. Why do you have a TBitmap Pic in the TGame?TBitmap Pic holds the chosen picture to play with which is chopped up and the fragments are kept in Puzzles but I need to keep the whole Pic because during the game there are some things to be drawn on Puzzles and that's what DrawMe does, takes a piece from Pic, draws on it what's necessary and keeps it in Puzzle (whose parent is the main form to be displayed on.... ::)).
I always thought you would take TCustomControl if you want keyboard interaction and TGraphicsControl for the rest, as class to inherit for your components.I'll take it into account when rewriting the classes.
unless you need specific things from TImage which I can't see without your codeI don't need anything from TImage but display my custom drawn semi-transparent BGRABitmap. I tried to do it with a Canvas but it doesn't seem to support transparency (but I guess it's a different topic).
Now I'm beginning to understand but it's hard without seeing the whole picture/puzzleSee the attachment. After NewGameBtn is clicked you'll chose a pic you wanna play with, number of pieces and it looks like in the attachment. Then Puzzles/pieces are shuffled and... have fun :-)
Then TGame could have stayed a simple bare class but then you would have needed to change the create constructor of TPuzzle to take TGame as parameter.Yes, I think a bare class is good enough as I'm not drawing on its Canvas and I can have my own (not inherited) constructor that takes parameters I need.
Now that Game is a TComponent you need to realize that TGame is owner of the TPuzzle's even if the form is the parent so you need to clean up TGame. When you do TGame.Free in the end, all the TPuzzle's are freed too.That's how I imagine it now and that's how it "works" in my head but when it comes to its implementation, it's a whole different story :/
So essentially TGame becomes master component with all the little TPuzzle's as children (which in turn paint their content on a TForm).
You can 'change' the parameters of a constructor by using " reintroduce; overload; " instead of override.reintroduce s completely new to me, I'll take a deeper look at it. I neither knew that classes can have multiple constructors.
You can also just introduce a new constructor. A class can have multiple constructors. You just need to give it a different name.
But... what I miss in your code is the cleanup.I'm aware of cleaning, this is implemented in "real" code. I just stripped the code down to make it easier to review. I even went overboard, as you noticed I even missed Pic: TBitmap;. The array if TPuzzle is freed so is the local BGRABitmap you mentioned further.
You have a TGame with a BoardArray full of TPuzzle.
Who is cleaning all those TPuzzle's up when your done?
And finally the DrawMe;The code you pasted seems to be not modified, it's like from my code and I still can't "see" Pic from DrawMe.
Because TPuzzle.Owner now is a TGame we can cast TGame(Owner) to get to the Pic.
BUTTTT...... There is a big problem here...About cleaning, as I mentioned above, it's done, I just didn't include it here to keep the code as clean as possible.
The TPuzzle itself is a TImage, which is cleaned up without problem.
But inside the DrawMe you have a bmp := TBGRABitmap.Create
It is only used as local variable... but it's never cleaned up !!!!
You need to get rid if it. I'm also not entirely sure what you are doing there.
You create a TBGRABitmap, copy part of the TGame(Owner).Pic to it.
And then.... does nothing. Does this really display something???
Wasn't it your intention so draw to the TImage of TPuzzle itself ??
Also... it's debatable if you want to draw directly on the TForm.I'm not really drawing on the Form directly. There is a TImage which serves as a background for Puzzles. TImage can't have children so puzzles are just moving on top of it according to TImage.Left/Top values. In case the Form resizes the BackgrounImage and Puzzles are resized as well.
Normally you have a container on which you draw.
And because TGame is already converted to a TComponent, it could also just as well be a TCustomControl (with height/width and keyboard/mouse controls).
I'm aware of cleaning, this is implemented in "real" code. I just stripped the code down to make it easier to review.Ok. I assume that you do the cleanup in TGame.Destroy then because that's the place for it if you create those things in CreateGame.
For me it works perfectly. Then there must be something still wrong with your code.QuoteAnd finally the DrawMe;The code you pasted seems to be not modified, it's like from my code and I still can't "see" Pic from DrawMe.
Because TPuzzle.Owner now is a TGame we can cast TGame(Owner) to get to the Pic.
Maybe you pasted wrong piece of code?
It's true that you don't directly draw on TForm but on TImage. But you have a lot of TImages which you make TForm the parent of. That's a lot of small TImages individually on the TForm. It should work... but if you are writing components... you normally use a container for that. So when you traverse the TForms component you only end up with the TGame. And not with a bunch of TImages.QuoteAlso... it's debatable if you want to draw directly on the TForm.I'm not really drawing on the Form directly. There is a TImage which serves as a background for Puzzles. TImage can't have children so puzzles are just moving on top of it according to TImage.Left/Top values. In case the Form resizes the BackgrounImage and Puzzles are resized as well.
Normally you have a container on which you draw.
And because TGame is already converted to a TComponent, it could also just as well be a TCustomControl (with height/width and keyboard/mouse controls).
I was thinking of TGame = class(TCustomControl), this would make a lot of sense to hold Puzzles inside TGame but as you see I want the background to be semi-transparent and I can't seem to get transparency when drawing on TCustomControl.Canvas. But I haven't investigated it yet, so I'm not asking questions.
For me it works perfectly. Then there must be something still wrong with your code.
(And I don't see the code so I can't say what's wrong exactly.)
It's true that you don't directly draw on TForm but on TImage. But you have a lot of TImages which you make TForm the parent of. That's a lot of small TImages individually on the TForm. It should work... but if you are writing components... you normally use a container for that. So when you traverse the TForms component you only end up with the TGame. And not with a bunch of TImages.True, the Puzzle image "belong" to TGame, not TForm, they should be there. I'll go this way.
The TCanvas itself isn't non- or transparent.... it's just what you do with it. And you can draw with BGRA on the TCustomControl.Canvas just as well as on the TForm.Canvas :Dyes, I'm using BGRABitmap to draw my semi-transparent "panels" or the background but they are transparent only when I assign them to a TImage, if I throw it on .Canvas it's not transparent. I quickly googled this and it turns out that canvas can't be semi transparent since it doesn't "know" what's below it so there is nothing my semi-transparent image can be blend with but TImage somehow interacts with the form or something like this.
I just double- and "triplechecked" and everything seems to be like you code but I still get "Error: Identifier not found "Pic".Sorry, apparently I didn't make the change in the code I pasted there.
Although this is the proper way (adding property Pic to get to the private variable), because TGame is in the same .pas file as TPuzzle, accessing the private variable can be done through casting TGame(Owner).Pic. But doing it via a property is the cleaner/proper way to do it ;) (Accessing the private variable via cast won't work if TGame is declared outside this .pas file.)Code: [Select]property Pic: TBitmap read fPic write fPic; // Add this
Sorry, apparently I didn't make the change in the code I pasted there.ohh yes, now it's working perfectly!
Pic is a property of TGame which is the owner.
So you need to do TGame(Owner).Pic.Canvas to get to it.
But doing it via a property is the cleaner/proper way to do it ;)indeed, @cdbc's solution seems to be neater. However I don't understand why property Pic: TBitmap would be visible from DrawMe if Pic as a variable was not .
As ususal TRon beat me to it :DMy apologies Benny... it was not my intention to get in the middle as you and rvk seem to be doing a splendid job already.
Otherwise, you'd have to add a field 'fGame: TGame;' in TPuzzle and in the constructor, you'll do the typecast 'fGame:= AOwner as TGame;' and don't forget to make a forward declaration 'TGame = class(TComponent);' just above 'TPuzzle = class(TImage)'. Then in 'DrawMe' you could just do 'fGame.Pic.Canvas'... etcThat would be a neater approach but I believe that that 2-liner has so much stuff mentioned inside it that it perhaps is a bit confusing for TS :)
Perhaps a fresh cup of coffee will help :DHA... Got you beat there ;D :D 8-)
HA... Got you beat there ;D :D 8-)For sure because I just noticed that I mentioned "right now" 3 times in the same sentence.... speaking of redundancy :)
My apologies Benny... it was not my intention to get in the middle as you and rvk seem to be doing a splendid job already.I think any help is always welcome :)
Just for curiosity... If I need casting in both solutions, how is the property solution better? It seems just an additional (unnecessary, redundant?) feature around Pic.Because Pic was defined in private in the TGame class. And normally, when TGame would have been in another .pas file, you wouldn't be able to reach it because it's in private. Not even with a cast(Owner). And officially it's the proper way to access local variables of that class.
Because Pic was defined in private in the TGame class. And normally, when TGame would have been in another .pas file, you wouldn't be able to reach it because it's in private. Not even with a cast(Owner). And officially it's the proper way to access local variables of that class.Originally Pic was private because it was meant to be used only inside the class and I believed TPuzzle would use it "internally" as it was a part of TGame, my bad. When I realized I can't reach Pic from TPuzzle.DrawMe I moved Pic to public but it didn't help...
But if you are creating a component (which would eventually be redistributed) using property is the correct way.That's what I wanted to know, if it's just because it's the right way or maybe there is some more obscure reason behind this.
Another BTW. Now you do the TGame(Owner) casting. It could also be possible to create a dummy variable MasterGame: TGame in TPuzzle and assign Owner to it. Then you don't need the cast in the DrawMe (because you have the 'local' MasterGame). For that you would need the cast in TPuzzle.Create ( MasterGame := TGame(Owner) ) OR you could change the constructor of TPuzzle to take TGame parameter instead of a TComponent.Another solution. I already have a a couple of them to sleep on. I'll be testing them all these days.
You see... there are several ways to do that all :D