Lazarus

Programming => LCL => Topic started by: majolika on January 22, 2025, 04:55:52 pm

Title: Painting on a control - 3 questions: OnPaint, ImageList, program logic
Post by: majolika on January 22, 2025, 04:55:52 pm
Hello!
I have three newbies question about painting on a control.
For the educational purposes I'm programming a mine sweeper game.

1st question: OnPaint
When I click on a start buttion I show a mine field and fill it with squares. Then I click on the mine field and show a mine. But the mine field fully repaint and my squares is gone. Why?

2nd question: ImageList
ImageList is very usefull control but I found one strange thing: when I'm drawing an same image with ImageList and with Canvas.Draw it gives me different results. Why?

Code: Pascal  [Select][+][-]
  1. ImageList1.Resolution[24].Draw(Bevel1.Canvas,  x, y, 0, True);
Code: Pascal  [Select][+][-]
  1. MineField.Canvas.Draw(x, y, Picture_Loaded_From_TImage_Placed_On_The_Form);

I attached screenshot to show the difference: left, brighter mine field — ImageList, right, darker — Canvas.Draw

3rd question: program logic
First click on the start button works well but the second doesn't work at all. Why?

Here's the minimal proof-of-concept code:

Code: Pascal  [Select][+][-]
  1. unit test_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.  
  12.   TForm1 = class(TForm)
  13.     Bevel1: TBevel;
  14.     Button1: TButton;
  15.     ImageList1: TImageList;
  16.     procedure Bevel1MouseUp(Sender: TObject; Button: TMouseButton;
  17.       Shift: TShiftState; X, Y: Integer);
  18.     procedure Bevel1Paint(Sender: TObject);
  19.     procedure Button1Click(Sender: TObject);
  20.   private
  21.  
  22.   public
  23.  
  24.   end;
  25.  
  26. var
  27.   Form1: TForm1;
  28.   BoxSize: Integer = 24;
  29.   FieldRow: integer = 10;
  30.   FieldCol: integer = 10;
  31.   Button1Clicked: Boolean = False;
  32.   DrawABomb: Boolean = False;
  33.   CoordX, CoordY: Integer;
  34.  
  35. implementation
  36.  
  37. {$R *.lfm}
  38.  
  39. procedure TForm1.Button1Click(Sender: TObject);
  40. begin
  41.   Button1Clicked := True;
  42.   Bevel1.Width := FieldCol * BoxSize;
  43.   Bevel1.Height := FieldRow * BoxSize;
  44.   Bevel1.Show;
  45. end;
  46.  
  47. procedure TForm1.Bevel1Paint(Sender: TObject);
  48. var
  49.   x, y: Integer;
  50. begin
  51.   if Button1Clicked then begin
  52.      for x := 0 to FieldCol - 1 do begin
  53.          for y := 0 to FieldRow - 1 do begin
  54.              ImageList1.Resolution[24].Draw(Bevel1.Canvas,
  55.              (x * BoxSize), (y * BoxSize), 0, True);
  56.          end;
  57.      end;
  58.      Button1Clicked := False;
  59.   end;
  60.   if DrawABomb then begin
  61.      ImageList1.Resolution[24].Draw(Bevel1.Canvas, CoordX, CoordY, 1, True);
  62.      DrawABomb := False;
  63.   end;
  64. end;
  65.  
  66. procedure TForm1.Bevel1MouseUp(Sender: TObject; Button: TMouseButton;
  67.           Shift: TShiftState; X, Y: Integer);
  68. begin
  69.   if Button = mbLeft then begin
  70.      DrawABomb := True;
  71.      CoordX := X; CoordY := Y;
  72.      Bevel1.Repaint;
  73.   end;
  74. end;
  75.  
  76. end.
  77.  
Title: Re: Painting on a control - 3 questions: OnPaint, ImageList, program logic
Post by: ASerge on January 22, 2025, 05:36:36 pm
1st question: OnPaint
When I click on a start buttion I show a mine field and fill it with squares. Then I click on the mine field and show a mine. But the mine field fully repaint and my squares is gone. Why?
It is better to use TPaintBox.
In the OnPaint handler, assume that all the content is corrupted and incorrect. Based on this, build the logic of rendering.
Title: Re: Painting on a control - 3 questions: OnPaint, ImageList, program logic
Post by: majolika on January 22, 2025, 05:43:26 pm
It is better to use TPaintBox.
Thanks! I'll try and be back later to say if it works for me.
Title: Re: Painting on a control - 3 questions: OnPaint, ImageList, program logic
Post by: majolika on January 22, 2025, 07:23:14 pm
It is better to use TPaintBox.
No, if I simply replace TBevel with TPaintBox it's not working. Attached pics demonstrates three steps with TBevel (show mine) and PaintBox (show no mine).

