Lazarus

Programming => Graphics and Multimedia => Graphics => Topic started by: apeoperaio on August 24, 2017, 12:22:26 pm

Title: [SOLVED] tcustomcontrol and paint
Post by: apeoperaio on August 24, 2017, 12:22:26 pm
I am developing a control derived from TCustomControl
I draw customly my control.
My control works well on win and linux but I have refreshing problems on osx.
Here a simple code reproducing this behaviours.
A form with a tcheckbox, when I change the checkbox selection I would like to change my control color.
Why it does not work on osx? What I am doing wrong?

Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls;
  9.  
  10. type
  11.  
  12.   { TMyDrawingControl }
  13.  
  14.   TMyDrawingControl = class(TCustomControl)
  15.   private
  16.     FRedGrid: boolean;
  17.     procedure SetRedGrid(AValue: boolean);
  18.   public
  19.     procedure Paint; override;
  20.     property RedGrid: boolean read FRedGrid write SetRedGrid;
  21.   end;
  22.   { TForm1 }
  23.  
  24.   TForm1 = class(TForm)
  25.     CheckBox1: TCheckBox;
  26.     MyDrawingControl: TMyDrawingControl;
  27.     procedure CheckBox1Change(Sender: TObject);
  28.     procedure Edit1Change(Sender: TObject);
  29.     procedure FormCreate(Sender: TObject);
  30.     procedure FormDestroy(Sender: TObject);
  31.   private
  32.     { private declarations }
  33.   public
  34.     { public declarations }
  35.   end;
  36.  
  37. var
  38.   Form1: TForm1;
  39.  
  40. implementation
  41.  
  42. {$R *.lfm}
  43.  
  44. procedure TMyDrawingControl.SetRedGrid(AValue: boolean);
  45. begin
  46.   if FRedGrid=AValue then Exit;
  47.   FRedGrid:=AValue;
  48.   Paint;
  49. end;
  50.  
  51. procedure TMyDrawingControl.Paint;
  52. var
  53.   x, y: Integer;
  54. begin
  55.   // Draws the background
  56.   Canvas.Pen.Color := clWhite;
  57.   Canvas.Rectangle(0, 0, Width, Height);
  58.  
  59.   // Draws squares
  60.   if RedGrid then
  61.      Canvas.Pen.Color := clRed
  62.   else
  63.     Canvas.Pen.Color := clBlack;
  64.   for x := 1 to 8 do
  65.     for y := 1 to 8 do
  66.       Canvas.Rectangle(Round((x - 1) * Width / 8), Round((y - 1) * Height / 8),
  67.         Round(x * Width / 8), Round(y * Height / 8));
  68.  
  69.   inherited Paint;
  70. end;
  71. { TForm1 }
  72.  
  73.  
  74. procedure TForm1.CheckBox1Change(Sender: TObject);
  75. begin
  76.   MyDrawingControl.RedGrid:= CheckBox1.Checked;
  77. end;
  78.  
  79. procedure TForm1.FormCreate(Sender: TObject);
  80. begin
  81.   MyDrawingControl := TMyDrawingControl.Create(Self);
  82.   MyDrawingControl.Align := alClient;
  83.   MyDrawingControl.Parent := Self;
  84.   MyDrawingControl.DoubleBuffered := True;
  85.   MyDrawingControl.RedGrid:= True;
  86.   CheckBox1.Checked:= True;
  87. end;
  88.  
  89. procedure TForm1.FormDestroy(Sender: TObject);
  90. begin
  91. end;
  92.  
  93. end.
  94.  
Title: Re: tcustomcontrol and paint
Post by: zeljko on August 24, 2017, 12:41:57 pm
As I can see you're painting outside of paint event. eg inside SetRedGrid() call Update() or Repaint() instead of Paint().
Title: Re: tcustomcontrol and paint
Post by: Blaazen on August 24, 2017, 01:09:37 pm
Invalidate; should be enough.
Code: Pascal  [Select][+][-]
  1. procedure TMyDrawingControl.SetRedGrid(AValue: boolean);
  2. begin
  3.   if FRedGrid=AValue then Exit;
  4.   FRedGrid:=AValue;
  5.   Invalidate;
  6. end;
