[Solved] SplitterGadget redraw issue

Just starting out? Need help? Post your questions and find answers here.
Dude
Addict
Addict
Posts: 1907
Joined: Mon Feb 16, 2015 2:49 pm

Re: SplitterGadget redraw issue

Post by Dude »

Okay, I finally got some REPRODUCIBLE code! :D

Try this with #EnablePanel set to 1, then 0, to see what I mean.

Why would putting the gadgets on a panel, make the second listicongadget flicker so bad? Without the panel, there is NO flickering. :shock:

Code: Select all

#EnablePanel=1 ; If set to 1, it makes ListIconGadget #2 flicker badly when window is resized vertically!

OpenWindow(0, 200, 200, 230, 180, "SplitterGadget", #PB_Window_SystemMenu | #PB_Window_MaximizeGadget | #PB_Window_SizeGadget)

CreateStatusBar(0,WindowID(0))

CompilerIf #EnablePanel=1
  PanelGadget(0, 10, 10, 210, 160)
  AddGadgetItem(0,-1,"Tab 1")
CompilerEndIf

ListIconGadget(1, 10, 10, 200, 150, "1", 200)
For i=1 To 100
  AddGadgetItem(1, -1, Str(i))
Next

ListIconGadget(2, 10, 10, 200, 150, "2", 200)
For i=1 To 100
  AddGadgetItem(2, -1, Str(i))
Next

SplitterGadget(3, 10, 10, 180, 110, 1, 2, #PB_Splitter_Vertical | #PB_Splitter_Separator | #PB_Splitter_FirstFixed)

CompilerIf #EnablePanel=1
  AddGadgetItem(0,-1,"Tab 2")
  CloseGadgetList()
CompilerEndIf

Procedure ResizeApp()
  appx=WindowX(0)
  appy=WindowY(0)
  appw=WindowWidth(0)
  apph=WindowHeight(0)
  CompilerIf #EnablePanel=1
    ResizeGadget(0,#PB_Ignore,#PB_Ignore,appw-20,apph-40)
  CompilerEndIf
  ResizeGadget(3,#PB_Ignore,#PB_Ignore,appw-45,apph-90)
EndProcedure

Procedure Callback(hWnd,Message,wParam,lParam)
  result=#PB_ProcessPureBasicEvents
  If Message=#WM_WINDOWPOSCHANGED
    ResizeApp()
  EndIf
  ProcedureReturn result
EndProcedure

SetWindowCallback(@Callback())

ResizeApp() ; Set initial size.

Repeat : Until WaitWindowEvent() = #PB_Event_CloseWindow
RASHAD
PureBasic Expert
PureBasic Expert
Posts: 4946
Joined: Sun Apr 12, 2009 6:27 am

Re: SplitterGadget redraw issue

Post by RASHAD »

As a guide line

Code: Select all

#EnablePanel=1 ; If set to 1, it makes ListIconGadget #2 flicker badly when window is resized vertically!

OpenWindow(0, 200, 200, 230, 180, "SplitterGadget", #PB_Window_SystemMenu | #PB_Window_MaximizeGadget | #PB_Window_SizeGadget)

CreateStatusBar(0,WindowID(0))

CompilerIf #EnablePanel=1
  PanelGadget(0, 10, 10, 210, 140)
    AddGadgetItem(0,-1,"Tab 1")

    ListIconGadget(1, 0, 0, 0, 150, "1", 200, #PB_ListIcon_FullRowSelect      )
    For i=1 To 100
      AddGadgetItem(1, -1, Str(i))
    Next
    
    ListIconGadget(2, 0, 0, 0, 150, "2", 200, #PB_ListIcon_FullRowSelect      )
    For i=1 To 100
      AddGadgetItem(2, -1, Str(i))
    Next
    
    SplitterGadget(3, 10, 10, 200, 115, 1, 2, #PB_Splitter_Vertical | #PB_Splitter_Separator | #PB_Splitter_FirstFixed)
  CloseGadgetList()
CompilerEndIf

Procedure Callback(hWnd,Message,wParam,lParam)
  result=#PB_ProcessPureBasicEvents
  If Message=#WM_EXITSIZEMOVE
    ResizeGadget(0,10,10,WindowWidth(0)-20,WindowHeight(0)-40)
    ResizeGadget(3,GadgetX(0),GadgetY(0),GadgetWidth(0)-20,WindowHeight(0))
  EndIf
  ProcedureReturn result
EndProcedure

SetWindowCallback(@Callback())

Repeat

 Select WaitWindowEvent()
  Case #PB_Event_CloseWindow
    Quit = 1
    
  Case #PB_Event_Gadget
    Select EventGadget()
      Case 3
        InvalidateRect_(GadgetID(2),0,1)
    EndSelect
  EndSelect    
Until Quit = 1

Egypt my love
Dude
Addict
Addict
Posts: 1907
Joined: Mon Feb 16, 2015 2:49 pm

Re: SplitterGadget redraw issue

Post by Dude »

RASHAD wrote:#WM_EXITSIZEMOVE
But that stops my GUI resizing in real-time with the user. I can't do that. I want the gadgets to resize in accordance with the user resizing the window, just like it does in my code with #EnablePanel set to 0; but without the gadget flickering.

[Edit] SOLVED! This works with NO flicker now - woohoo! :) Now to compare it to my app to see why my app still flickers.

Code: Select all

#EnablePanel=1

OpenWindow(0, 200, 200, 230, 180, "SplitterGadget", #PB_Window_SystemMenu | #PB_Window_MaximizeGadget | #PB_Window_SizeGadget)

SmartWindowRefresh(0,#True)

CreateStatusBar(0,WindowID(0))

CompilerIf #EnablePanel=1
  PanelGadget(0, 10, 10, 210, 160)
  AddGadgetItem(0,-1,"Tab 1")
CompilerEndIf

ListIconGadget(1, 10, 10, 200, 150, "1", 200)
For i=1 To 100
  AddGadgetItem(1, -1, Str(i))
Next

ListIconGadget(2, 10, 10, 200, 150, "2", 200)
For i=1 To 100
  AddGadgetItem(2, -1, Str(i))
Next

SplitterGadget(3, 10, 10, 180, 110, 1, 2, #PB_Splitter_Vertical | #PB_Splitter_Separator | #PB_Splitter_FirstFixed)

CompilerIf #EnablePanel=1
  AddGadgetItem(0,-1,"Tab 2")
  CloseGadgetList()
CompilerEndIf

Procedure ResizeApp()
  appw=WindowWidth(0)
  apph=WindowHeight(0)
  CompilerIf #EnablePanel=1
    ResizeGadget(0,#PB_Ignore,#PB_Ignore,appw-20,apph-40)
  CompilerEndIf
  ResizeGadget(3,#PB_Ignore,#PB_Ignore,appw-45,apph-90)
EndProcedure

Procedure Callback(hWnd,Message,wParam,lParam)
  result=#PB_ProcessPureBasicEvents
  If Message=#WM_SIZING Or Message=#WM_WINDOWPOSCHANGED
    ResizeApp()
  EndIf
  ProcedureReturn result
EndProcedure

SetWindowCallback(@Callback())

ResizeApp() ; Set initial size.

Repeat : Until WaitWindowEvent() = #PB_Event_CloseWindow
RASHAD
PureBasic Expert
PureBasic Expert
Posts: 4946
Joined: Sun Apr 12, 2009 6:27 am

Re: [Solved] SplitterGadget redraw issue

Post by RASHAD »

Not quit well
Just delete CreateStatusBar(0,WindowID(0)) and you will be OK
Egypt my love
Dude
Addict
Addict
Posts: 1907
Joined: Mon Feb 16, 2015 2:49 pm

Re: [Solved] SplitterGadget redraw issue

Post by Dude »

RASHAD wrote:Just delete CreateStatusBar(0,WindowID(0)) and you will be OK
My app uses a statusbar, so I can't delete it.
PureLust
Enthusiast
Enthusiast
Posts: 477
Joined: Mon Apr 16, 2007 3:57 am
Location: Germany, NRW

Re: SplitterGadget redraw issue

Post by PureLust »

Dude wrote:[Edit] SOLVED! This works with NO flicker now - woohoo! :) Now to compare it to my app to see why my app still flickers.
Your latest Version does not flicker (because of SmartWindowRefresh()), but is has a lot of redraw-issues when you resize the Window quickly.

As I recommended before - try DeFlicker.

I've included the DeFlicker-Module into your test-code, so you will have a full working version to see the difference.

Code: Select all

#DeFlicker_NO			= 1
#DeFlicker_Region		= 2
#DeFlicker_Top16		= 16
#DeFlicker_Top20		= 20
#DeFlicker_Full		= 1000

DeclareModule      DeFlicker

	Declare   StartResize(Window)
	Declare   RefreshWindow(Window)
	Declare   EndResize(RefreshWindow=#True)
   
	Declare   GetGadgetTypeDeFlicker(GadgetType)
	Declare   SetGadgetTypeDeFlicker(GadgetType, RefreshType)

	Declare   GetGadgetDeFlicker(Gadget)
	Declare   SetGadgetDeFlicker(Gadget, RefreshType, xPos=#PB_Ignore, yPos=#PB_Ignore, Width=#PB_Ignore, Height=#PB_Ignore)

EndDeclareModule
Module   DeFlicker
   
   EnableExplicit
   
   #DeFlicker_NO			= 1
   #DeFlicker_Region		= 2
   #DeFlicker_Top16		= 16
   #DeFlicker_Top20		= 20
   #DeFlicker_Full		= 1000
      
   Structure   GadgetDetails_Struct
      GadgetID.i
      GadgetNumber.i
      GadgetType.i
      left.l
      top.l
      right.l
      bottom.l
   EndStructure
   Structure   UserDefined_DeFlickerType_Struct
      GadgetNumber.i
      DeFlicker_Type.l
      xPos.w
      yPos.w
      Width.w
      Height.w
   EndStructure
   
   #MaxGadgetTypes = 40
   Global   Dim GadgetType_Default.l(#MaxGadgetTypes)
   Global   NewList   CallBackGadgetList.GadgetDetails_Struct()
   Global   NewList   UserDefined_DeFlickerType.UserDefined_DeFlickerType_Struct()
   Global   LastResizedWindow
   Global 	DeflickerLevel
   Define   n
   
   For n = 0 To #MaxGadgetTypes
      GadgetType_Default(n) = #DeFlicker_Full								; Standard für alle Gadgets ist erst einmal 'Full'-Deflicker
   Next
   
   GadgetType_Default(#PB_GadgetType_Frame)		= #DeFlicker_Top16	; Jetzt werden einige Ausnamen voreingestellt
   GadgetType_Default(#PB_GadgetType_Container)	= #DeFlicker_NO
   GadgetType_Default(#PB_GadgetType_Splitter)	= #DeFlicker_NO
   GadgetType_Default(#PB_GadgetType_Panel)		= #DeFlicker_Full
   
   Procedure   GetGadgetList_Callback(hwnd,*WinPos.POINT)				; interne Routine - CallBack zum Ermitteln der Gadgets-Liste
      
      ; CallBack zur Erstellung einer LinkedListe mit Details zu allen Gadgets in einem Window
      
      Protected   ActPBGadget, GadgetPos.RECT
      
      ActPBGadget = GetProp_(hwnd, "PB_ID")												; Gadget# des PB-Gadgets
      
      If IsGadget(ActPBGadget) And GadgetID(ActPBGadget) = hwnd					; Checken ob die ermittelte Gadget# aus ok ist
         If AddElement(CallBackGadgetList())
            CallBackGadgetList()\GadgetID			= hwnd
            CallBackGadgetList()\GadgetNumber	= ActPBGadget
            CallBackGadgetList()\GadgetType		= GadgetType(ActPBGadget)
            GetWindowRect_(hwnd, @GadgetPos)															; Desktop-Position des Gadgets ermitteln
            CallBackGadgetList()\left				= GadgetPos\left		- *WinPos\x			; Gadgetposition auf Position im Fenster umrechnen
            CallBackGadgetList()\top				= GadgetPos\top		- *WinPos\y			;       ''
            CallBackGadgetList()\right				= GadgetPos\right		- *WinPos\x			;       ''
            CallBackGadgetList()\bottom			= GadgetPos\bottom	- *WinPos\y			;       ''
         EndIf
      EndIf
      
      ProcedureReturn #True
      
   EndProcedure
   Procedure   GetGadgetList(Window, List GadgetList.GadgetDetails_Struct())   ; interne Routine zum Erstellen einer Liste mit allen Gadgets
      
      ; Ermittelt Details aller PB-Gadgets in einem Fenster und gibt diese in einer LinkedList zurück
      
      Protected   WinPos.POINT
      
      If IsWindow(Window)
         WinPos\x = WindowX(Window, #PB_Window_InnerCoordinate)	; Window-Position ermitteln, dami diese an den CallBack übergeben werden kann
         WinPos\y = WindowY(Window, #PB_Window_InnerCoordinate)
        
         ClearList(CallBackGadgetList())														; LinkedList des CallBacks löschen
         EnumChildWindows_(WindowID(Window),@GetGadgetList_Callback(),@WinPos)	; Alle Gadgets ermitteln und per CallBack auswerten
         CopyList(CallBackGadgetList(), GadgetList())										; Die vom CallBack erstellte LinkedListe auf die eigentliche Liste kopieren
      Else
         Debug "Window Nr."+Str(Window)+" konnte nicht gefunden werden."
      EndIf
      
      ProcedureReturn   ListSize(GadgetList())
      
   EndProcedure
   
   Procedure   StartResize(Window)
   	If IsWindow(Window)
   		If DeflickerLevel = 0
   			SendMessage_(WindowID(Window),#WM_SETREDRAW,#False,0)
   			LastResizedWindow = Window
   		EndIf
;    		DeflickerLevel + 1
      Else
         Debug "Window Nr."+Str(Window)+" nicht gefunden."
      EndIf
   EndProcedure
   Procedure   RefreshWindow(Window)
      
      If IsWindow(Window)
         
         Protected   ps.PAINTSTRUCT
         Protected   Validate.RECT
         Protected   NewList GadgetList.GadgetDetails_Struct()
         Protected   hWnd.i = WindowID(Window)
         Protected   WinRect.rect
         Protected   ActDeFlickerType
         Protected   ClearEventLoop = #True
         
         GetGadgetList(Window, GadgetList())
         
         GetClientRect_(hWnd, @WinRect)
         InvalidateRect_(hWnd,WinRect,1)
         
         ForEach GadgetList()
            
            If GadgetList()\GadgetType = #PB_GadgetType_Panel
               ClearEventLoop = #True
            EndIf
            
            ActDeFlickerType   = GadgetType_Default(GadgetList()\GadgetType)
            
            ForEach UserDefined_DeFlickerType()
               If UserDefined_DeFlickerType()\GadgetNumber = GadgetList()\GadgetNumber
                  ActDeFlickerType = UserDefined_DeFlickerType()\DeFlicker_Type
                  Break
               EndIf
            Next
            
            Select ActDeFlickerType
                  
               Case   #DeFlicker_Full            ;   Gadget-Bereich wird komplett 'DeFlickered'
                  
                  ValidateRect_(hWnd, @GadgetList()\left)
                  
               Case   #DeFlicker_Region
                  
                  Validate\left		= GadgetList()\left	+ UserDefined_DeFlickerType()\xPos
                  Validate\top		= GadgetList()\top	+ UserDefined_DeFlickerType()\yPos
                  Validate\right		= GadgetList()\left	+ UserDefined_DeFlickerType()\Width
                  Validate\bottom	= GadgetList()\top	+ UserDefined_DeFlickerType()\Height
                  
               Case   #DeFlicker_Region+1 To #DeFlicker_Full-1      ;  Nur oberen Teil des Gadgets 'DeFlickern'
                  
                  Validate\left		= GadgetList()\left + 8
                  Validate\top		= GadgetList()\top
                  Validate\right		= GadgetList()\right - 8
                  Validate\bottom	= GadgetList()\top + ActDeFlickerType - 1
                  
                  If GadgetList()\GadgetType = #PB_GadgetType_Frame
                     If StartDrawing(WindowOutput(Window))
                        DrawingFont(GetGadgetFont(GadgetList()\GadgetNumber))
                        Validate\right		= GadgetList()\left + 8 + TextWidth(GetGadgetText(GadgetList()\GadgetNumber))
                        Validate\bottom	= GadgetList()\top + TextHeight(GetGadgetText(GadgetList()\GadgetNumber))
                        If Validate\right > GadgetList()\right : Validate\right = GadgetList()\right : EndIf
                        StopDrawing()
                     EndIf
                  EndIf
                  ValidateRect_(hWnd, @Validate)
               Default
                  
                  ; No DeFlicker at all
                  
            EndSelect
            
         Next
         
         BeginPaint_(hWnd, ps.PAINTSTRUCT)
         EndPaint_(hWnd, ps.PAINTSTRUCT)
         RedrawWindow_(hWnd,#Null,#Null,#RDW_INVALIDATE);
         
         CompilerIf #PB_Compiler_Debugger         ; need this Trick to get special Gadgets refreshed (like PanelGadget)
            DisableDebugger
            If ClearEventLoop
               While WindowEvent() : Wend
            EndIf
            EnableDebugger
         CompilerElse
            If ClearEventLoop
               While WindowEvent() : Wend
            EndIf
         CompilerEndIf

      Else
         Debug "Window Nr."+Str(Window)+" nicht gefunden."
      EndIf
   EndProcedure
   Procedure   EndResize(RefreshWindow=#True)
;    	If DeflickerLevel > 0 : DeflickerLevel - 1 : EndIf
   	If DeflickerLevel = 0
   		SendMessage_(WindowID(LastResizedWindow),#WM_SETREDRAW,#True,0)
   		If RefreshWindow : RefreshWindow(LastResizedWindow): EndIf
   	EndIf
   EndProcedure
   
   Procedure   GetGadgetTypeDeFlicker(GadgetType)
      If GadgetType >= 0 And GadgetType <= #MaxGadgetTypes
         ProcedureReturn GadgetType_Default(GadgetType)
      EndIf
   EndProcedure
   Procedure   SetGadgetTypeDeFlicker(GadgetType, RefreshType)
      If GadgetType >= 0 And GadgetType <= #MaxGadgetTypes And RefreshType >= #DeFlicker_NO And RefreshType <= #DeFlicker_Full And RefreshType <> #DeFlicker_Region
         GadgetType_Default(GadgetType) = RefreshType
         ProcedureReturn #True
      EndIf
   EndProcedure
   
   Procedure   GetGadgetDeFlicker(Gadget)
      ForEach UserDefined_DeFlickerType()
         If UserDefined_DeFlickerType()\GadgetNumber = Gadget
            ProcedureReturn UserDefined_DeFlickerType()\DeFlicker_Type
         EndIf
      Next
   EndProcedure
   Procedure   SetGadgetDeFlicker(Gadget, RefreshType, xPos=#PB_Ignore, yPos=#PB_Ignore, Width=#PB_Ignore, Height=#PB_Ignore)
      
      If IsGadget(Gadget) And RefreshType >= #DeFlicker_NO And RefreshType <= #DeFlicker_Full
         
         Repeat
            ForEach UserDefined_DeFlickerType()
               If UserDefined_DeFlickerType()\GadgetNumber = Gadget
                  Break 2
               EndIf
            Next
            
            AddElement(UserDefined_DeFlickerType())
         Until #True
         
         With UserDefined_DeFlickerType()
         	\GadgetNumber		= Gadget
         	\DeFlicker_Type	= RefreshType
				If RefreshType	= #DeFlicker_Region
					\xPos				= xPos
					\yPos				= yPos
					\Width			= Width
					\Height			= Height
            EndIf
         EndWith
         
      EndIf
      
   EndProcedure
   
EndModule

#Enable_DeFlicker	= 1				; <======================  Switch DeFlicker ON or OFF

#EnablePanel=1 ; If set to 1, it makes ListIconGadget #2 flicker badly when window is resized vertically!

OpenWindow(0, 200, 200, 230, 180, "SplitterGadget", #PB_Window_SystemMenu | #PB_Window_MaximizeGadget | #PB_Window_SizeGadget)

CreateStatusBar(0,WindowID(0))

CompilerIf #EnablePanel=1
  PanelGadget(0, 10, 10, 210, 160)
  AddGadgetItem(0,-1,"Tab 1")
CompilerEndIf

ListIconGadget(1, 10, 10, 200, 150, "1", 200)
For i=1 To 100
  AddGadgetItem(1, -1, Str(i))
Next

ListIconGadget(2, 10, 10, 200, 150, "2", 200)
For i=1 To 100
  AddGadgetItem(2, -1, Str(i))
Next

SplitterGadget(3, 10, 10, 180, 110, 1, 2, #PB_Splitter_Vertical | #PB_Splitter_Separator | #PB_Splitter_FirstFixed)

CompilerIf #EnablePanel=1
  AddGadgetItem(0,-1,"Tab 2")
  CloseGadgetList()
CompilerEndIf

Procedure ResizeApp()
  appx=WindowX(0)
  appy=WindowY(0)
  appw=WindowWidth(0)
  apph=WindowHeight(0)
  If #Enable_DeFlicker : DeFlicker::StartResize(0) : EndIf					; <====================== Begin DeFlicker
  CompilerIf #EnablePanel=1
    ResizeGadget(0,#PB_Ignore,#PB_Ignore,appw-20,apph-40)
  CompilerEndIf
  ResizeGadget(3,#PB_Ignore,#PB_Ignore,appw-45,apph-90)
  If #Enable_DeFlicker	: DeFlicker::EndResize() : EndIf						; <====================== End-DeFlicker
  
EndProcedure

Procedure Callback(hWnd,Message,wParam,lParam)
  result=#PB_ProcessPureBasicEvents
  If Message=#WM_WINDOWPOSCHANGED
    ResizeApp()
  EndIf
  ProcedureReturn result
EndProcedure

SetWindowCallback(@Callback())

ResizeApp() ; Set initial size.

Repeat : Until WaitWindowEvent() = #PB_Event_CloseWindow
[Dynamic-Dialogs] - create complex GUIs the easy way
[DeFlicker] - easily deflicker your resizeable Windows
[WinFX] - Window Effects (incl. 'click-through' Window)
Dude
Addict
Addict
Posts: 1907
Joined: Mon Feb 16, 2015 2:49 pm

Re: SplitterGadget redraw issue

Post by Dude »

Thanks, PureLust! :D

Initially I was a bit put off by adding a lot of module code to fix a small problem, but in the end I decided it was worth it to have such smooth, flicker-free resizing. 8)

A quick question: I haven't used modules before, so I assume your DeFlicker module is 100% standalone to the rest of my source and won't interfere with anything else? It's just a drop-in thing that I can rely on to not mess with the rest of my code? That's what I think it does, but I want to be 100% sure before I commit to it.
PureLust
Enthusiast
Enthusiast
Posts: 477
Joined: Mon Apr 16, 2007 3:57 am
Location: Germany, NRW

Re: SplitterGadget redraw issue

Post by PureLust »

Dude wrote:A quick question: I haven't used modules before, so I assume your DeFlicker module is 100% standalone to the rest of my source and won't interfere with anything else? It's just a drop-in thing that I can rely on to not mess with the rest of my code? That's what I think it does, but I want to be 100% sure before I commit to it.
Yes, Modules are designed to function as a kind of PlugIn.
IMHO best practice is to save them in an Incude-Directory (in my case "..\Includes\DeFlicker.pbi") and then XInclude them if you want to access it's functions.
So, just put "XInclude "..\Includes\DeFlicker.pbi" at the beginning of your code, and you are fine.

Usually a Module does not interfere with the rest of your code in any way (except if someone stupid has declared some outside constants :wink: ).

Modules are a great way to extend PBs Functionality and you may have a look at the forum ... there are a lot of great Modules already available.
[Dynamic-Dialogs] - create complex GUIs the easy way
[DeFlicker] - easily deflicker your resizeable Windows
[WinFX] - Window Effects (incl. 'click-through' Window)
Post Reply