Code: Pascal  [Select][+][-]
  1. unit test2_unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, Forms, Controls, Graphics, Dialogs, ExtCtrls, StdCtrls;
  9.  
  10. type
  11.  
  12.   TForm1 = class(TForm)
  13.     Button1: TButton;
  14.     ImageList1: TImageList;
  15.     PaintBox1: TPaintBox;
  16.     procedure Button1Click(Sender: TObject);
  17.     procedure PaintBox1MouseUp(Sender: TObject; Button: TMouseButton;
  18.               Shift: TShiftState; X, Y: Integer);
  19.     procedure PaintBox1Paint(Sender: TObject);
  20.   private
  21.  
  22.   public
  23.  
  24.   end;
  25.  
  26. var
  27.   Form1: TForm1;
  28.   BoxSize: Integer = 24;
  29.   FieldRow: integer = 10;
  30.   FieldCol: integer = 10;
  31.   Button1Clicked: Boolean = False;
  32.   DrawABomb: Boolean = False;
  33.   FieldWidth, FieldHeight: Integer;
  34.  
  35. implementation
  36.  
  37. {$R *.lfm}
  38.  
  39. procedure TForm1.Button1Click(Sender: TObject);
  40. begin
  41.   Button1Clicked := True;
  42.   PaintBox1.Width := FieldCol * BoxSize;
  43.   PaintBox1.Height := FieldRow * BoxSize;
  44. end;
  45.  
  46. procedure TForm1.PaintBox1MouseUp(Sender: TObject; Button: TMouseButton;
  47.           Shift: TShiftState; X, Y: Integer);
  48. begin
  49.   if Button = mbLeft then begin
  50.      DrawABomb := True;
  51.      PaintBox1.Repaint;
  52.   end;
  53. end;
  54.  
  55. procedure TForm1.PaintBox1Paint(Sender: TObject);
  56. var x, y: integer;
  57. begin
  58.   if Button1Clicked then begin
  59.      for x := 0 to FieldCol - 1 do begin
  60.          for y := 0 to FieldRow - 1 do begin
  61.              ImageList1.Resolution[24].Draw(PaintBox1.Canvas,
  62.              (x * BoxSize), (y * BoxSize), 0, True);
  63.          end;
  64.      end;
  65.      Button1Clicked := False;
  66.   end;
  67.   if DrawABomb then begin
  68.      ImageList1.Resolution[24].Draw(PaintBox1.Canvas, x, y, 1, True);
  69.      DrawABomb := False;
  70.   end;
  71. end;
  72.  
  73. end.
  74.  

In the OnPaint handler, assume that all the content is corrupted and incorrect. Based on this, build the logic of rendering.
I found a simplistic clone of MS Paint (https://github.com/DreamVB/MePaint) on Github. It seems that author creates a bitmap in memory, in OnMouseUp/Down/Move draws something on top of this bitmap and then in OnPaint just put the whole bitmap on the PaintBox. But it's kinda wierd solution. Why are the things so complicated?!
Title: Re: Painting on a control - 3 questions: OnPaint, ImageList, program logic
Post by: ASerge on January 22, 2025, 07:55:27 pm
It seems that author creates a bitmap in memory, in OnMouseUp/Down/Move draws something on top of this bitmap and then in OnPaint just put the whole bitmap on the PaintBox. But it's kinda wierd solution. Why are the things so complicated?!
It just caches a lot of drawings on a separate bitmap. To speed up. But the principle remains the same - redraw each time.
Title: Re: Painting on a control - 3 questions: OnPaint, ImageList, program logic
Post by: TRon on January 22, 2025, 08:18:09 pm
But it's kinda wierd solution. Why are the things so complicated?!
That is how graphics work.

Think for it a minute. In general why do you see what is on your screen ? How does that compute in practice ? When speaking of raster graphics the graphics card 'draws' the content of a buffer (video memory) to a screen and does that at a certain rate. So each time the graphics in that buffer is completely outputted to the monitor. If you change even a single pixel inside that buffer, the whole buffer outputted to the monitor again. It even outputs that buffer when nothing was changed inside it. Notice the old-school description as in this day and age modern graphic cards work much more complicated. see also howstuffworks. (https://computer.howstuffworks.com/graphics-card.htm).
Title: Re: Painting on a control - 3 questions: OnPaint, ImageList, program logic
Post by: majolika on January 22, 2025, 08:37:19 pm
It just caches a lot of drawings on a separate bitmap. To speed up. But the principle remains the same - redraw each time.
So I have to do the same, right? Create a bitmap in memory, draw the mine field on this bitmap, place bitmap on PaintBox, trigger OnPaint by Repaint, Update or whatever, draw something (e.g. bomb) on top of mine field, place this new picture on PaintBox, trigger OnPaint again and so on. Not an elegant way. And passing signals like "I press a button" or "I click on mine field" from OnClick to OnPaint with global variables is not an elegant too. Well... I have to deal with it. :)

And I think I realize my ImageList issue. It about transperency. But I have to check it out.

So only one question left: why does start button stops working after first click?
Title: Re: Painting on a control - 3 questions: OnPaint, ImageList, program logic
Post by: majolika on January 22, 2025, 08:43:38 pm
That is how graphics work.
Ok, I get it. But why covering/uncovering my window with another window (e.g. browser) does not triiger repainting and minimizing — trigger? Althouth it's pretty the same for my app form — nothing changed on it.
Title: Re: Painting on a control - 3 questions: OnPaint, ImageList, program logic
Post by: TRon on January 22, 2025, 09:01:07 pm
Ok, I get it. But why covering/uncovering my window with another window (e.g. browser) does not triiger repainting and minimizing — trigger? Althouth it's pretty the same for my app form — nothing changed on it.
The only difference with other controls is that the painting (refresh) happens automatically on/for certain events.

This can't be done for a graphic control (TPaintbox in this particular case) in a manner that satisfies everyone. The control is simply too basic for that. Though having said that you could perhaps react on another event that signifies to redraw the window or panel at which the paintbox control is positioned and use that to update your paintbox. tbh, I never tried.

btw: your message on how to do it makes it sound very complicated while in fact it is only drawing a bitmap to a canvas of the paintbox and 'catching' the mouse/pixel positions at which was clicked and compared that with your 'cells' location.
Title: Re: Painting on a control - 3 questions: OnPaint, ImageList, program logic
Post by: majolika on January 22, 2025, 09:26:27 pm
you could perhaps react on another event that signifies to redraw the window or panel at which the paintbox control is positioned and use that to update your paintbox. tbh, I never tried.
You wanna say what I can prevent control repainting?
While googling I read something about that but I thought it's needed only when some operations take too much time.

your message on how to do it makes it sound very complicated
No, no, it's not a big deal. :) Dealing with painting, repainting and so on seems to me complicated and unclear.
I almost complited the first draft of my MineSweeper (unchageable 16×16, primitive randomizing the mine field, etc) when I found this repainting problem.
Title: Re: Painting on a control - 3 questions: OnPaint, ImageList, program logic
Post by: majolika on January 22, 2025, 09:30:24 pm
{ offtop on
How do you edit your messages? I dont' see any button or something like that.
offtop off}

Title: Re: Painting on a control - 3 questions: OnPaint, ImageList, program logic
Post by: TRon on January 22, 2025, 09:34:04 pm
{ offtop on
How do you edit your messages? I dont' see any button or something like that.
offtop off}

As a new user you are not allowed to edit your message until you have done a minimal amount of posts. This is done by the forum software as a protection to prevent trolls and spammers from posting and modifying their message. Unfortunately we have a lot of those.

For now if you need it, quote your original message in a new post and add the modification you wanted to make to make it clear what you wanted to change. Sorry, it is what it is. I am not sure what exactly the amount of posts is btw. I believe it was over 10.
Title: Re: Painting on a control - 3 questions: OnPaint, ImageList, program logic
Post by: lainz on January 22, 2025, 09:35:39 pm
To update less times you can do:

Have a bitmap, update only the parts you changed, repaint is always needed, so no problem with that..
Title: Re: Painting on a control - 3 questions: OnPaint, ImageList, program logic
Post by: majolika on January 22, 2025, 09:37:28 pm
As a new user you are not allowed to edit your message until you have done a minimal amount of posts.
Ahh... Ok, I get it. Thanks for the clarification!
Title: Re: Painting on a control - 3 questions: OnPaint, ImageList, program logic
Post by: majolika on January 22, 2025, 09:48:48 pm
Have a bitmap
MineSweeper is not Doom. There's no need to get as much fps as you can. :)
"Having a bitmap", that's the case! Intermediate layer between one cotrol and another. It's not difficult in realization but it makes program logic more complex, more unclear.
"Beautiful is better than ugly, simple is better than complex...", you know. :)
Title: Re: Painting on a control - 3 questions: OnPaint, ImageList, program logic
Post by: lainz on January 22, 2025, 09:51:28 pm
If your game doesn't flicker do as you wish, but there's a flicker in some cases because you're painting several times on the screen. Noticeable only in some old hardware (that I had in time and noticed it with Lazarus and Windows 7).
Title: Re: Painting on a control - 3 questions: OnPaint, ImageList, program logic
Post by: TRon on January 22, 2025, 09:54:23 pm
If your game doesn't flicker do as you wish, but there's a flicker in some cases because you're painting several times on the screen. Noticeable only in some old hardware (that I had in time and noticed it with Lazarus and Windows 7).
This was exactly what I wanted to write as I remember minesweeper on win95 and when a lot of cells were being opened you could see the flickering of the cells when they were drawn. Ofc that has been a long time ago with different hardware but still it did not have to that and could be solved even back then.
 