Title: Re: tcustomcontrol and paint
Post by: apeoperaio on August 24, 2017, 02:39:34 pm
Invalidate works as expected.
But what does it means that I am painting outside paint event?
I inherited Paint method for drawing (all drawings are performed in paint procedure), then I call paint method, if necessary (e.g. some drawing settings changed).
What shoould I do to paint inside paint event?
Title: Re: tcustomcontrol and paint
Post by: apeoperaio on August 24, 2017, 04:32:12 pm
Invalidate works on windows and osx but not on linux.
I tried paint, invalidate and update. None works.
The problem is different from the previous one.
If I change the checkbox value the grid color is updated but if I resize the form, the new grid is painted over the previous one.
See attached screenshot
Title: Re: tcustomcontrol and paint
Post by: taazz on August 24, 2017, 05:07:47 pm
If I change the checkbox value the grid color is updated but if I resize the form, the new grid is painted over the previous one.
You have that backwards. From what I can see you have a secondary paint process that is not aware of the size change and it runs after the size aware drawing has finished.
Title: Re: tcustomcontrol and paint
Post by: apeoperaio on August 24, 2017, 05:12:14 pm
So, what I have to do to correctly paint my customcontrol?
Title: Re: tcustomcontrol and paint
Post by: taazz on August 24, 2017, 05:19:16 pm
I haven't looked at the code posted in the first post yet, my conclusions come from the screenshot alone, if the code posted is the complete code that creates the screenshot I'll take a look a bit later, if not start with posting the complete code that produced your screenshot.
Title: Re: tcustomcontrol and paint
Post by: apeoperaio on August 24, 2017, 05:37:09 pm
Yes, in the first post I pasted the whole source code of my sample form.
Line 48 I tried changing with invalidate, repaint, update.
Title: Re: tcustomcontrol and paint
Post by: taazz on August 24, 2017, 06:55:28 pm
I have build the code with lazarus 1.4.4 on open suse 13.2 (harlequin)(i586) for gtk2 replacing the call to paint with invalidate and everything works as expected see attached screenshot. So I have to ask for your versions for
1) linux
2)GTK
3)lazarus
4)FPC
if you have a default lazarus installation linux users might guess the fpc version the rest are mandatory. I'm sorry I do not have any newer linux distro installed to check farther
Title: Re: tcustomcontrol and paint
Post by: Blaazen on August 24, 2017, 07:01:32 pm
The point is that you cannot call Paint; directly (at least on Qt and Mac). All repainting is processed internally, Paint; is called by LCL (and widgetset) when needed. And when you call Invalidate; Update; Refresh; or Repaint; you are telling to LCL (and widgetset) that it IS needed.
Title: Re: tcustomcontrol and paint
Post by: apeoperaio on August 24, 2017, 07:19:48 pm
Ok, get the point about painting.
Using invalidate it works on mac (and win).
Anyway I got unexpected behaviour using linux:
Virtualbox with Ubuntu 16.10 64bit
lazarus 1.6.2 fpc 3.0.0

Title: Re: tcustomcontrol and paint
Post by: apeoperaio on August 24, 2017, 07:30:25 pm
same behaviour laz 1.4.4 fpc 2.6.4 virtual box ubuntu
Title: Re: tcustomcontrol and paint
Post by: taazz on August 24, 2017, 07:43:52 pm
it looks like partial redraw based on the area that GTK things it needs redrawing, an old region trick used extensively on windows. Just in case there is an lcl bug somewhere try setting doublebuffered to false
Code: Pascal  [Select][+][-]
  1. procedure TForm1.FormCreate(Sender: TObject);
  2. begin
  3.   MyDrawingControl := TMyDrawingControl.Create(Self);
  4.   MyDrawingControl.Align := alClient;
  5.   MyDrawingControl.Parent := Self;
  6.   MyDrawingControl.DoubleBuffered := False;//This has changed.
  7.   MyDrawingControl.RedGrid:= True;
  8.   CheckBox1.Checked:= True;
  9. end;
  10.  

EDIT:
Sorry about that but since I can't run my own tests I'm just dropping ideas here for testing.
first it looks like you have a problem with resize so the paint;/invalidate call in the oncheckboxchange event should not have any effect. Try after resizing changing the value on the checkbox and see if that solves the problem.
If it does then override the setbounds method and call invalidate after the call to inherited.
Title: Re: tcustomcontrol and paint
Post by: apeoperaio on August 25, 2017, 10:38:05 am
DoubleBuffered True/False has no effect.
The problem is related to resize, and yes changing the checkbox value solves the problem.
I overrided the setbounds method adding invalidate after the call to inherited and now it works properly on win/mac/linux.

Is it a bug? Or is it the correct behaviour? Why Linux acts differently?
Title: Re: tcustomcontrol and paint
Post by: GetMem on August 25, 2017, 11:28:33 am
Hi apeoperaio,

Add this to your custom control:
Code: Pascal  [Select][+][-]
  1.   //...
  2.  TMyDrawingControl = class(TCustomControl)
  3.   private
  4.     FRedGrid: boolean;
  5.     procedure SetRedGrid(AValue: boolean);
  6.   protected
  7.     procedure Resize; override; //<--this line
  8.   public
  9.     procedure Paint; override;
  10.     property RedGrid: boolean read FRedGrid write SetRedGrid;
  11.   end;
  12.  
  13. //...
  14.  
  15. procedure TMyDrawingControl.Resize;
  16. begin
  17.   Invalidate;
  18.   inherited Resize;
  19. end;
Title: Re: tcustomcontrol and paint
Post by: apeoperaio on August 25, 2017, 11:53:29 am
It works.

Actually I have two solutions, overriding SetBounds or Resize method and add Invalidate to that.
Which one should I choose?

Since I need it only for Linux, is it a bug?
Title: Re: tcustomcontrol and paint
Post by: taazz on August 25, 2017, 01:17:23 pm
It works.

Actually I have two solutions, overriding SetBounds or Resize method and add Invalidate to that.
Which one should I choose?
anyone setbounds is used to actually change the size and position of your control before exiting it call the resize method so the onresize event can be called. calling invalidate etiher on the begining or the end of that process makes no difference (on windows at least) it will be processed after the resize has finished.
Since I need it only for Linux, is it a bug?
No, I wouldn't characterize it as a bug, its widget set behavior they chose to do it that way for speed reasons you just need to inform the widget that you require a fill repaint.
Title: Re: tcustomcontrol and paint
Post by: apeoperaio on August 25, 2017, 03:14:04 pm
Thanks for the explanation!
TinyPortal © 2005-2018