Page 1 of 1

Less paint & events for the SplitterGadget

Posted: Wed Feb 14, 2024 4:23 pm
by ChrisR
It seems that some redrawing and event sending are done for the Splitter Gadget even without changing the Splitter position (same Gadget state), they seem superfluous and cause flashing.
Including when the cursor is beyond the minimum position (MinimumSize or 0) or maximum position(GadgetWidth-SeparatorSize).
Would be good to avoid these re-drawing and sending event, if there is no change of position.

Code: Select all

EnableExplicit

Enumeration Window
  #Window
EndEnumeration

Enumeration Gadgets
  #ScrlArea_1
  #Txt_1
  #Btn_1
  #ScrlArea_2
  #Txt_2
  #Btn_2
  #Splitter
EndEnumeration

Procedure BindSplitter()
  Static SplitterPosition
  Protected NewSplitterPosition  = GetGadgetState(EventGadget())
  
  If Not NewSplitterPosition = SplitterPosition
    ;Debug "Splitter: new position = " + Str(SplitterPosition) + " - previous position = "  + Str(NewSplitterPosition)
    SplitterPosition = NewSplitterPosition
  Else
    Debug "Event received with same Splitter position = " + Str(SplitterPosition)
  EndIf
EndProcedure

If OpenWindow(#Window, 0, 0, 540, 220, "Less Paint & Event for SplitterGadget", #PB_Window_SystemMenu | #PB_Window_MinimizeGadget | #PB_Window_ScreenCentered)
  
  ScrollAreaGadget(#ScrlArea_1, 0, 0, 0, 0, 500, 500, 10, #PB_ScrollArea_Single)
  SetGadgetColor(#ScrlArea_1, #PB_Gadget_BackColor, $999999)
  ;SetWindowLongPtr_(GadgetID(#ScrlArea_1), #GWL_EXSTYLE, GetWindowLongPtr_(GadgetID(#ScrlArea_1), #GWL_EXSTYLE) | #WS_EX_COMPOSITED)
  TextGadget(#Txt_1, 20, 20, 240, 80, "The Splitter's child gadgets are redrawn and events are received even without position changes ", #PB_Text_Center)
  SetGadgetColor(#Txt_1, #PB_Gadget_BackColor, $400000) : SetGadgetColor(#Txt_1, #PB_Gadget_FrontColor, $40FFFF)
  ButtonGadget(#Btn_1, 50, 130, 200, 60, "Button First Gadget ")
  CloseGadgetList()
  
  ScrollAreaGadget(#ScrlArea_2, 0, 0, 0, 0, 500, 500, 10, #PB_ScrollArea_Single)
  SetGadgetColor(#ScrlArea_2, #PB_Gadget_BackColor, $999999)
  ;SetWindowLongPtr_(GadgetID(#ScrlArea_2), #GWL_EXSTYLE, GetWindowLongPtr_(GadgetID(#ScrlArea_2), #GWL_EXSTYLE) | #WS_EX_COMPOSITED)
  TextGadget(#Txt_2, 20, 20, 240, 80, "Including when the cursor is beyond the minimum position (MinimumSize or 0) or maximum position(GadgetWidth-SeparatorSize)", #PB_Text_Center)
  SetGadgetColor(#Txt_2, #PB_Gadget_BackColor, $400000) : SetGadgetColor(#Txt_2, #PB_Gadget_FrontColor, $40FFFF)
  ButtonGadget(#Btn_2, 50, 130, 200, 60, "Button Second Gadget")
  CloseGadgetList()
  
  SplitterGadget(#Splitter, 0, 0, 540, 220, #ScrlArea_1, #ScrlArea_2, #PB_Splitter_Separator | #PB_Splitter_Vertical)
  ;SetGadgetAttribute(#Splitter, #PB_Splitter_FirstMinimumSize, 60) : SetGadgetAttribute(#Splitter, #PB_Splitter_SecondMinimumSize, 60)
  BindGadgetEvent(#Splitter, @BindSplitter())
  
  Repeat : Until WaitWindowEvent() = #PB_Event_CloseWindow
EndIf

Re: Less paint & events for the SplitterGadget

Posted: Thu May 02, 2024 6:39 am
by BarryG
Confirmed: Your example flickers badly here on my Win 10 Pro. Makes the SplitterGadget() basically unusable because it's so ugly.

Re: Less paint & events for the SplitterGadget

Posted: Thu May 02, 2024 8:00 am
by Justin
A quick fix for Windows is double buffering, add this line after the splitter and the flickering is gone:

Code: Select all

SetWindowLong_(GadgetID(#Splitter), #GWL_EXSTYLE, GetWindowLong_(GadgetID(#Splitter), #GWL_EXSTYLE) | #WS_EX_COMPOSITED)

Re: Less paint & events for the SplitterGadget

Posted: Thu May 02, 2024 9:24 am
by mk-soft
#WS_EX_COMPOSITED

MS:
Paints all descendants of a window in bottom-to-top painting order using double-buffering. Bottom-to-top painting order allows a descendent window to have translucency (alpha) and transparency (color-key) effects, but only if the descendent window also has the WS_EX_TRANSPARENT bit set. Double-buffering allows the window and its descendents to be painted without flicker. This cannot be used if the window has a class style of either CS_OWNDC or CS_CLASSDC.
Windows 2000: This style is not supported.
Help function

Update v1.02

Code: Select all

;-TOP by mk-soft, v1.02, 02.05.2024

CompilerIf #PB_Compiler_OS = #PB_OS_Windows
  Import ""
    PB_Object_EnumerateStart(PB_Objects)
    PB_Object_EnumerateNext(PB_Objects, *ID.Integer)
    PB_Object_EnumerateAbort(PB_Objects)
    PB_Object_GetObject(PB_Object , DynamicOrArrayID)
    PB_Window_Objects
    PB_Gadget_Objects
    PB_Image_Objects
    PB_Font_Objects
  EndImport
  
  Procedure SetGadgetsExStyle(Flags, WindowID = 0)
    Protected hWnd
    
    PB_Object_EnumerateStart(PB_Gadget_Objects)
    If WindowID = 0
      While PB_Object_EnumerateNext(PB_Gadget_Objects, @object)
        hWnd = GadgetID(object)
        SetWindowLongPtr_(hWnd, #GWL_EXSTYLE, GetWindowLongPtr_(hWnd, #GWL_EXSTYLE) | #WS_EX_COMPOSITED)
        
      Wend
    Else 
      While PB_Object_EnumerateNext(PB_Gadget_Objects, @object)
        hWnd = GadgetID(object)
        If GetAncestor_(hWnd, #GA_ROOT) = WindowID
          SetWindowLongPtr_(hWnd, #GWL_EXSTYLE, GetWindowLongPtr_(hWnd, #GWL_EXSTYLE) | #WS_EX_COMPOSITED)
        EndIf
      Wend
    EndIf  
  EndProcedure
CompilerElse
  Macro SetGadgetsExStyle(Flags, WindowID)
  EndMacro
CompilerEndIf

; ********

CompilerIf #PB_Compiler_IsMainFile
  
  EnableExplicit

Enumeration Window
  #Window
EndEnumeration

Enumeration Gadgets
  #ScrlArea_1
  #Txt_1
  #Btn_1
  #ScrlArea_2
  #Txt_2
  #Btn_2
  #Splitter
EndEnumeration

Procedure BindSplitter()
  Static SplitterPosition
  Protected NewSplitterPosition  = GetGadgetState(EventGadget())
  
  If Not NewSplitterPosition = SplitterPosition
    ;Debug "Splitter: new position = " + Str(SplitterPosition) + " - previous position = "  + Str(NewSplitterPosition)
    SplitterPosition = NewSplitterPosition
  Else
    Debug "Event received with same Splitter position = " + Str(SplitterPosition)
  EndIf
EndProcedure

If OpenWindow(#Window, 0, 0, 540, 220, "Less Paint & Event for SplitterGadget", #PB_Window_SystemMenu | #PB_Window_MinimizeGadget | #PB_Window_ScreenCentered)
  
  ScrollAreaGadget(#ScrlArea_1, 0, 0, 0, 0, 500, 500, 10, #PB_ScrollArea_Single)
  SetGadgetColor(#ScrlArea_1, #PB_Gadget_BackColor, $999999)
  TextGadget(#Txt_1, 20, 20, 240, 80, "The Splitter's child gadgets are redrawn and events are received even without position changes ", #PB_Text_Center)
  SetGadgetColor(#Txt_1, #PB_Gadget_BackColor, $400000) : SetGadgetColor(#Txt_1, #PB_Gadget_FrontColor, $40FFFF)
  ButtonGadget(#Btn_1, 50, 130, 200, 60, "Button First Gadget ")
  CloseGadgetList()
  
  ScrollAreaGadget(#ScrlArea_2, 0, 0, 0, 0, 500, 500, 10, #PB_ScrollArea_Single)
  SetGadgetColor(#ScrlArea_2, #PB_Gadget_BackColor, $999999)
  TextGadget(#Txt_2, 20, 20, 240, 80, "Including when the cursor is beyond the minimum position (MinimumSize or 0) or maximum position(GadgetWidth-SeparatorSize)", #PB_Text_Center)
  SetGadgetColor(#Txt_2, #PB_Gadget_BackColor, $400000) : SetGadgetColor(#Txt_2, #PB_Gadget_FrontColor, $40FFFF)
  ButtonGadget(#Btn_2, 50, 130, 200, 60, "Button Second Gadget")
  CloseGadgetList()
  
  SplitterGadget(#Splitter, 0, 0, 540, 220, #ScrlArea_1, #ScrlArea_2, #PB_Splitter_Separator | #PB_Splitter_Vertical)
  BindGadgetEvent(#Splitter, @BindSplitter())
  
  SetGadgetsExStyle(#WS_EX_COMPOSITED, WindowID(#Window))
  
  Repeat : Until WaitWindowEvent() = #PB_Event_CloseWindow
EndIf

CompilerEndIf

Re: Less paint & events for the SplitterGadget

Posted: Thu May 02, 2024 9:38 am
by dige
Justin wrote: Thu May 02, 2024 8:00 am A quick fix for Windows is double buffering, add this line after the splitter and the flickering is gone:

Code: Select all

SetWindowLong_(GadgetID(#Splitter), #GWL_EXSTYLE, GetWindowLong_(GadgetID(#Splitter), #GWL_EXSTYLE) | #WS_EX_COMPOSITED)
Unfortunately, this leads here to a significant slowdown and redrawing problems. It may only be suitable if only a few gadgets are used within the SplitterArea.

Re: Less paint & events for the SplitterGadget

Posted: Thu May 02, 2024 11:45 am
by ChrisR
With WS_EX_COMPOSITED's double buffer, the rendering is really smooth here and when it can be used. Thanks mk-soft for your help function :)
I use it, in my app, for the properties container with many gadgets inside, this container is resized using a splitter and it works like a charm 8) :)
But it's not always the solution, it doesn't work well for other types of Gadget, it must be tested for each new splitter. :?

It seems to me that the splitter receives too many events and redraws, for each mouse movement, horizontal as well as vertical, smaller than the splitter's minimum position or larger than its max position. It should not!
It would be nice to receive an event and have the children redrawn only if the size of one of the two children has changed.

Re: Less paint & events for the SplitterGadget

Posted: Thu May 02, 2024 12:55 pm
by dige
Please try the following code with PB 5.73 and the current 6.11B2 with and without SetWindowLong_()

Code: Select all

If OpenWindow(0, 0, 0, 230, 180, "SplitterGadget", #PB_Window_SystemMenu | #PB_Window_ScreenCentered | #PB_Window_Maximize)
    #Button1  = 0
    #Button2  = 1
    #Splitter = 2
    
    CreateImage(0, 64, 64, 24, #Blue)
    CreateImage(1, 64, 64, 24, #Red)
    
    ContainerGadget(#Button1, 0, 0, WindowWidth(0), WindowHeight(0)/2, #PB_Container_Flat )
    For y = 1 To 20
      For x = 1 To 20
        ImageGadget(#PB_Any, X*68, y * 68, 64, 64, ImageID(1))
      Next
    Next
    CloseGadgetList()
    
    ContainerGadget(#Button2, 0, WindowHeight(0)/2, WindowWidth(0), WindowHeight(0)/2, #PB_Container_Flat)
    For y = 1 To 20
      For x = 1 To 20
        ImageGadget(#PB_Any, X*68, y * 68, 64, 64, ImageID(0))
      Next
    Next
    CloseGadgetList()
    
    SetGadgetColor(#Button1, #PB_Gadget_BackColor, #Blue)
    SetGadgetColor(#Button2, #PB_Gadget_BackColor, #Red)
    
    SplitterGadget(#Splitter, 10, 10, WindowWidth(0)-50, WindowHeight(0)-50, #Button1, #Button2, #PB_Splitter_Separator)
    SetWindowLong_(GadgetID(#Splitter), #GWL_EXSTYLE, GetWindowLong_(GadgetID(#Splitter), #GWL_EXSTYLE) | #WS_EX_COMPOSITED)
    
    

    Repeat
      
    Until WaitWindowEvent() = #PB_Event_CloseWindow
  EndIf


Re: Less paint & events for the SplitterGadget

Posted: Thu May 02, 2024 1:48 pm
by breeze4me
dige wrote: Thu May 02, 2024 12:55 pm Please try the following code with PB 5.73 and the current 6.11B2 with and without SetWindowLong_()
This might be fine on a high-performance PC, but in your case, you definitely shouldn't apply the #WS_EX_COMPOSITED style and it's much better to handle it like below.

Code: Select all

#OldWndProc$ = "OldWndProc"

Procedure WndProc_Splitter(hWnd, Message, wParam, lParam)
  Protected old = GetProp_(hWnd, #OldWndProc$)
  Protected iResult, Gadget, PrevPosXY.l
  
  Select Message
    Case #WM_LBUTTONDOWN
      Gadget = GetWindowLongPtr_(hWnd, #GWLP_ID)
      If GadgetID(Gadget) = hWnd
        SetGadgetData(Gadget, lParam)
      EndIf
      
    Case #WM_LBUTTONUP
      Gadget = GetWindowLongPtr_(hWnd, #GWLP_ID)
      If GadgetID(Gadget) = hWnd
        SetGadgetData(Gadget, -1)
      EndIf
      
    Case #WM_MOUSEMOVE
      If wParam = #MK_LBUTTON
        Gadget = GetWindowLongPtr_(hWnd, #GWLP_ID)
        If GadgetID(Gadget) = hWnd
          PrevPosXY = GetGadgetData(Gadget)
          If PrevPosXY <> lParam
            SetGadgetData(Gadget, lParam)
            
            iResult = CallWindowProc_(old, hWnd, Message, wParam, lParam)
            UpdateWindow_(hWnd)
            
            ProcedureReturn iResult
          Else
            ProcedureReturn 0
          EndIf
        EndIf
      EndIf
      
    Case #WM_NCDESTROY
      RemoveProp_(hWnd, #OldWndProc$)
      
  EndSelect
  ProcedureReturn CallWindowProc_(old, hWnd, Message, wParam, lParam)
EndProcedure

If OpenWindow(0, 0, 0, 230, 180, "SplitterGadget", #PB_Window_SystemMenu | #PB_Window_ScreenCentered | #PB_Window_Maximize)
  #Button1  = 0
  #Button2  = 1
  #Splitter = 2
  
  CreateImage(0, 64, 64, 24, #Blue)
  CreateImage(1, 64, 64, 24, #Red)
  
  hWnd = ContainerGadget(#Button1, 0, 0, WindowWidth(0), WindowHeight(0)/2, #PB_Container_Flat )
    SetWindowLongPtr_(hWnd, #GWL_STYLE, GetWindowLongPtr_(hWnd, #GWL_STYLE) | #WS_CLIPCHILDREN)
    For y = 1 To 20
      For x = 1 To 20
        ImageGadget(#PB_Any, X*68, y * 68, 64, 64, ImageID(1))
      Next
    Next
  CloseGadgetList()
  
  hWnd = ContainerGadget(#Button2, 0, WindowHeight(0)/2, WindowWidth(0), WindowHeight(0)/2, #PB_Container_Flat)
    SetWindowLongPtr_(hWnd, #GWL_STYLE, GetWindowLongPtr_(hWnd, #GWL_STYLE) | #WS_CLIPCHILDREN)
    For y = 1 To 20
      For x = 1 To 20
        ImageGadget(#PB_Any, X*68, y * 68, 64, 64, ImageID(0))
      Next
    Next
  CloseGadgetList()
  
  SetGadgetColor(#Button1, #PB_Gadget_BackColor, #Blue)
  SetGadgetColor(#Button2, #PB_Gadget_BackColor, #Red)
  
  hWnd = SplitterGadget(#Splitter, 10, 10, WindowWidth(0)-50, WindowHeight(0)-50, #Button1, #Button2, #PB_Splitter_Separator)
  ;SetWindowLong_(GadgetID(#Splitter), #GWL_EXSTYLE, GetWindowLong_(GadgetID(#Splitter), #GWL_EXSTYLE) | #WS_EX_COMPOSITED)
  SetWindowLongPtr_(hWnd, #GWL_STYLE, GetWindowLongPtr_(hWnd, #GWL_STYLE) | #WS_CLIPCHILDREN)
  SetProp_(hWnd, #OldWndProc$, SetWindowLongPtr_(hWnd, #GWLP_WNDPROC, @WndProc_Splitter()))
  
  
  Repeat
    
  Until WaitWindowEvent() = #PB_Event_CloseWindow
EndIf

Re: Less paint & events for the SplitterGadget

Posted: Thu May 02, 2024 2:47 pm
by ChrisR
dige wrote: Thu May 02, 2024 12:55 pm Please try the following code...
Nice example, It burns the eyes, it's like watching a solar eclipse in live, without sunglasses 8)

Re: Less paint & events for the SplitterGadget

Posted: Thu May 02, 2024 2:58 pm
by Axolotl
mk-soft wrote: Thu May 02, 2024 9:24 am .....

Code: Select all

;.....
  Procedure SetGadgetsExStyle(Flags, WindowID = 0)
;....
  Macro SetGadgetsStyleEx(WindowID, Flags)
;....
Hi mk-soft,
just looking at your example, maybe the two lines above should be the same names?
I guess the parameter order is not so important because the macro is empty anyway.

Re: Less paint & events for the SplitterGadget

Posted: Thu May 02, 2024 3:03 pm
by Axolotl
To whom it may concerns,

copied a note from MSDN about the SetWindowLong function.

Note This function has been superseded by the SetWindowLongPtr function. To write code that is compatible with both 32-bit and 64-bit versions of Windows, use the SetWindowLongPtr function.

Re: Less paint & events for the SplitterGadget

Posted: Thu May 02, 2024 3:25 pm
by dige
:shock:

I'm afraid I was misunderstood:

My example shows that it really helps with an older PB version. But with the current beta there is a big loss for in performance.

Re: Less paint & events for the SplitterGadget

Posted: Thu May 02, 2024 4:24 pm
by mk-soft
Axolotl wrote: Thu May 02, 2024 2:58 pm
mk-soft wrote: Thu May 02, 2024 9:24 am .....

Code: Select all

;.....
  Procedure SetGadgetsExStyle(Flags, WindowID = 0)
;....
  Macro SetGadgetsStyleEx(WindowID, Flags)
;....
Hi mk-soft,
just looking at your example, maybe the two lines above should be the same names?
I guess the parameter order is not so important because the macro is empty anyway.
You are right. Fixed the bug and use Get/SetWindowLongPtr