@majolika: PS: how is the weather over there ?  (let's up that postcount of yours  :D )

edit: nevermind. I just noticed you are  not 'green' anymore. You should be able to edit your messages now.
Title: Re: Painting on a control - 3 questions: OnPaint, ImageList, program logic
Post by: majolika on January 22, 2025, 10:12:31 pm
PS: how is the weather over there ?  (let's up that postcount of yours  :D )
Just got back from the outside. :)
During the day it was +7°C but now there's a bliizzard out there and about –15°C.
Title: Re: Painting on a control - 3 questions: OnPaint, ImageList, program logic
Post by: lainz on January 22, 2025, 10:15:53 pm
Here is 35° C.

And yes I can also remember the flickering from the original minesweeper from Windows 95.  :)
Title: Re: Painting on a control - 3 questions: OnPaint, ImageList, program logic
Post by: majolika on January 22, 2025, 10:22:23 pm
Here is 35° C.
Lucky you! :)


And yes I can also remember the flickering from the original minesweeper from Windows 95.  :)
I remember flickering in MS DOS on my monochrome display. :))
Title: Re: Painting on a control - 3 questions: OnPaint, ImageList, program logic
Post by: TRon on January 22, 2025, 10:29:29 pm
Just got back from the outside. :)
During the day it was +7°C but now there's a bliizzard out there and about –15°C.
Minus 15 ? darn that is not weather to show up in shorts. It's currently around 4°C here.

Still not able to edit a message ?
Title: Re: Painting on a control - 3 questions: OnPaint, ImageList, program logic
Post by: majolika on January 22, 2025, 10:41:48 pm
Still not able to edit a message ?
No. But that's not the most wanted feature for me. :)

You better tell me why my Button1 works only once. :)

Logic is pefectly clear:
• Button clicked? → ok, set global bool
• in OnPaint check global bool, if true do staff  and unset global bool
Nothing special, it seems. But then Button stops reacting.
Why? WHY?!
Title: Re: Painting on a control - 3 questions: OnPaint, ImageList, program logic
Post by: TRon on January 22, 2025, 11:25:16 pm
You better tell me why my Button1 works only once. :)
Oh, I'm sorry. I missed that you still have issues.

Quote
Logic is pefectly clear:
• Button clicked? → ok, set global bool
• in OnPaint check global bool, if true do staff  and unset global bool
Nothing special, it seems. But then Button stops reacting.
Why? WHY?!
I can't answer the why as your logic seems correct. However I suspect that the paintbox does not get a refresh on secondary press of your button. Why that does work for the first press and not (as you stated) consecutive ones I have no idea. Try adding a call to paintbox1.refresh at your buttonclick event after having set the necessary values and see if that helps.

