Page 1 of 2

What is InvalidateRect_() ?

Posted: Sat Sep 04, 2010 11:12 am
by charvista
I had a big problem: After a SetGadgetText(), the text is put in the gadget, but disappears as quickly as it was put.
The background is a graphical element (a gradient).
I solved the problem by adding InvalidateRect_() after each SetGadgetText().
What is InvalidateRect_()? I copied this from an existing code, but I would like to know what it exactly does, and what are the two parameters 0,1 ? And does ValidateRect_() also exist?
Thanks for clarify this mystery.

Code: Select all

    SetGadgetText(InfoGad(1),"Text1"):InvalidateRect_(GadgetID(InfoGad(1)),0,1)
    SetGadgetText(InfoGad(2),"Text2"):InvalidateRect_(GadgetID(InfoGad(2)),0,1)
    SetGadgetText(InfoGad(3),"Text3"):InvalidateRect_(GadgetID(InfoGad(3)),0,1)
    SetGadgetText(InfoGad(4),"Text4"):InvalidateRect_(GadgetID(InfoGad(4)),0,1)

Re: What is InvalidateRect_() ?

Posted: Sat Sep 04, 2010 11:39 am
by srod
InvalidateRect_() places an instruction for the control to repaint itself (and optionally erase it's background) into it's message queue. Specifically, a #WM_PAINT message is added to the queue.

The first parameter is a pointer to a rectangle structure giving the bounds of that part of the control you wish to invalidate and have repainted. This rectangle is added to the control's update rectangle/region. When a #WM_PAINT message is retrieved, then under normal circumstances, a paint handler will only be allowed to paint within the update rectangle etc. This means that you only need to repaint that part of the control which has specifically been invalidated, rather than paint the entire control etc.

If this parameter is set to null then the control's entire client-area is invalidated.

The second paramter is a boolean. Set to #True if you wish the control to erase it's own background at the beginning of it's painting routines etc. You would quite often set this to #False for custom controls in which the paint handler will handle the erasing itself etc.

Note that #WM_PAINT messages are retrieved only when there are no other messages in the message queue. That is, they are always left to last. To force an immediate repaint, follow InvalidateRect_() with UpdateWindow_() etc.

Yes there is a ValidateRect_() function. This is normally called automatically at the end of a paint handler (as part of the EndPaint_() function) and so you would not normally need to call this yourself.

Re: What is InvalidateRect_() ?

Posted: Sat Sep 04, 2010 12:00 pm
by charvista
Thank you, srod, for the detailed explanation which is of golden value.
InvalidateRect_() places an instruction for the control to repaint itself
If I understand well, both the background and the text are fighting against each other to get the display. If the text would disappear, it will reappear [when the message queue is idle]... Interesting.
So, I believe the use of InvalidateRect_() is correct in my case.

Re: What is InvalidateRect_() ?

Posted: Sat Sep 04, 2010 12:05 pm
by srod
charvista wrote:Thank you, srod, for the detailed explanation which is of golden value.
InvalidateRect_() places an instruction for the control to repaint itself
If I understand well, both the background and the text are fighting against each other to get the display. If the text would disappear, it will reappear [when the message queue is idle]... Interesting.
So, I believe the use of InvalidateRect_() is correct in my case.
Well, use of InvalidateRect_() in such cases as this is normally a 'last ditch resort' and shouldn't really be needed and will probably prove unreliable. Run the same program on another system (or another version of Windows) and you may well find that the background once again covers the text. I would suggest that there is perhaps something wrong with what you are doing or the approach you are taking.

Is the text gadget sitting on top of another control? Is it inside another control? (Note the distinction of being on top of as opposed to being inside.)

Re: What is InvalidateRect_() ?

Posted: Sat Sep 04, 2010 12:41 pm
by charvista
Well, I am almost always defining my windows in 'zones'.
I'll try to explain with some snippets of the real code.
At startup, after an OpenWindow(), I am creating the main zone, called A with the array ImA() containing the ImageNumber, ImageGadget and X,Y,W,H attributes:

Code: Select all

    Dim ImA.i(5)
    zBaseImage(ImA(),0,_BAR,_WW,_WH)
    zGradient(ImA(),3,0,0,_WW,_WH,$1F364E,$1F364E)
Then, I create another zone D over the zone A:

Code: Select all

    Dim ImD.i(5); zone for image's info
    ImAx=ImA(2)+910:ImAy=ImA(3)+20+450+20
    zBaseImage(ImD(),ImAx,ImAy,450,450)
    zGradient(ImD(),1,0,0,450,160,$70B5AB,$70B5AB)
    zGradient(ImD(),1,0,160,450,290,$70B5AB,$1F364E)
    Dim InfoGad(5)
    InfoGad(0)=TextGadget(#PB_Any,ImAx+10,ImAy+8,150,20,"")
    InfoGad(1)=TextGadget(#PB_Any,ImAx+300,ImAy+8,150,20,"")
    InfoGad(2)=TextGadget(#PB_Any,ImAx+10,ImAy+40,440,20,"")
    InfoGad(3)=TextGadget(#PB_Any,ImAx+10,ImAy+60,440,20,"")
    InfoGad(4)=TextGadget(#PB_Any,ImAx+10,ImAy+80,440,20,"")
    InfoGad(5)=TextGadget(#PB_Any,ImAx+10,ImAy+100,440,220,"")
    For i=0 To 5
        SetGadgetFont(InfoGad(i),FontID(_F01))
        SetGadgetColor(InfoGad(i),#PB_Gadget_BackColor,$70B5AB)
        SetGadgetColor(InfoGad(i),#PB_Gadget_FrontColor,$000000)
    Next
The zone D has some TextGadgets().
And when needed, the program calls a procedure that fills up the TextGadgets():

Code: Select all

    ; Info
    SetGadgetText(InfoGad(0),zStringGetItem(Rec,3,#SEP,1)):InvalidateRect_(GadgetID(InfoGad(0)),0,1)
    SetGadgetText(InfoGad(1),"#"+Mid(Key,1,6)):InvalidateRect_(GadgetID(InfoGad(1)),0,1)
    zLine(ImD(),0,30,450,30,$000000)
    SetGadgetText(InfoGad(2),zStringGetItem(Rec,2,#SEP,1)):InvalidateRect_(GadgetID(InfoGad(2)),0,1)
    SetGadgetText(InfoGad(3),zStringGetItem(Rec,4,#SEP,1)):InvalidateRect_(GadgetID(InfoGad(3)),0,1)
    SetGadgetText(InfoGad(4),zStringGetItem(Rec,5,#SEP,1)):InvalidateRect_(GadgetID(InfoGad(4)),0,1)
    SetGadgetText(InfoGad(5),zStringGetItem(Rec,6,#SEP,1)):InvalidateRect_(GadgetID(InfoGad(5)),0,1)
The function zBaseImage() creates an empty image, which is then filled with a gradient.
This is done twice (zone A and then zone D on zone A) before the TextGadget() is placed. And the zone D is then left untouched. I really do not see why I need InvalidateRect_(), as there is also a zone P with the same kind of TextGadget(), where I do not need InvalidateRect_(), hence my frustration for the zone D. The only difference is that the TextGadget() in the zone P prints a value upon creation.
There is no other gadget on top nor inside the TextGadget() in zone D.
Would it be possible that a text on image2 on image1 might cause problems?

Re: What is InvalidateRect_() ?

Posted: Sat Sep 04, 2010 12:48 pm
by srod
Well, not sure I can really follow that.

If you are placing text gadgets on top of an Image gadget then... expect these problems! Consider giving the #WS_CLIPSIBLINGS style to the image gadgets and the text gadgets.

Why not just draw directly onto the images rather than use text gadgets?

Re: What is InvalidateRect_() ?

Posted: Sat Sep 04, 2010 1:07 pm
by charvista
If you are placing text gadgets on top of an Image gadget then... expect these problems!
I see them! :mrgreen:
Why not just draw directly onto the images rather than use text gadgets?
I have thought to do so with DrawText() but the text can change, and drawing text over text does not remove the previous text...

Re: What is InvalidateRect_() ?

Posted: Sat Sep 04, 2010 1:29 pm
by srod
Yes you'd have to start afresh each time you wished to draw the text etc.

An alternative is to place the text gadgets inside the image gadgets - that is make them child gadgets.

Simply make use of UseGadgetList(GadgetID(#ImageGadget)) immediately prior to creating the text gadgets.

Re: What is InvalidateRect_() ?

Posted: Sat Sep 04, 2010 6:20 pm
by WilliamL
An alternative is to place the text gadgets inside the image gadgets - that is make them child gadgets.

Simply make use of UseGadgetList(GadgetID(#ImageGadget)) immediately prior to creating the text gadgets.
Hey srod!

Example... please!

Re: What is InvalidateRect_() ?

Posted: Sat Sep 04, 2010 6:41 pm
by srod

Code: Select all

UseJPEGImageDecoder()

If OpenWindow(0, 0, 0, 300, 300, "ImageGadget", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
  If LoadImage(0, "girl.jpg")
    hWnd = ImageGadget(0,  10, 10, 0, 0, ImageID(0))
    UseGadgetList(hWnd)
      TextGadget(1, 10, 10, 80, 20, "Heyho!")
  EndIf
  Repeat : Until WaitWindowEvent() = #PB_Event_CloseWindow
EndIf

Re: What is InvalidateRect_() ?

Posted: Sat Sep 04, 2010 8:10 pm
by WilliamL
[deleted]

Re: What is InvalidateRect_() ?

Posted: Sat Sep 04, 2010 9:05 pm
by charvista
Works for me:

Code: Select all

    UsePNGImageDecoder()

    If OpenWindow(0, 0, 0, 300, 300, "ImageGadget", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
        If LoadImage(0, #PB_Compiler_Home +"Examples/Sources/Data/flare.png")
            hWnd = ImageGadget(0,  10, 10, 0, 0, ImageID(0))
            UseGadgetList(hWnd)
            TextGadget(1, 10, 10, 80, 20, "Heyho!")
        Else
            MessageRequester("Error","Image Not Found"):End
        EndIf
        Repeat : Until WaitWindowEvent() = #PB_Event_CloseWindow
    EndIf

Re: What is InvalidateRect_() ?

Posted: Sat Sep 04, 2010 9:24 pm
by charvista
@srod
This afternoon (and begin of the night) I worked out to create a zTextLabel() function, that does about the same as the TextGadget(), but using DrawText(), and clearing the gadget before writing a new value.
It works already well, and tomorrow I will try to combine the functions zTextLabel() and zPrint() to make life easier...

Code: Select all

Global _F01=LoadFont(#PB_Any,"MS Sans Serif",8)

Procedure.i zBaseImage(Array Im.i(1),X.i,Y.i,W.i,H.i)
    ImCre=CreateImage(#PB_Any,W,H)
    ImGad=ImageGadget(#PB_Any,X,Y,W,H,ImageID(ImCre))
    DisableGadget(ImGad,#True)
    InvalidateRect_(GadgetID(ImGad),0,1)
    Im(0)=ImCre
    Im(1)=ImGad
    Im(2)=X
    Im(3)=Y
    Im(4)=W
    Im(5)=H
EndProcedure

Procedure.i zTextLabel(Array Im.i(1),Array ImUnder.i(1),X.i,Y.i,W.i,H.i)
    If Im(1)=0
        Im(0)=CreateImage(#PB_Any,W,H)
        Im(1)=ImageGadget(#PB_Any,X,Y,W,H,ImageID(Im(0)))
    EndIf
    Grab=GrabImage(ImUnder(0),#PB_Any,X,Y,W,H)
    StartDrawing(ImageOutput(Im(0)))
        DrawImage(ImageID(Grab),0,0,ImUnder(2)+W,ImUnder(3)+H)
    StopDrawing()
    SetGadgetState(ImUnder(1),ImageID(ImUnder(0)))
    DisableGadget(Im(1),#True)
    InvalidateRect_(GadgetID(Im(1)),0,1)
    Im(2)=X
    Im(3)=Y
    Im(4)=W
    Im(5)=H
EndProcedure

Procedure zPrint(Array Im.i(1),X.i,Y.i,Text.s,FontNumber.i=-1,TxColor.i=$000000,Shadow.i=0)
    If FontNumber=-1
        FontNumber=_F01
    EndIf
    StartDrawing(ImageOutput(Im(0)))
        DrawingMode(#PB_2DDrawing_Transparent)
        DrawingFont(FontID(FontNumber.i))
        If Shadow=2
            DrawText(X+2,Y+2,Text,$202020); double shadow always dark gray
        EndIf
        If Shadow=2 Or Shadow=1
            DrawText(X+1,Y+1,Text,$000000) ;shadows always black
        EndIf
        DrawText(X,Y,Text.s,TxColor)
    StopDrawing()
    SetGadgetState(Im(1),ImageID(Im(0)))    
EndProcedure

Win=OpenWindow(#PB_Any, 0, 0, 600, 500, "Drawing Example", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
Dim ImA(5)
zBaseImage(ImA(),0,0,600,500)
StartDrawing(ImageOutput(ImA(0)))
    Box(0, 0, 600, 500, $FFFFFF)
    DrawingMode(#PB_2DDrawing_Gradient)      
    BackColor($0000FF)
    GradientColor(0.4, $00FFFF)
    GradientColor(0.6, $FFFF00)
    FrontColor($FF0000)
    LinearGradient(0, 0, 600, 500)    
    Box(0,0,600,500)   
StopDrawing() 
SetGadgetState(ImA(1),ImageID(ImA(0)))

Dim ImL1(5)
zTextLabel(ImL1(),ImA(),10,10,400,15)
zPrint(ImL1(),0,0,"Hello.... I am Charvista.")
Delay(3000)
zTextLabel(ImL1(),ImA(),10,10,400,15)
zPrint(ImL1(),0,0,"Thank you, srod, for your valuable help")

Repeat : Until WaitWindowEvent() = #PB_Event_CloseWindow

Re: What is InvalidateRect_() ?

Posted: Sun Sep 05, 2010 9:58 pm
by charvista
Here is the final program as promised... Without InvalidateRect_() and with a DrawText() where the background is cleared first. And this only with one function: zPrint().
Hope it will be useful to you.
Cheers, Charvista

Code: Select all

Global _F01=LoadFont(#PB_Any, "Tahoma", 8, #PB_Font_Bold)

Procedure.i zBaseImage(Array Im.i(1),X.i,Y.i,W.i,H.i)
    ImCre=CreateImage(#PB_Any,W,H)
    ImGad=ImageGadget(#PB_Any,X,Y,W,H,ImageID(ImCre))
    DisableGadget(ImGad,#True)
    ;InvalidateRect_(GadgetID(ImGad),0,1)
    Im(0)=ImCre
    Im(1)=ImGad
    Im(2)=X
    Im(3)=Y
    Im(4)=W
    Im(5)=H
EndProcedure

Procedure.i zBaseImageGrabbed(Array Im.i(1),Array ImUnder.i(1),X.i,Y.i,W.i,H.i); this also plays the role of creation of a Text label
    ImCre=CreateImage(#PB_Any,W,H)
    ImGad=ImageGadget(#PB_Any,X,Y,W,H,ImageID(ImCre))
    Grab=GrabImage(ImUnder(0),#PB_Any,X,Y,W,H)
    StartDrawing(ImageOutput(ImCre))
        DrawImage(ImageID(Grab),0,0,ImUnder(2)+W,ImUnder(3)+H)
    StopDrawing()
    DisableGadget(ImGad,#True)
    ;InvalidateRect_(GadgetID(ImGad),0,1)
    Im(0)=ImCre
    Im(1)=ImGad
    Im(2)=X
    Im(3)=Y
    Im(4)=W
    Im(5)=H
EndProcedure

Procedure zPrint(Array Im.i(1),Array ImUnder.i(1),Text.s,FontNumber.i=-1,TxColor.i=$000000,Shadow.i=0)
    If FontNumber=-1
        FontNumber=_F01
    EndIf
    Grab=GrabImage(ImUnder(0),#PB_Any,Im(2),Im(3),Im(4),Im(5))
    StartDrawing(ImageOutput(Im(0)))
        DrawImage(ImageID(Grab),0,0,Im(4),Im(5))
    StopDrawing()
    SetGadgetState(ImUnder(1),ImageID(ImUnder(0)))
    DisableGadget(Im(1),#True)
    ;InvalidateRect_(GadgetID(Im(1)),0,1)
    StartDrawing(ImageOutput(Im(0)))
        DrawingMode(#PB_2DDrawing_Transparent)
        DrawingFont(FontID(FontNumber.i))
        If Shadow=2
            DrawText(X+2,Y+2,Text,$202020); double shadow always dark gray
        EndIf
        If Shadow=2 Or Shadow=1
            DrawText(X+1,Y+1,Text,$000000) ;shadows always black
        EndIf
        DrawText(X,Y,Text.s,TxColor)
    StopDrawing()
    SetGadgetState(Im(1),ImageID(Im(0)))    
EndProcedure


Win=OpenWindow(#PB_Any, 0, 0, 600, 500, "Drawing Example", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
Dim ImA(5)
zBaseImage(ImA(),0,0,600,500)
StartDrawing(ImageOutput(ImA(0)))
    Box(0, 0, 600, 500, $FFFFFF)
    DrawingMode(#PB_2DDrawing_Gradient)      
    BackColor($0000FF)
    GradientColor(0.4, $00FFFF)
    GradientColor(0.6, $FFFF00)
    FrontColor($FF0000)
    LinearGradient(0, 0, 600, 500)    
    Box(0,0,600,500)   
StopDrawing() 
SetGadgetState(ImA(1),ImageID(ImA(0)))
Dim ImL1(5)
zBaseImageGrabbed(ImL1(),ImA(),210,210,590,15)
zPrint(ImL1(),ImA(),"A loop 1..1000 will be launched...")
Delay(3000)
For i=1 To 1000
    zPrint(ImL1(),ImA(),"Program Loop is now at "+Str(i))
Next

Repeat : Until WaitWindowEvent() = #PB_Event_CloseWindow

Re: What is InvalidateRect_() ?

Posted: Mon Sep 06, 2010 12:41 am
by RASHAD
@charvista Hi

Next snippet will give you some benefits:
1- Draw you Image direct to the window to avoid Gadgets Z-Order Problems
2- Run the loop through a Thread to avoid waiting for the loop to finish
3- The ability to pause resume the loop at any time
4- The shadow effect as been seen in your code

Code: Select all

Global Dim ImA(5),color,Win

Procedure LoopThread(Parameter)
  Text1$ = "Loop number :  "
  LoadFont(0, "Arial", 14)
  Repeat
    InvalidateRect_(WindowID(win),0,#True)
        StartDrawing( WindowOutput(Win))
          DrawingMode(#PB_2DDrawing_Transparent)
          DrawingFont(FontID(0))    
          DrawText(190,225,Text1$+Str(i),$000000)
          DrawText(190,225,Text1$+Str(i),$000000)
          DrawText(188,223,Text1$+Str(i),$FFFFFF)
          ;DrawText(188,223,Text1$+Str(i),$FFFFFF)
          Delay(50)
        StopDrawing() 
        i = i +1
  ForEver
EndProcedure  

Procedure.i zBaseImage(Array Im.i(1),X.i,Y.i,W.i,H.i)
    ImCre=CreateImage(#PB_Any,W,H)
    Im(0)=ImCre
    Im(1)=ImGad
    Im(2)=X
    Im(3)=Y
    Im(4)=W
    Im(5)=H
EndProcedure

Win=OpenWindow(#PB_Any, 0, 0, 600, 500, "Drawing Example", #PB_Window_SystemMenu | #PB_Window_ScreenCentered|#PB_Window_Invisible)
zBaseImage(ImA(),0,0,600,500)
ButtonGadget(3,10,470,80,24,"Start/Stop",#PB_Button_Toggle)
StartDrawing(ImageOutput(ImA(0)))
    Box(0, 0, 600, 500, $FFFFFF)
    DrawingMode(#PB_2DDrawing_Gradient)      
    BackColor($0000FF)
    GradientColor(0.4, $00FFFF)
    GradientColor(0.6, $FFFF00)
    FrontColor($FF0000)
    LinearGradient(0, 0, 600, 500)    
    Box(0,0,600,500)   
StopDrawing()
hBrush = CreatePatternBrush_(ImageID(ImA(0)))
SetClassLongPtr_(WindowID(Win), #GCL_HBRBACKGROUND, hBrush)
HideWindow(Win,0)

Repeat

  Select WaitWindowEvent()      
      
      Case #PB_Event_CloseWindow
      Q = 1
       
      Case #WM_CLOSE      
      
      Case #PB_Event_Gadget
          Select EventGadget()
            Case 3
              If  GetGadgetState(3) = 1
                If Not IsThread(Thread)
                  Thread=CreateThread(@LoopThread(),100)
                  Run = 1
                ElseIf Run = 0
                  Run = 1
                  ResumeThread(Thread)
                EndIf
              ElseIf GetGadgetState(3) = 0 And Run = 1  
                  Run = 0
                  PauseThread(Thread)
              EndIf              
            EndSelect
        EndSelect
Until q = 1

Have a good day sir