[SOLVED] tcustomcontrol and paint lazarus / 39-Graphics ---------------------------------------------------------------------- apeoperaio · 2017-08-24 10:22 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][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i unit Unit1; {$mode objfpc}{$H+} interface uses  Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls; type   { TMyDrawingControl }   TMyDrawingControl = class(TCustomControl)  private    FRedGrid: boolean;    procedure SetRedGrid(AValue: boolean);  public    procedure Paint; override;    property RedGrid: boolean read FRedGrid write SetRedGrid;  end;  { TForm1 }   TForm1 = class(TForm)    CheckBox1: TCheckBox;    MyDrawingControl: TMyDrawingControl;    procedure CheckBox1Change(Sender: TObject);    procedure Edit1Change(Sender: TObject);    procedure FormCreate(Sender: TObject);    procedure FormDestroy(Sender: TObject);  private    { private declarations }  public    { public declarations }  end; var  Form1: TForm1; implementation {$R *.lfm} procedure TMyDrawingControl.SetRedGrid(AValue: boolean);begin  if FRedGrid=AValue then Exit;  FRedGrid:=AValue;  Paint;end; procedure TMyDrawingControl.Paint;var  x, y: Integer;begin  // Draws the background  Canvas.Pen.Color := clWhite;  Canvas.Rectangle(0, 0, Width, Height);   // Draws squares  if RedGrid then     Canvas.Pen.Color := clRed  else    Canvas.Pen.Color := clBlack;  for x := 1 to 8 do    for y := 1 to 8 do      Canvas.Rectangle(Round((x - 1) * Width / 8), Round((y - 1) * Height / 8),        Round(x * Width / 8), Round(y * Height / 8));   inherited Paint;end;{ TForm1 }  procedure TForm1.CheckBox1Change(Sender: TObject);begin  MyDrawingControl.RedGrid:= CheckBox1.Checked;end; procedure TForm1.FormCreate(Sender: TObject);begin  MyDrawingControl := TMyDrawingControl.Create(Self);  MyDrawingControl.Align := alClient;  MyDrawingControl.Parent := Self;  MyDrawingControl.DoubleBuffered := True;  MyDrawingControl.RedGrid:= True;  CheckBox1.Checked:= True;end; procedure TForm1.FormDestroy(Sender: TObject);beginend; end. ---------------------------------------------------------------------- zeljko · 2017-08-24 10:41 As I can see you're painting outside of paint event. eg inside SetRedGrid() call Update() or Repaint() instead of Paint(). ---------------------------------------------------------------------- Blaazen · 2017-08-24 11:09 Invalidate; should be enough. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i procedure TMyDrawingControl.SetRedGrid(AValue: boolean);begin  if FRedGrid=AValue then Exit;  FRedGrid:=AValue;  Invalidate;end; ---------------------------------------------------------------------- apeoperaio · 2017-08-24 12:39 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? ---------------------------------------------------------------------- apeoperaio · 2017-08-24 14:32 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 ---------------------------------------------------------------------- taazz · 2017-08-24 15:07 Quote from: apeoperaio on August 24, 2017, 04:32:12 pmIf 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. ---------------------------------------------------------------------- apeoperaio · 2017-08-24 15:12 So, what I have to do to correctly paint my customcontrol? ---------------------------------------------------------------------- taazz · 2017-08-24 15:19 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. ---------------------------------------------------------------------- apeoperaio · 2017-08-24 15:37 Yes, in the first post I pasted the whole source code of my sample form. Line 48 I tried changing with invalidate, repaint, update. ---------------------------------------------------------------------- taazz · 2017-08-24 16:55 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 ---------------------------------------------------------------------- Blaazen · 2017-08-24 17:01 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. ---------------------------------------------------------------------- apeoperaio · 2017-08-24 17:19 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 ---------------------------------------------------------------------- apeoperaio · 2017-08-24 17:30 same behaviour laz 1.4.4 fpc 2.6.4 virtual box ubuntu ---------------------------------------------------------------------- taazz · 2017-08-24 17:43 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][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i procedure TForm1.FormCreate(Sender: TObject);begin  MyDrawingControl := TMyDrawingControl.Create(Self);  MyDrawingControl.Align := alClient;  MyDrawingControl.Parent := Self;  MyDrawingControl.DoubleBuffered := False;//This has changed.  MyDrawingControl.RedGrid:= True;  CheckBox1.Checked:= True;end;  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. ---------------------------------------------------------------------- apeoperaio · 2017-08-25 08:38 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? ---------------------------------------------------------------------- balazsszekely · 2017-08-25 09:28 Hi apeoperaio, Add this to your custom control: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i   //... TMyDrawingControl = class(TCustomControl)  private    FRedGrid: boolean;    procedure SetRedGrid(AValue: boolean);  protected    procedure Resize; override; //<--this line  public    procedure Paint; override;    property RedGrid: boolean read FRedGrid write SetRedGrid;  end; //... procedure TMyDrawingControl.Resize;begin  Invalidate;  inherited Resize;end; ---------------------------------------------------------------------- apeoperaio · 2017-08-25 09:53 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? ---------------------------------------------------------------------- taazz · 2017-08-25 11:17 Quote from: apeoperaio on August 25, 2017, 11:53:29 amIt 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. Quote from: apeoperaio on August 25, 2017, 11:53:29 amSince 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. ---------------------------------------------------------------------- apeoperaio · 2017-08-25 13:14 Thanks for the explanation!