edit: on second thought. The first time around the dimensions of the paintbox are being set which probably triggers the repaint. On consecutive runs the dimensions are the same, so nothing has to be changed/refreshed.
Title: Re: Painting on a control - 3 questions: OnPaint, ImageList, program logic
Post by: majolika on January 22, 2025, 11:35:44 pm
Try adding a call to paintbox1.refresh at your buttonclick event after having set the necessary values and see if that helps.
It works! Great! Thanks a lot!
But why it works?! What's the matter?
Title: Re: Painting on a control - 3 questions: OnPaint, ImageList, program logic
Post by: TRon on January 22, 2025, 11:37:30 pm
But why it works?! What's the matter?
See edit in my previous post. You can verify that by setting the paintbox dimensions on form create event and then try the buttonclick. If my clarification is correct then it should not paint on the first click either.

To be fair in that case you should assign the onpaint event in the formcreate event as well.
Title: Re: Painting on a control - 3 questions: OnPaint, ImageList, program logic
Post by: majolika on January 22, 2025, 11:55:05 pm
If my clarification is correct then it should not paint on the first click either.
It works fine: paint from the first click, even wothout assigning onpaint in the fomcreate.
Your guess is interesting but does not explain how paintbox.onpaint corupt button.onclick?
How one control can so unclearly ingluence another thougth I must send signals with the global vars, bot directly.
It blows my mind. I need to get some sleep. :)
Title: Re: Painting on a control - 3 questions: OnPaint, ImageList, program logic
Post by: majolika on January 22, 2025, 11:56:46 pm
Sorry for typos I'm dying to sleep.
Title: Re: Painting on a control - 3 questions: OnPaint, ImageList, program logic
Post by: TRon on January 23, 2025, 12:23:25 am
Sorry for typos I'm dying to sleep.
By all means, then get some sleep please. There is always tomorrow. I see you are still logged in. I'll have a look at it later because I have other things on my plate right now as well. We'll figure it out  :)
Title: Re: Painting on a control - 3 questions: OnPaint, ImageList, program logic
Post by: TRon on January 23, 2025, 01:04:09 am
It works fine: paint from the first click, even wothout assigning onpaint in the fomcreate.
Your guess is interesting but does not explain how paintbox.onpaint corupt button.onclick?
How one control can so unclearly ingluence another thougth I must send signals with the global vars, bot directly.
It blows my mind. I need to get some sleep. :)
Well, in that case there must be something off with your testing method or a difference in widgetset/platform.

If the onpaint method is removed from the object inspector and set the dimensons of the paintbox at form create event and after that assign the onpaint event...
Code: Pascal  [Select][+][-]
  1. procedure TForm1.FormCreate(Sender: TObject);
  2. begin
  3.   PaintBox1.Width := FieldCol * BoxSize;
  4.   PaintBox1.Height := FieldRow * BoxSize;
  5.   PaintBox1.OnPaint := @Self.PaintBox1Paint;
  6. end;
  7.  

.. and have the buttonclick event read:
Code: Pascal  [Select][+][-]
  1. procedure TForm1.Button1Click(Sender: TObject);
  2. begin
  3.   Button1Clicked := True;
  4. //  PaintBox1.Width := FieldCol * BoxSize;
  5. //  PaintBox1.Height := FieldRow * BoxSize;
  6. //  PaintBox1.Refresh;
  7. end;
  8.  

Then the paintbox onpaint event will not be called at that click and as a result the grid will not be painted and will stay empty.

This is to be expected as changing the width or height (e.g. the value you set differs from what the value of that property already was set to) triggers for the paintbox control to be resized, in the process triggering an onpaint event.

Because code in this post does not have an onpaint event assigned while resizing the paintbox, the paintbox will not draw anything besides what it draws in its default paint implementation (which apparently is an empty box).

Hence we have to trigger the event ourselves by either using .refresh, .update, .invalidate or invoke the paint method (whatever is applicable for the situation).

See also attached project.
Title: Re: Painting on a control - 3 questions: OnPaint, ImageList, program logic
Post by: lainz on January 23, 2025, 03:04:18 am
Use a Timer and refresh the paintbox every 33ms (1000/30) to get something like 30fps.

Is like real games works.
Title: Re: Painting on a control - 3 questions: OnPaint, ImageList, program logic
Post by: majolika on January 23, 2025, 10:42:47 am
Use a Timer and refresh the paintbox every 33ms (1000/30) to get something like 30fps.
Is like real games works.
Thanks for advice! Somewhen I'll use it!
Title: Re: Painting on a control - 3 questions: OnPaint, ImageList, program logic
Post by: majolika on January 23, 2025, 10:56:33 am
If the onpaint method is removed from the object inspector and set the dimensons of the paintbox at form create event and after that assign the onpaint event...
.. and have the buttonclick event read:
Then the paintbox onpaint event will not be called at that click and as a result the grid will not be painted and will stay empty.
That's definitly clear!

Hence we have to trigger the event ourselves by either using .refresh, .update, .invalidate or invoke the paint method (whatever is applicable for the situation).
Googling and reading this topic (https://forum.lazarus.freepascal.org/index.php/topic,40433.0.html) gives me understanding that
refresh — refreshes the component internally without repainting it;
update — refresh + repaint;
invalidate — tells the component that its visuals is outdated and then repaint only visible part of component (maybe some part off screen or cover by another window, whatever)
// there is some unclearness: are refresh and update not telling the component that its visuals is outdated? What's the point?
repaint — just repaint
// and there is some unclearness too: what's the difference between calling Repaint and calling OnPaint method? For example, PaintBox1.Repaint or PaintBox1.OnPaintDoSomeStuff.

See also attached project.
Thanks for you time! I've read it and here some things...

Quote
  // actually we should calc the row and column of the cell that contains a bomb
  // and then draw the complete grid as above, making an exception for the cell
  // that contains the bomb and draw the bomb instead of a 'normal' closed cell.
No, we shouldn't. If there are some other already opened cells drawing the complete grid will override it.
In my real program I'm doing exactly how you mentioned beneath — working with the dynamic array of cells and repainting (or not, if it's already opened) only one that clicked.
Actually I was a fool and doing all my drawings in the onclick method. I thougth that component (in my case — bevel) holds its state unchanged until I'm draw something on it.
When I tried to rewrite it to draw in the onpaint method I'm stuck with the problem, reproduce it with the minimal code and came here for advice. :)

