Page 1 of 2

Fill a ScrollAreaGadget without flickering?

Posted: Tue Feb 11, 2020 10:06 am
by Lebostein
If I clear and fill a ScrollAreaGadget with new gadgets, then it is very slow and flickering. You see the order in which the new gadgets pops up...
Can I change that behaviour? The problem is only on Windows. Mac OS no problems...

Re: Fill a ScrollAreaGadget without flickering?

Posted: Tue Feb 11, 2020 11:09 am
by srod
That sounds very odd. Which version of PB / Windows?

Have you got some code you can post which reproduces this?

Re: Fill a ScrollAreaGadget without delays and flickering?

Posted: Tue Feb 11, 2020 12:48 pm
by Lebostein
Windows 10.
Please try it with Mac OS too! On Mac OS this is very, very fast without any delays and flickering....

Code: Select all

#maxline = 12
#start_combo = 100
#start_string = 200
#max_item = 30

Procedure Rebuild()

  For line = 0 To #maxline
    If IsGadget(#start_combo + line): FreeGadget(#start_combo + line): EndIf
    If IsGadget(#start_string + line): FreeGadget(#start_string + line): EndIf
  Next
  OpenGadgetList(0)
    For line = 0 To #maxline
      ComboBoxGadget(#start_combo + line, 10, 10 + line * 25, 230, 22)
      For item = 0 To #max_item: AddGadgetItem(#start_combo + line, item, "Blablablup " + Str(item)): Next item
      SetGadgetState(#start_combo + line, Random(#max_item))
      StringGadget(#start_string + line, 250, 10 + line * 25, 80, 22, "")
    Next line
  CloseGadgetList()

EndProcedure

If OpenWindow(0, 0, 0, 405, 470, "ScrollAreaGadget", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
  ScrollAreaGadget(0, 10, 10, 390,420, 575, 555, 30)
  CloseGadgetList()
  ButtonGadget(1, 10, 430, 390,30, "Refresh")
  Rebuild()
  Repeat
    Select WaitWindowEvent()
      Case  #PB_Event_CloseWindow
        End
      Case  #PB_Event_Gadget
        Select EventGadget()
          Case 1: Rebuild()
        EndSelect
    EndSelect
  ForEver
EndIf

Re: Fill a ScrollAreaGadget without flickering?

Posted: Tue Feb 11, 2020 1:18 pm
by PureLust
Lebostein wrote:The problem is only on Windows. Mac OS no problems...
Japp, .... it's a known Problem with GUIs, created by PB on Windows.

You could suppress Windows redrawing using handpicked API commands or you can use [DeFlicker] if you are not confirm with API or try to deflicker your GUI in general and also to keep your source OS independent.

Deflicker by using handpicked API commands:

Code: Select all

EnableExplicit

Procedure Rebuild(Window)
	Protected line, x
	
	SendMessage_(WindowID(Window),#WM_SETREDRAW,#False,0)          ; <======================
	
	For line = 0 To 10
		If IsGadget(100 + line): FreeGadget(100 + line): EndIf
		If IsGadget(200 + line): FreeGadget(200 + line): EndIf
	Next
	OpenGadgetList(0)
	For line = 0 To 10
		ComboBoxGadget(100 + line, 10, 10 + line * 30, 230, 25)
		For x = 0 To 30: AddGadgetItem(100 + line, x, "Blablablup " + Str(x)): Next x
		StringGadget(200 + line, 250, 10 + line * 30, 80, 25, "")
		SetGadgetState(100 + line, Random(29))
	Next line
CloseGadgetList()

SendMessage_(WindowID(0),#WM_SETREDRAW,#True,0)                   ; <======================
RedrawWindow_(WindowID(0),#Null,#Null,#RDW_INVALIDATE)            ; <======================

EndProcedure


If OpenWindow(0, 0, 0, 605, 470, "ScrollAreaGadget", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
	ScrollAreaGadget(0, 10, 10, 390,420, 575, 555, 30)
	CloseGadgetList()
	ButtonGadget(1, 10, 430, 390,30, "Refresh")
	ListIconGadget(2,410,10,100,400, "test", 50)
	Rebuild(0)
	Repeat
		Select WaitWindowEvent()
			Case  #PB_Event_CloseWindow
				End
			Case  #PB_Event_Gadget
				Select EventGadget()
					Case 1: Rebuild(0)
				EndSelect
		EndSelect
	ForEver
EndIf
Deflicker by using DeFlicker-Module:

Code: Select all

EnableExplicit

XIncludeFile "..\DeFlicker\Module_DeFlicker.pbi"

Procedure Rebuild(Window)
	Protected line, x
	
	DeFlicker_StartResize(Window)          ; <======================
	
	For line = 0 To 10
		If IsGadget(100 + line): FreeGadget(100 + line): EndIf
		If IsGadget(200 + line): FreeGadget(200 + line): EndIf
	Next
	OpenGadgetList(0)
	For line = 0 To 10
		ComboBoxGadget(100 + line, 10, 10 + line * 30, 230, 25)
		For x = 0 To 30: AddGadgetItem(100 + line, x, "Blablablup " + Str(x)): Next x
		StringGadget(200 + line, 250, 10 + line * 30, 80, 25, "")
		SetGadgetState(100 + line, Random(29))
	Next line
CloseGadgetList()

DeFlicker_EndResize()                     ; <======================

EndProcedure


If OpenWindow(0, 0, 0, 605, 470, "ScrollAreaGadget", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
	ScrollAreaGadget(0, 10, 10, 390,420, 575, 555, 30)
	CloseGadgetList()
	ButtonGadget(1, 10, 430, 390,30, "Refresh")
	ListIconGadget(2,410,10,100,400, "test", 50)
	Rebuild(0)
	Repeat
		Select WaitWindowEvent()
			Case  #PB_Event_CloseWindow
				End
			Case  #PB_Event_Gadget
				Select EventGadget()
					Case 1: Rebuild(0)
				EndSelect
		EndSelect
	ForEver
EndIf

Re: Fill a ScrollAreaGadget without flickering?

Posted: Tue Feb 11, 2020 1:20 pm
by srod
I don't have a Mac, but, yes confirmed on Win 7. I can speed it up a little with some use of the #WM_SETREDRAW message and removing all the FreeGadget() calls because they are not needed (PB will free the existing gadgets each time you reuse the gadget#'s), but not a great deal of improvement.

Perhaps rather than keep recreating the gadgets in question, you can instead place them on a container and hide/show the container as required?

Re: Fill a ScrollAreaGadget without flickering?

Posted: Tue Feb 11, 2020 1:23 pm
by DK_PETER
@ Lebostein
There is nothing wrong. You have Windows visual effects on. (Animated)
Open Explorer, right click 'Computer'
Select Properties and performance tab.
You have probably everything turned on or give Windows free reigns to
decide itself.
But then again..Not many users fiddles with this...

Re: Fill a ScrollAreaGadget without flickering?

Posted: Tue Feb 11, 2020 1:26 pm
by Lebostein
@PureLust: Thanks, that removes the flickering.
But the code remains slow. It seems the "AddGadgetItem" slows down the execution massively.

I have added a timer:

Mac OS: 25 ms (old MacBook from 2012)
Windows 10: 900 ms (i7-3930K 3.20 Ghz, 12 CPU)

Code: Select all

#maxline = 12
#start_combo = 100
#start_string = 200
#max_item = 30

Procedure Rebuild()

  start = ElapsedMilliseconds()

  CompilerIf #PB_Compiler_OS = #PB_OS_Windows
    SendMessage_(WindowID(0),#WM_SETREDRAW,#False,0)          ; <======================
  CompilerEndIf

  For line = 0 To #maxline
    If IsGadget(#start_combo + line): FreeGadget(#start_combo + line): EndIf
    If IsGadget(#start_string + line): FreeGadget(#start_string + line): EndIf
  Next
  OpenGadgetList(0)
    For line = 0 To #maxline
      ComboBoxGadget(#start_combo + line, 10, 10 + line * 25, 230, 22)
      For item = 0 To #max_item: AddGadgetItem(#start_combo + line, item, "Blablablup " + Str(item)): Next item
      SetGadgetState(#start_combo + line, Random(#max_item))
      StringGadget(#start_string + line, 250, 10 + line * 25, 80, 22, "")
    Next line
  CloseGadgetList()

  CompilerIf #PB_Compiler_OS = #PB_OS_Windows
    SendMessage_(WindowID(0),#WM_SETREDRAW,#True,0)                   ; <======================
    RedrawWindow_(WindowID(0),#Null,#Null,#RDW_INVALIDATE)          ; <======================
  CompilerEndIf

  SetGadgetText(1, "Press to refresh (" + Str(ElapsedMilliseconds() - start) + ")")

EndProcedure

If OpenWindow(0, 0, 0, 405, 470, "ScrollAreaGadget", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
  ScrollAreaGadget(0, 10, 10, 390,420, 575, 555, 30)
  CloseGadgetList()
  ButtonGadget(1, 10, 430, 390,30, "Refresh")
  Rebuild()
  Repeat
    Select WaitWindowEvent()
      Case  #PB_Event_CloseWindow
        End
      Case  #PB_Event_Gadget
        Select EventGadget()
          Case 1: Rebuild()
        EndSelect
    EndSelect
  ForEver
EndIf

Re: Fill a ScrollAreaGadget without flickering?

Posted: Tue Feb 11, 2020 1:35 pm
by DK_PETER
Lebostein wrote:@PureLust: Thanks, that removes the flickering.
But the code remains slow. It seems the "AddGadgetItem" slows down the execution massively.

I have added a timer:
Mac OS: 25 ms (old MacBook from 2012)
Windows 10: 900 ms

Code: Select all

#maxline = 12
#start_combo = 100
#start_string = 200
#max_item = 30

Procedure Rebuild()

  start = ElapsedMilliseconds()

  SendMessage_(WindowID(0),#WM_SETREDRAW,#False,0)          ; <======================

  For line = 0 To #maxline
    If IsGadget(#start_combo + line): FreeGadget(#start_combo + line): EndIf
    If IsGadget(#start_string + line): FreeGadget(#start_string + line): EndIf
  Next
  OpenGadgetList(0)
    For line = 0 To #maxline
      ComboBoxGadget(#start_combo + line, 10, 10 + line * 25, 230, 22)
      For item = 0 To #max_item: AddGadgetItem(#start_combo + line, item, "Blablablup " + Str(item)): Next item
      SetGadgetState(#start_combo + line, Random(#max_item))
      StringGadget(#start_string + line, 250, 10 + line * 25, 80, 22, "")
    Next line
  CloseGadgetList()

  SendMessage_(WindowID(0),#WM_SETREDRAW,#True,0)                   ; <======================
  RedrawWindow_(WindowID(0),#Null,#Null,#RDW_INVALIDATE)          ; <======================

  SetGadgetText(1, Str(ElapsedMilliseconds() - start))

EndProcedure

If OpenWindow(0, 0, 0, 405, 470, "ScrollAreaGadget", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
  ScrollAreaGadget(0, 10, 10, 390,420, 575, 555, 30)
  CloseGadgetList()
  ButtonGadget(1, 10, 430, 390,30, "Refresh")
  Rebuild()
  Repeat
    Select WaitWindowEvent()
      Case  #PB_Event_CloseWindow
        End
      Case  #PB_Event_Gadget
        Select EventGadget()
          Case 1: Rebuild()
        EndSelect
    EndSelect
  ForEver
EndIf
If you use disablegadget(#start_combo + line, #True) before adding items
and disablegadget(#start_combo + line, #False) after..
Does that help?

Re: Fill a ScrollAreaGadget without flickering?

Posted: Tue Feb 11, 2020 1:41 pm
by Lebostein
DK_PETER wrote: If you use disablegadget(#start_combo + line, #True) before adding items
and disablegadget(#start_combo + line, #False) after..
Does that help?
No, has no effect.

Re: Fill a ScrollAreaGadget without flickering?

Posted: Tue Feb 11, 2020 1:42 pm
by PureLust
Lebostein wrote:@PureLust: Thanks, that removes the flickering.
But the code remains slow. It seems the "AddGadgetItem" slows down the execution massively.

I have added a timer:

Mac OS: 25 ms (old MacBook from 2012)
Windows 10: 900 ms (i7-3930K 3.20 Ghz, 12 CPU)
As srod mentioned before, you could send specific #WM_SETREDRAW Message to suppress the redraw of the ComboBoxGadget.
On my system it will speed it up by a factor of ~3 (~296ms before, ~98ms after).

Code: Select all

#maxline = 12
#start_combo = 100
#start_string = 200
#max_item = 30

Procedure Rebuild()
	
	start = ElapsedMilliseconds()
	
	SendMessage_(WindowID(0),#WM_SETREDRAW,#False,0)          ; <======================
	
	For line = 0 To #maxline
		If IsGadget(#start_combo + line): FreeGadget(#start_combo + line): EndIf
		If IsGadget(#start_string + line): FreeGadget(#start_string + line): EndIf
	Next
	OpenGadgetList(0)
	For line = 0 To #maxline
		ComboBoxGadget(#start_combo + line, 10, 10 + line * 25, 230, 22)
		
		SendMessage_(GadgetID(#start_combo + line),#WM_SETREDRAW,#False,0)          ; <##########################
		
		For item = 0 To #max_item: AddGadgetItem(#start_combo + line, item, "Blablablup " + Str(item)): Next item
		
		SendMessage_(GadgetID(#start_combo + line),#WM_SETREDRAW,#True,0)           ; <##########################
		
		SetGadgetState(#start_combo + line, Random(#max_item))
		StringGadget(#start_string + line, 250, 10 + line * 25, 80, 22, "")
	Next line
CloseGadgetList()

SendMessage_(WindowID(0),#WM_SETREDRAW,#True,0)                   ; <======================
RedrawWindow_(WindowID(0),#Null,#Null,#RDW_INVALIDATE)				; <======================

SetGadgetText(1, Str(ElapsedMilliseconds() - start))

EndProcedure

If OpenWindow(0, 0, 0, 405, 470, "ScrollAreaGadget", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
	ScrollAreaGadget(0, 10, 10, 390,420, 575, 555, 30)
	CloseGadgetList()
	ButtonGadget(1, 10, 430, 390,30, "Refresh")
	Rebuild()
	Repeat
		Select WaitWindowEvent()
			Case  #PB_Event_CloseWindow
				End
			Case  #PB_Event_Gadget
				Select EventGadget()
					Case 1: Rebuild()
				EndSelect
		EndSelect
	ForEver
EndIf

Re: Fill a ScrollAreaGadget without flickering?

Posted: Tue Feb 11, 2020 1:45 pm
by Lebostein
@PureLust: Thanks, that speeds up the execution. I see 320 ms now instead of 900. But 300 ms compared to 25 ms is still slow...
I wonder why Mac OS can handle that redraw things automatically...

Re: Fill a ScrollAreaGadget without flickering?

Posted: Tue Feb 11, 2020 2:10 pm
by PureLust
Lebostein wrote:@PureLust: Thanks, that speeds up the execution. I see 320 ms now instead of 900. But 300 ms compared to 25 ms is still slow...
I wonder why Mac OS can handle that redraw things automatically...
Speed seems to be a windows issue and not related to PB.
If you add items directly trough API, it will not speed up:

Code: Select all

#maxline = 12
#start_combo = 100
#start_string = 200
#max_item = 30

Procedure Rebuild()
   
   start = ElapsedMilliseconds()
   
   SendMessage_(WindowID(0),#WM_SETREDRAW,#False,0)          ; <======================
   
   For line = 0 To #maxline
      If IsGadget(#start_combo + line): FreeGadget(#start_combo + line): EndIf
      If IsGadget(#start_string + line): FreeGadget(#start_string + line): EndIf
   Next
   OpenGadgetList(0)
   For line = 0 To #maxline
      ComboBoxGadget(#start_combo + line, 10, 10 + line * 25, 230, 22)
      
      SendMessage_(GadgetID(#start_combo + line),#WM_SETREDRAW,#False,0)          ; <##########################
      
      For item = 0 To #max_item:
      	SendMessage_(GadgetID(#start_combo + line), #CB_ADDSTRING, 0, "Blablablup " + Str(item))			; <*****************************
      	;AddGadgetItem(#start_combo + line, item, "Blablablup " + Str(item))
      Next item
      
      SendMessage_(GadgetID(#start_combo + line),#WM_SETREDRAW,#True,0)           ; <##########################
      
      SetGadgetState(#start_combo + line, Random(#max_item))
      StringGadget(#start_string + line, 250, 10 + line * 25, 80, 22, "")
   Next line
CloseGadgetList()

SendMessage_(WindowID(0),#WM_SETREDRAW,#True,0)                   ; <======================
RedrawWindow_(WindowID(0),#Null,#Null,#RDW_INVALIDATE)            ; <======================

SetGadgetText(1, Str(ElapsedMilliseconds() - start))

EndProcedure

If OpenWindow(0, 0, 0, 405, 470, "ScrollAreaGadget", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
   ScrollAreaGadget(0, 10, 10, 390,420, 575, 555, 30)
   CloseGadgetList()
   ButtonGadget(1, 10, 430, 390,30, "Refresh")
   Rebuild()
   Repeat
      Select WaitWindowEvent()
         Case  #PB_Event_CloseWindow
            End
         Case  #PB_Event_Gadget
            Select EventGadget()
               Case 1: Rebuild()
            EndSelect
      EndSelect
   ForEver
EndIf

Re: Fill a ScrollAreaGadget without flickering?

Posted: Tue Feb 11, 2020 2:26 pm
by Lebostein
Can I copy/duplicate a filled ComboGadget with window-API? So I can fill this thing one time an copy it as often as I need to...

I need it for a filter system like the intelligent playlist in iTunes (see picture). So every line is a condition build with 2 ComboGadgets and a string gadget with dozens of properties and operators. But every ComboGadget in each line has the same values....

Image

Re: Fill a ScrollAreaGadget without flickering?

Posted: Tue Feb 11, 2020 2:51 pm
by srod
Closest thing to doing that would be an owner-drawn combo without the #CBS_HASSTRINGS styles. In this case you would simply draw the text yourself, taking the text from your own data structures. Quite a bit of work involved in this.

How about the following where we create the combo's just once and then simply repopulate everytime you hit the rebuild button. Together with some #WM_SETREDRAW's seems to speed up massively here. The original #WM_SETREDRAW was directed at the main window. It should instead be directed at the ScrollArea's 'inner panel' which I have corrected.

Code: Select all

Procedure Rebuild()
  hWnd = GetParent_(GadgetID(#start_combo))
  SendMessage_(hWnd,#WM_SETREDRAW,#False,0)          ; <======================
  For line = 0 To #maxline
    SendMessage_(GadgetID(#start_combo + line),#WM_SETREDRAW,#False,0)           ; <##########################
      For item = 0 To #max_item: AddGadgetItem(#start_combo + line, item, "Blablablup " + Str(item)): Next item
    SendMessage_(GadgetID(#start_combo + line),#WM_SETREDRAW,#True,0)           ; <##########################
    SetGadgetState(#start_combo + line, Random(#max_item))
  Next  

SendMessage_(hWnd,#WM_SETREDRAW,#True,0)                   ; <======================
RedrawWindow_(hWnd,#Null,#Null,#RDW_INVALIDATE)            ; <======================
EndProcedure

If OpenWindow(0, 0, 0, 405, 470, "ScrollAreaGadget", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
  ScrollAreaGadget(0, 10, 10, 390,420, 575, 555, 30)
    For line = 0 To #maxline
      ComboBoxGadget(#start_combo + line, 10, 10 + line * 25, 230, 22)
      StringGadget(#start_string + line, 250, 10 + line * 25, 80, 22, "")
    Next
  CloseGadgetList()
  ButtonGadget(1, 10, 430, 390,30, "Refresh")

   Rebuild()
   Repeat
      Select WaitWindowEvent()
         Case  #PB_Event_CloseWindow
            End
         Case  #PB_Event_Gadget
            Select EventGadget()
               Case 1: Rebuild()
            EndSelect
      EndSelect
   ForEver
EndIf
If this is not enough then your next step might be to intercept the #CBN_DROPDOWN notification and populate the combo box in question at this point only (if the combo is empty). That is, do not populate the controls all at once, only when needed.
If this is not enough then I refer you back to my original reply regarding using a container gadget! :wink:

Re: Fill a ScrollAreaGadget without flickering?

Posted: Tue Feb 11, 2020 3:20 pm
by PureLust
srod wrote:How about the following where we create the combo's just once and then simply repopulate everytime you hit the rebuild button. Together with some #WM_SETREDRAW's seems to speed up massively here.
Wow .... that speeded it up A LOT . . . but . . . you missed to clear the GadgetItems first, so you add more and more items each time.

But ... if you clear the GadgetItem List first, this will eat up a lot of time again. :cry:

Code: Select all

ClearGadgetItems(#start_combo + line)
[Edit] Doesn't seems to be ClearGadgetItems() that eats up the time (placed it outside the Timecount-Loop). But it increases the time, needed to add the next (first) 30 items a lot.
I wonder, why it's so much faster to add 30 items to an already filled ItemList than it takes to add them to an empty List. :?