Now I know that the components redraw itselves on every sneeze. God damn it! :D

By the way if so what's the difference between Bevel and PaintBox?
If every component redraws itself on every move so I can use any as a layer for the mine field, even a button (if I somehow override reacting on clicking).
Title: Re: Painting on a control - 3 questions: OnPaint, ImageList, program logic
Post by: TRon on January 23, 2025, 06:32:13 pm
Googling and reading this topic gives me understanding that
Although you can use google, the help is a much better source. It is just a press to F1 away  :)

As a good second alternative, the online documentation (https://lazarus-ccr.sourceforge.io/docs/index.html) as well as dsiders nextdoc documentation (https://dsiders.gitlab.io/lazdocsnext/)

Still have questions, then there is the wiki (https://wiki.freepascal.org/) with a plethora of topics and examples. Speaking of which the Lazarus IDE and its component comes loaded with examples

fwiw in particular the wiki article developing with graphics (https://wiki.freepascal.org/Developing_with_Graphics), if even to get an idea of the possibilities that are available.

I am simply mentioning all of them in case you are not aware, in case you already are then please ignore.

Ofc. when in doubt you can always post your questions but as an genuine advise, it should not be your first resort.

refresh — refreshes the component internally without repainting it;
TControl.Refresh (https://lazarus-ccr.sourceforge.io/docs/lcl/controls/tcontrol.refresh.html):
Quote
Calls the Repaint method to either draw the clipping rectangle for the control to the handle in the Parent, or invalidate the control area for the next paint operation that occurs when the Parent controls are Visible.

invalidate — tells the component that its visuals is outdated and then repaint only visible part of component (maybe some part off screen or cover by another window, whatever)
Invalidate (https://lazarus-ccr.sourceforge.io/docs/lcl/controls/tcontrol.invalidate.html)
Quote
Calls the InvalidateControl method to invalidate the bounds rectangle for the control using the clipping rectangle for the Parent. The control is redrawn when there are no pending messages in the message queue.
How exactly it is implemented depends on the used control. But yes in basics what you wrote applies.

update — refresh + repaint;

update (https://lazarus-ccr.sourceforge.io/docs/lcl/controls/tcontrol.update.html)
Quote
Update calls the Update method in the Parent control to refresh the window where the control is hosted. No actions are performed in the method when Parent has not been assigned.
Thus, the parent gets notified. What the parent does (in relation to the component that was placed on it and notified it) depends on the parent.

// there is some unclearness: are refresh and update not telling the component that its visuals is outdated? What's the point?
In case the former, yes. In case the latter it depends on the parent component. But it also depends on how the child-control handles 'instructions' from the parent. It can vary per control.

repaint — just repaint
Correct.

// and there is some unclearness too: what's the difference between calling Repaint and calling OnPaint method? For example, PaintBox1.Repaint or PaintBox1.OnPaintDoSomeStuff.
You do not call the onpaint method. It is an event that is invoked by the control (when assigned) whenever it it deems necessary to do so while invoking paint or repaint is something that you are able to call whenever you feel the need to do so.

When the event exactly is invoked depends on how a control implements it but basically it does this behind the scenes so that you do not have to worry about the order in which things need to happen for the control to visually presents itself (correctly).

Calling a method directly, could imply you have to wait for the method to finish while other methods uses messaging in order to eventually invoke the (assigned) event handler(s).

No, we shouldn't. If there are some other already opened cells drawing the complete grid will override it.
Not if you keep track of the state, that is the whole point I was trying to make. The state determines how a cell should be drawn.

To make an analogy, it seems to me like you view this as a piece of paper and a pencil with an eraser where each next step you add or remove something from the paper. That is the way as suggested by ASerge by using a off-screen bitmap.

However, the way you have implemented it in your example requires to redraw the whole grid for each change that was made to the grid.

Both solutions are valid but also both have their pros and cons.

n my real program I'm doing exactly how you mentioned beneath — working with the dynamic array of cells and repainting (or not, if it's already opened) only one that clicked.
Actually I was a fool and doing all my drawings in the onclick method. I thougth that component (in my case — bevel) holds its state unchanged until I'm draw something on it.
When I tried to rewrite it to draw in the onpaint method I'm stuck with the problem, reproduce it with the minimal code and came here for advice.
As stated /if/ you keep using the path as in your example (nothing wrong with it, just making sure you get the idea) then get rid of the drawbomb check and the click check and draw the whole grid (e.g. every cell). Use a state for each cell to know exactly what you need to draw (cell open, cell closed, etc.). When you left click the mouse then change the state of the cell before drawing/updating/refreshing the grid.

In case you only wish to draw the changes in opposite to the method above then make use of an off-screen bitmap and in the onpaint event draw that bitmap to the canvas of the paintbox.

Hopefully that was able to clarify a few things, if not then don't hesitate to point them out.

edit: typos and hopefully better distinction between quotes.
Title: Re: Painting on a control - 3 questions: OnPaint, ImageList, program logic
Post by: majolika on January 23, 2025, 07:13:20 pm
Although you can use google, the help is a much better source. It is just a press to F1 away :-)
Thanks for your paitience!
I'm not a newbie in programming itself but I'm total newbie in LCL so a couple of things can be unclear and challenging for me. :)
And of course I'm not an expert, programming is just a hobby for me. Sort of. :)

Hopefully that was able to clarify a few things, if not then don't hesitate to point them out.
Sure it was! Thanks again!

I redwrote my example to use bitmap as an intermediate layer and it works well.
Only one thing seems to me not the best and elegant way to do it.

Code: Pascal  [Select][+][-]
  1. procedure TForm1.FieldInit(Cols, Rows: Integer);
  2. begin
  3.   ...
  4.   if FieldBitmap <> nil then begin
  5.      FieldBitmap.Destroy;
  6.      FieldBitmap := TBitmap.Create;
  7.   end;
  8.   ...
  9. end;
  10.  
  11. procedure TForm1.FormCreate(Sender: TObject);
  12. begin
  13.   FieldBitmap := TBitmap.Create;
  14. end;

I'm just recreating the bitmap on every restart but it feels unnatural.
To my confuse just repaint doesn't work.

Renewed example attached so you can play around with it.
Title: Re: Painting on a control - 3 questions: OnPaint, ImageList, program logic
Post by: TRon on January 23, 2025, 07:25:43 pm
I was just on my way heading out so it might take me a while to reply. Post noted and I will have a look when I'm back. But perhaps someone else is able to address your concerns/question(s) as well.
Title: Re: Painting on a control - 3 questions: OnPaint, ImageList, program logic
Post by: majolika on January 23, 2025, 07:29:27 pm
I was just on my way heading out so it might take me a while to reply. Post noted and I will have a look when I'm back. But perhaps someone else is able to address your concerns/question(s) as well.
I'm not in a hurry! Good luck out there!
Title: Re: Painting on a control - 3 questions: OnPaint, ImageList, program logic
Post by: majolika on January 23, 2025, 10:07:31 pm
Offtopic

I was playing around with Form Designer for better understanding Aligniing and Anchoring mechanics and I think I found a bug in Docked Form Editor. Very easy to reproduce.

For now I use Lazarus 3.6 and I think I have to update to 3.8. Maybe this bug is already corrected in 3.8. But I'm not sure how to do it properly.
Title: Re: Painting on a control - 3 questions: OnPaint, ImageList, program logic
Post by: TRon on January 23, 2025, 10:52:32 pm
Thanks for your paitience!
To show incentive is half the job  :)

Quote
I'm not a newbie in programming itself but I'm total newbie in LCL so a couple of things can be unclear and challenging for me. :)
I understand and that shouldn't be a problem.

However, and try keep that in mind if you are able, although in basics components work in a similar fashion the details will differ for each individual component.

That is why it is relatively difficult to get the hang of it because the only way to figure that out is by trying... lots of trying.

Quote
And of course I'm not an expert, programming is just a hobby for me. Sort of. :)
Did we not all started out that way ?

I consider myself a newbie as well even though I have been using FPC and Lazarus for decades. Both lazarus/LCL and FPC/RTL/FCL are a moving target and get new features with each release.


As a hint (take it or leave it as you wish):
I personally treat every component that I am not familiar with as a Christmas present that requires careful unwrapping.

In that regards when start using a new component (that I are unfamiliar with) in an existing project, I never do that right away but experiment with the component first and try figure out that the functionality that my project requires is actually feasible to accomplish with that component. If the components behaves or acts in a way that makes my project more difficult to maintain or to even implement the use of that component then I seek elsewhere or in case a bit more familiar with the component try to change the default behaviour to match the requirements for my project. Worse case scenario requires to create my own component.

The unwrapping sequence allows me to get familiar with the component, get to know it in general but especially its quirks, know how it behaves for different widgetsets and platforms.

As you can imagine that takes time and for sure I have not even touched a quarter of the existing components out there. Therefor I consider that to not have knowledge on any of those other than what I am able to read in the documentation and/or wiki and/or am able to check with existing examples. Questions asked in the forums are sometimes helpful as well especially when there are hardly any examples present and/or the components are undocumented (sometimes even requires to dive into their sources in order to be able to see how it is suppose to behave).

Things get easier the longer you play around with Lazarus and its components. It is a matter of how much time do you wish to invest.

And that answer is different for each individual. Some are able to get by using some buttons, labels and panels with their basic functionality while others require reporting components, database access and 3D drawing/editing controls with detailed modified behaviour to match their project's requirements.

fwiw: There are even individuals that do not get all of this, try the shortcut option and just start copying over other people's snippets, post them on the forum reporting a prolem in the hopes someone else will solve their issues so that they do not have to (they are easy to spot though).

Back to topic.

Quote
I redwrote my example to use bitmap as an intermediate layer and it works well.
Yes, I can see. That is nice progress.

I do have a couple of hints but remember that no matter what anyone tries to tell you (unless you are ofc really in the wrong) how you implement
things can be a topic of discussion but in the end if it works (for you) then there is no right nor wrong.

Quote
Only one thing seems to me not the best and elegant way to do it.
Ok, let's try provide some pointers. Keep my previous remark in mind.

The bitmap variable in the source is positioned between the components. It is wiser to situate that into a separate section. If you do not have to access that field outside your form then it is custom to make it private (but you could just as well make it part of your other variable declaration block).

Speaking of which. All variables declared right under the form variable declaration are accessible outside your form units as well. It is custom to make these variables part of a private section in your form type declaration.

If you must place them outside your form type declaration it is custom to do that as part of the implementation section. It is rare though but I understand and on occasion use it myself as well in testing phase.

The bitmap can be resized and also be cleared. Therefor there is no actual need to create the bitmap over and over again. You can create it once at form create event and destroy (free) it once at form destruction event.

Other than that I could perhaps start nitpicking on a dozen of other things but it looks as you seem to be doing well. If it works, it works  :D

I rather wait for your epiphany moment regarding cell status (or perhaps you might surprise us with something clever)   :P

Keep it up I would say as you are on the right track !
Title: Re: Painting on a control - 3 questions: OnPaint, ImageList, program logic
Post by: majolika on January 24, 2025, 12:30:40 am
I personally treat every component that I am not familiar with as a Christmas present that requires careful unwrapping.
That's a good idea!

I do have a couple of hints
And I'm here to get some!

The bitmap variable in the source is positioned between the components.
It is wiser to situate that into a separate section.
You right! I just placed it right after form variables keeping in mind only one thing: it sholud have access to other form's vars and methods.
I moved it to private section now but I thibk it doesn't matter for such a small educational project like mine.

Speaking of which. All variables declared right under the form variable declaration are accessible outside your form units as well. It is custom to make these variables part of a private section in your form type declaration.
You right. But what if I make another form (for example, Options form to change mine field sizes with sliders and see how the mine field is changing sizes in real time)? Option form will not have access to private section of Main form. So some vars should be global.

If you must place them outside your form type declaration it is custom to do that as part of the implementation section.
Correct me if I'm wrong.
All that is placed in the interface section are accessible from another units and forms.
All that is placed in the implementation section are not accessible from another units and forms.
This is how I can recall it from my Borland Pascal times. :)

The bitmap can be resized and also be cleared.
TBitmap.Clear! Damn it! So easy!

I rather wait for your epiphany moment regarding cell status.
:D
Cell status is not a problem: I'm already using 2 arrays of integer to manage that: one array — with bombs and neighbours (I decided to calculate neighbors one time at a randomizing stage but this is a subject to change), another — with cell statuses.
Of cource I can use only one array of bytes to minimize using memory but it will cost more annoying calculations, bitmasks... I don't like that.
And I hope I will never be in need of a million×billion-sized mine fiield. :)

First of all  I want to make things as simple as I can.
Initially I want to make program with basic functionality keeping in mind future functionality.
For example now I randomize the mine field in just 4 code lines: 1 - get random col 2 - get random row, 3 - check if there's already a bomb, 4 - set bomb and atatus in my 2 arrays.
But later I want to make more complex randomization to avoid unsolveable fields. Later. For now let the things be simple but extendable.

Maybe I should start another topic for qustions which definitly will come up...
E.g. aligning and anchoring... I just touched water but see a lot of underwater stones.
Title: Re: Painting on a control - 3 questions: OnPaint, ImageList, program logic
Post by: TRon on January 24, 2025, 06:38:20 am
You right! I just placed it right after form variables keeping in mind only one thing: it sholud have access to other form's vars and methods.
I moved it to private section now but I thibk it doesn't matter for such a small educational project like mine.
You're probably right.

Like most people I do not like mixing IDE generated content with my own especially since it has the habit of being tossed around when adding and removing components.

In light of your other remark(s) it is quite possible to add a class visibility specifier multiple times in a class type definition, for instance the public specifier.

Quote
You right. But what if I make another form (for example, Options form to change mine field sizes with sliders and see how the mine field is changing sizes in real time)? Option form will not have access to private section of Main form. So some vars should be global.
You are quite right about that. I assumed (yes, mother of all screw-ups  :D ) it was a simple implementation contained in a single unit/form (perhaps using menu's and/or other components on the same form).

Besides that, it is also possible to pass along variables to another procedure/function/method so that its value can be changed outside it's normal scope. You can also do that with a bunch of variables tucked away inside a record, object or class therefor it is not always necessary to have direct access to variables. Getting really fancy you can also pass along messages between different forms to request (which you would manually have to handle) a change of value for a variable thought that would be really overkill.

Quote
Correct me if I'm wrong.
All that is placed in the interface section are accessible from another units and forms.
All that is placed in the implementation section are not accessible from another units and forms.
This is how I can recall it from my Borland Pascal times.
If you are talking about direct access then yes you are absolutely correct (see above remark about passing along)

With regards to your other remarks about the game you can implement things using different methods. Whatever works best for you would be the correct implementation  ;D

Quote
Of cource I can use only one array of bytes to minimize using memory but it will cost more annoying calculations, bitmasks... I don't like that.
Instead of bitmasks, have a look at a set (enums). If my calculations are correct then the maximum number of neighbouring bombs would be eight (nine if you count zero). Add to that whether or not a cell contains a bomb or a flag perhaps a question-mark and you would be well on your way.

But as said, whatever works best for you is the way to go.

Quote
E.g. aligning and anchoring... I just touched water but see a lot of underwater stones.
Ah yes you mentioned having an issue with that. Note that there were quite a few changes/fixes with regards to anchors and alignments since the release you seem to be using so it might very well you encountered a bug (that might also have been fixed in the mean time). With the information provided (unless I missed something) it is quite impossible to reproduce/verify.

If you are able to explain what you are trying to achieve and what fails (e.g. what in your eyes is a (reproducable) bug) then we can have a look at it.

Also note that Lazarus 4.0RC2 also exist and that has even more fixes in comparison to 3.8 (though I am not sure how this relates wrt the few days ago released 3.8 fixes release).

Whether or not you wish to pursue that anchor issue in this same thread is up to you. If you wish to focus on a particular issue and perhaps try attract more readers then it might be helpful to start another thread solely about that topic but things always seem to have a tendency to work out differently as expected/intended.
Title: Re: Painting on a control - 3 questions: OnPaint, ImageList, program logic
Post by: majolika on January 24, 2025, 10:26:08 am
Like most people I do not like mixing IDE generated content with my own
Me too but it's not a big deal for a small educational projects like mine. And of course visual designer might be quite useful.

Quote
especially since it has the habit of being tossed around when adding and removing components.
Right! So I decided to write messy code at first and refactor it only when I'm done.

Quote
In light of your other remark(s) it is quite possible to add a class visibility specifier multiple times in a class type definition, for instance the public specifier.
Somthing like:   private {bla-bla-bla} public {bla-bla-bla} private {bla-bla-bla} public {bla-bla-bla} ?
Looks like useful but bad code practice.

Quote
I assumed (yes, mother of all screw-ups  :D ) it was a simple implementation contained in a single unit/form (perhaps using menu's and/or other components on the same form).
For now it is. But I write a code keeping in mind that functionality of my program might extend. At the same time I don't want to make my code overcomplicated (like passing massages with manually handling). No doubt there's a different ways to passing vars and using globals is not the best practice. But for a small project that's enough. I assume (mother of all screw-ups :D ) that there are no penalties or issues with using globals for — again! — a small project.
But I appreciate that you share this knowledge with me. It might be quite useful if I'm gonna make a big, serious project.

Quote
Whatever works best for you would be the correct implementation  ;D
I hope so! :)

Quote
Instead of bitmasks, have a look at a set (enums). If my calculations are correct then the maximum number of neighbouring bombs would be eight (nine if you count zero). Add to that whether or not a cell contains a bomb or a flag perhaps a question-mark and you would be well on your way.
Yes, It was my first attempt but lately I'll show you why I decided not to use it.

Quote
If you are able to explain what you are trying to achieve and what fails (e.g. what in your eyes is a (reproducable) bug) then we can have a look at it.
Ok, I'll explain and attach some screenshots.
First of all, I use Lazarus 3.6 with AnchorDockingDsgn and DockedFormEditor packeges incorporated. Windows 10.
I was playing around with a newly created project to see how anchoring, aligning and autosizing works.
So here's the minimal steps to reproduce:

1. Create new project;
2. In the Form Designer place TPaintBox on an empty form;
3. Set AutoSize property of TForm to True by checking it in the Object Inspector.
4. Ka-boom!

Have a look to attached screenshots to see what happens.

I think that the bug is inside DockedFormEditor because if I remove this package and rebuild IDE I'm no longer able to reproduce it.
But I don't want to remove this package, it's very handy and useful for me.
For now I don't need TForm.AutoSize property or I can set it in a code if I needed. But the bug is the bug.

Quote
Also note that Lazarus 4.0RC2 also exist and that has even more fixes in comparison to 3.8 (though I am not sure how this relates wrt the few days ago released 3.8 fixes release).
I prefer to use stable versions of products so betas or RCs is not an option. Only in special cases.
But it would be nice if you give me an advice on how to [properly update 3.6 to 3.8.

Quote
If you wish to focus on a particular issue and perhaps try attract more readers then it might be helpful to start another thread solely
I think later I'll start a new topic with subject something like "Stupid questions of a newbie making a sort of Mine Sweeper". :)

Quote
things always seem to have a tendency to work out differently as expected/intended.
Story of my life :D
Title: Re: Painting on a control - 3 questions: OnPaint, ImageList, program logic
Post by: majolika on January 24, 2025, 11:06:26 am
2. In the Form Designer place TPaintBox on an empty form;
3. Set AutoSize property of TForm to True by checking it in the Object Inspector.
Steps 2 and 3 can be reversed and instead of TPaintBox it can be used another non-visual components. Not all.
For example, TOpenDialog doesn't cause any trouble but TImage, TShape — do.
I didn't test all available components but I hope three (TPaintBox, TImage, TShape) is enough to locate the bug.
Title: Re: Painting on a control - 3 questions: OnPaint, ImageList, program logic
Post by: TRon on January 25, 2025, 06:26:57 pm
Ok, I'll explain and attach some screenshots.
Thank you. Nice and clear description.

Problem (for me) is that I do not run windows and the issue is non reproducible for me on Linux/GTK. I was unable to locate a mention of this particular problem in the bugtracker (but I might just as well have overlooked or misunderstood the bugtracker description(s)).

Therefor I am unable to guide you further on that topic other then to advise to try using a newer version of the IDE (either 3.8 or 4.0RC2) in order to see if the issue is still present.
Title: Re: Painting on a control - 3 questions: OnPaint, ImageList, program logic
Post by: majolika on January 25, 2025, 06:34:11 pm
try using a newer version of the IDE (either 3.8 or 4.0RC2)
The question is how I update properly? Fully uninstall and install new version? But how can I save all settings of IDE?
Title: Re: Painting on a control - 3 questions: OnPaint, ImageList, program logic
Post by: TRon on January 25, 2025, 06:51:38 pm
The question is how I update properly? Fully uninstall and install new version? But how can I save all settings of IDE?
That is a very good question because I am not familiar with the windows side of things (e.g. the installer).

It is quite possible to run different installations next to each other but as far as I know the windows version comes distributed only with the installer version. I have seen people mention that the installer automatically takes care of things (asking questions if you want to use the same configuration or not) but it is hearsay so don't take it for truth).

Personally I build Lazarus from source and "install" it manually.

In case you wish to preserve your configuration (something that Lazarus should ask if it uses you old settings) make sure to backup your Lazarus configuration directory just in case something does go amiss. It contains all the settings and in case having used OPM all your OPM installed components. You have to note that it is possible that once you have updated your old configuration files that it might happen you are not able to revert back these settings due to incompatibilities (new introduced features etc).

I am not sure what would be the best advise for your situation. Sorry.

edit: If you really rely on your current lazarus version for production then the safest would be to test a newer version inside a VM where it doesn't matter when things goes wrong.
Title: Re: Painting on a control - 3 questions: OnPaint, ImageList, program logic
Post by: majolika on January 25, 2025, 07:03:54 pm
In case you wish to preserve your configuration make sure to backup your Lazarus configuration directory
Thanks! This is how I should do it!

It would be nice if you help me with my new topic (https://forum.lazarus.freepascal.org/index.php/topic,69975.0.html).
TinyPortal © 2005-2018