Page 1 of 2

Synchronised Scroll Bars Swimming

Posted: Sat Apr 21, 2012 3:18 am
by IdeasVacuum
Ok, it's not about swimming. It's about List Icon Gadgets or List View Gadgets - If you have a pair side-by-side, how can you synchronise them such that when you move the scroll bar on one, the scroll bar on the other moves with it?

Re: Sychronised Swimming

Posted: Sat Apr 21, 2012 7:01 am
by RASHAD
Hi IdeasVacuum
Next snippet By FB (Fluid Byte)
And if you search the forum you will find a complicated one By Me to fix the first column too

Code: Select all

OpenWindow(0,0,0,415,320,"void",#PB_Window_SystemMenu | #PB_Window_ScreenCentered)

ContainerGadget(0,5,5,200 - 2 - 17,310)
ListIconGadget(1,0,0,200,310,"Name",170)
CloseGadgetList()
ListIconGadget(2,190,5,200,310,"Name",170)

For i=1 To 500
   AddGadgetItem(1,-1,"Gadget Item #" + RSet(Str(i),3,"0"))
   AddGadgetItem(2,-1,"Gadget Item #" + RSet(Str(i),3,"0"))
Next

Structure NMLVSCROLL
    hdr.NMHDR
    dx.l
    dy.l
EndStructure

Procedure WindowCallback(hWnd,uMsg,wParam,lParam)

   Select uMsg
      Case #WM_NOTIFY
      Protected *pnmh.NMHDR = lParam   
      
      If *pnmh\hwndFrom = GadgetID(2)
         If *pnmh\code = #LVN_ENDSCROLL
            Protected *pnms.NMLVSCROLL = lParam
            Protected LVTI = SendMessage_(GadgetID(2),#LVM_GETTOPINDEX,0,0)
            Protected LVCP = SendMessage_(GadgetID(2),#LVM_GETCOUNTPERPAGE,0,0)
            
            If *pnms\dy < 0
               SendMessage_(GadgetID(1),#LVM_ENSUREVISIBLE,LVTI,0)
            ElseIf *pnms\dy > 0
               SendMessage_(GadgetID(1),#LVM_ENSUREVISIBLE,LVTI + LVCP - 1,0)
            EndIf
         EndIf
      EndIf             
   EndSelect
   
   ProcedureReturn #PB_ProcessPureBasicEvents
EndProcedure

SetWindowCallback(@WindowCallback())

While WaitWindowEvent() ! #PB_Event_CloseWindow : Wend

Re: Sychronised Swimming

Posted: Sat Apr 21, 2012 9:05 am
by Danilo
With 2 ListViewGadget:

Code: Select all

Procedure MouseOverGadget(window,gadget)
  If (WindowMouseX(window) >= GadgetX(gadget)) And (WindowMouseX(window) <= (GadgetX(gadget)+GadgetWidth(gadget)))
    If (WindowMouseY(window) >= GadgetY(gadget)) And (WindowMouseY(window) <= (GadgetY(gadget)+GadgetHeight(gadget)))
      ProcedureReturn #True
    EndIf
  EndIf
EndProcedure


Procedure Syncro(hWnd,msg,wParam,lParam)
    Static activeGadgetID=0
    gadget = GetWindowLongPtr_(hWnd,#GWL_ID)
    If msg = #WM_VSCROLL
        If gadget = 0 Or gadget = 1 ; both ListView
            If MouseOverGadget(0,gadget)
                activeGadgetID=hWnd
            EndIf
            If activeGadgetID=hWnd
                If gadget = 0 : target = 1
                Else          : target = 0
                EndIf
                ;pos = GetScrollPos_(hWnd,#SB_VERT)
                ;SetScrollPos_(GadgetID(target),#SB_VERT,pos,1)
                top  = SendMessage_(    hWnd   ,#LB_GETTOPINDEX ,0,0)
                SendMessage_(GadgetID(target),#LB_SETTOPINDEX,top,0)
            EndIf
        EndIf
    EndIf
    proc = GetGadgetData(gadget)
    If proc
        ProcedureReturn CallWindowProc_(proc,hWnd,msg,wParam,lParam)
    EndIf
EndProcedure


Procedure SubClassGadget(gadget,*proc)
    SetGadgetData(gadget,SetWindowLongPtr_(GadgetID(gadget),#GWL_WNDPROC,*proc))
EndProcedure


If OpenWindow(0, 0, 0, 480, 300, "Sychronised Swimming", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)

    ListViewGadget(0, 10, 10, 225, 280)
    ListViewGadget(1, 245,10, 225, 280)
    
    SubClassGadget(0,@Syncro())
    SubClassGadget(1,@Syncro())
    
    For a = 1 To 100
      AddGadgetItem (0, -1, "Item " + Str(a) + " of the Listview 1")
      AddGadgetItem (1, -1, "Item " + Str(a) + " of the Listview 2")
    Next

    Repeat : Until WaitWindowEvent() = #PB_Event_CloseWindow
EndIf
1 ListViewGadget and 1 ListIconGadget, synchronized Top Index:

Code: Select all

Procedure MouseOverGadget(window,gadget)
  If (WindowMouseX(window) >= GadgetX(gadget)) And (WindowMouseX(window) <= (GadgetX(gadget)+GadgetWidth(gadget)))
    If (WindowMouseY(window) >= GadgetY(gadget)) And (WindowMouseY(window) <= (GadgetY(gadget)+GadgetHeight(gadget)))
      ProcedureReturn #True
    EndIf
  EndIf
EndProcedure


Procedure Syncro(hWnd,msg,wParam,lParam)
    Static activeGadgetID=0
    gadget = GetWindowLongPtr_(hWnd,#GWL_ID)
    If msg = #WM_VSCROLL
        If gadget = 0 ; ListView
            If MouseOverGadget(0,0)
                activeGadgetID=hWnd
            EndIf
            If activeGadgetID=hWnd
                top  = SendMessage_(    hWnd   ,#LB_GETTOPINDEX ,0,0)
                top2 = SendMessage_(GadgetID(1),#LVM_GETTOPINDEX,0,0)

                SendMessage_(GadgetID(1),#LVM_GETITEMPOSITION,top2,origin.POINT) ; get pos of top item
                SendMessage_(GadgetID(1),#LVM_GETITEMPOSITION,top,item.POINT)    ; get pos of item we want at top
                SendMessage_(GadgetID(1),#LVM_SCROLL,0,item\y - origin\y)        ; scroll difference
            EndIf
            
        ElseIf gadget = 1 ; ListIcon
            If MouseOverGadget(0,1)
                activeGadgetID=hWnd
            EndIf
            If activeGadgetID=hWnd
                top = SendMessage_(hWnd,#LVM_GETTOPINDEX,0,0)
                SendMessage_(GadgetID(0),#LB_SETTOPINDEX,top,0)
            EndIf
            
        EndIf
    EndIf
    proc = GetGadgetData(gadget)
    If proc
        ProcedureReturn CallWindowProc_(proc,hWnd,msg,wParam,lParam)
    EndIf
EndProcedure


Procedure SubClassGadget(gadget,*proc)
    SetGadgetData(gadget,SetWindowLongPtr_(GadgetID(gadget),#GWL_WNDPROC,*proc))
EndProcedure


If OpenWindow(0, 0, 0, 480, 300, "Sychronised Swimming", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)

    ListViewGadget(0, 10, 10, 225, 280)
    ListIconGadget(1, 245,10, 225, 280,"Column 1",100)
        AddGadgetColumn(1,2,"Column 2",100)
    
    SubClassGadget(0,@Syncro())
    SubClassGadget(1,@Syncro())
    
    For a = 1 To 100
      AddGadgetItem (0, -1, "Item " + Str(a) + " of the Listview")
      AddGadgetItem (1, -1, "Item " + Str(a) + Chr(10)+" of the ListIcon")
    Next

    Repeat : Until WaitWindowEvent() = #PB_Event_CloseWindow
EndIf

Re: Sychronised Swimming

Posted: Sat Apr 21, 2012 2:49 pm
by IdeasVacuum
Wow, thanks guys, truly excellent stuff! 8)

Re: Sychronised Swimming

Posted: Sun Apr 22, 2012 12:07 am
by electrochrisso
It would be good to be able to keep sync when using keyboard too. :)

Re: Sychronised Swimming

Posted: Sun Apr 22, 2012 3:58 am
by RASHAD
For ListIcon()

Code: Select all


OpenWindow(0,0,0,415,320,"void",#PB_Window_SystemMenu | #PB_Window_ScreenCentered)

ContainerGadget(0,5,5,200 - 2 - 17,310)
ListIconGadget(1,0,0,200,310,"Name",170)
CloseGadgetList()
ListIconGadget(2,190,5,200,310,"Name",170)

For i=1 To 500
   AddGadgetItem(1,-1,"Gadget Item #" + RSet(Str(i),3,"0"))
   AddGadgetItem(2,-1,"Gadget Item #" + RSet(Str(i),3,"0"))
Next

Structure NMLVSCROLL
    hdr.NMHDR
    dx.l
    dy.l
EndStructure

Procedure WindowCallback(hWnd,uMsg,wParam,lParam)

   Select uMsg
      Case #WM_NOTIFY
      Protected *pnmh.NMHDR = lParam   
      
      If *pnmh\hwndFrom = GadgetID(2)
         If *pnmh\code = #LVN_ENDSCROLL
            Protected *pnms.NMLVSCROLL = lParam
            Protected LVTI = SendMessage_(GadgetID(2),#LVM_GETTOPINDEX,0,0)
            Protected LVCP = SendMessage_(GadgetID(2),#LVM_GETCOUNTPERPAGE,0,0)
            
            If *pnms\dy < 0
               SendMessage_(GadgetID(1),#LVM_ENSUREVISIBLE,LVTI,0)
            ElseIf *pnms\dy > 0
               SendMessage_(GadgetID(1),#LVM_ENSUREVISIBLE,LVTI + LVCP - 1,0)
            EndIf
         EndIf
      EndIf
      
      Case #WM_KEYDOWN ,#WM_MENUSELECT
        If wParam=#VK_UP
          SendMessage_(GadgetID(1),#WM_VSCROLL,#SB_LINEUP,0)
          SendMessage_(GadgetID(2),#WM_VSCROLL,#SB_LINEUP,0)
        ElseIf wParam=#VK_DOWN
          SendMessage_(GadgetID(1),#WM_VSCROLL,#SB_LINEDOWN,0)
          SendMessage_(GadgetID(2),#WM_VSCROLL,#SB_LINEDOWN,0)
        EndIf             
   EndSelect
   
   ProcedureReturn #PB_ProcessPureBasicEvents
EndProcedure

SetWindowCallback(@WindowCallback())

While WaitWindowEvent() ! #PB_Event_CloseWindow : Wend

For ListView()

Code: Select all


OpenWindow(0,0,0,415,320,"void",#PB_Window_SystemMenu | #PB_Window_ScreenCentered)

ContainerGadget(0,5,5,200 - 2 - 17,310)
ListViewGadget(1,0,0,200,310)
CloseGadgetList()
ListViewGadget(2,190,5,200,310)

For i=1 To 500
   AddGadgetItem(1,-1,"Gadget Item #" + RSet(Str(i),3,"0"))
   AddGadgetItem(2,-1,"Gadget Item #" + RSet(Str(i),3,"0"))
Next

Procedure WindowCallback(hWnd,uMsg,wParam,lParam)
   Select uMsg
      Case #WM_CTLCOLORLISTBOX
      If lParam = GadgetID(2)
         SendMessage_(GadgetID(1),#LB_SETTOPINDEX,GetScrollPos_(GadgetID(2),#SB_VERT),0)
      EndIf
      
      Case #WM_KEYDOWN ,#WM_MENUSELECT
        If wParam=#VK_UP
          SendMessage_(GadgetID(1),#WM_VSCROLL,#SB_LINEUP,0)
          SendMessage_(GadgetID(2),#WM_VSCROLL,#SB_LINEUP,0)
        ElseIf wParam=#VK_DOWN
          SendMessage_(GadgetID(1),#WM_VSCROLL,#SB_LINEDOWN,0)
          SendMessage_(GadgetID(2),#WM_VSCROLL,#SB_LINEDOWN,0)
        EndIf 
   EndSelect
   
   ProcedureReturn #PB_ProcessPureBasicEvents
EndProcedure

SetWindowCallback(@WindowCallback())

While WaitWindowEvent() ! #PB_Event_CloseWindow : Wend

Re: Sychronised Swimming

Posted: Sun Apr 22, 2012 9:27 am
by electrochrisso
Hi RASHAD,
I made a quick addition to your listview() code, to match left and right lists to do the same job in the callback, do you know how to get the row select highlight to match as well. :?:
I have been playing but not very successful for the time being. :(

Code: Select all

OpenWindow(0,0,0,415,320,"void",#PB_Window_SystemMenu | #PB_Window_ScreenCentered)

ContainerGadget(0,5,5,200 - 2 - 17,310)
ListViewGadget(1,0,0,200,310)
CloseGadgetList()
ListViewGadget(2,190,5,200,310)

For i=1 To 500
   AddGadgetItem(1,-1,"Gadget Item #" + RSet(Str(i),3,"0"))
   AddGadgetItem(2,-1,"Gadget Item #" + RSet(Str(i),3,"0"))
Next

Procedure WindowCallback(hWnd,uMsg,wParam,lParam)
   Select uMsg
      Case #WM_CTLCOLORLISTBOX
      If lParam = GadgetID(1)
         SendMessage_(GadgetID(2),#LB_SETTOPINDEX,GetScrollPos_(GadgetID(1),#SB_VERT),0)
      EndIf
      If lParam = GadgetID(2)
         SendMessage_(GadgetID(1),#LB_SETTOPINDEX,GetScrollPos_(GadgetID(2),#SB_VERT),0)
      EndIf
     
      Case #WM_KEYDOWN ,#WM_MENUSELECT
        If wParam=#VK_UP
          SendMessage_(GadgetID(1),#WM_VSCROLL,#SB_LINEUP,0)
          SendMessage_(GadgetID(2),#WM_VSCROLL,#SB_LINEUP,0)
        ElseIf wParam=#VK_DOWN
          SendMessage_(GadgetID(1),#WM_VSCROLL,#SB_LINEDOWN,0)
          SendMessage_(GadgetID(2),#WM_VSCROLL,#SB_LINEDOWN,0)
        EndIf
   EndSelect
   
   ProcedureReturn #PB_ProcessPureBasicEvents
EndProcedure

SetWindowCallback(@WindowCallback())

While WaitWindowEvent() ! #PB_Event_CloseWindow : Wend

Re: Sychronised Swimming

Posted: Sun Apr 22, 2012 1:13 pm
by RASHAD
Your correction not needed as long as no vertical Scroll Bar for ListView() #1
Not the best but step forward

Code: Select all

OpenWindow(0,0,0,415,320,"void",#PB_Window_SystemMenu | #PB_Window_ScreenCentered)

ContainerGadget(0,5,5,200 - 2 - 17,310)
ListViewGadget(1,0,0,200,310)
CloseGadgetList()
ListViewGadget(2,190,5,200,310)

For i=1 To 500
   AddGadgetItem(1,-1,"Gadget Item #" + RSet(Str(i),3,"0"))
   AddGadgetItem(2,-1,"Gadget Item #" + RSet(Str(i),3,"0"))
Next


Procedure WindowCallback(hWnd,uMsg,wParam,lParam)
   Select uMsg
      Case #WM_CTLCOLORLISTBOX
      If lParam = GadgetID(2)
         SendMessage_(GadgetID(1),#LB_SETTOPINDEX,GetScrollPos_(GadgetID(2),#SB_VERT),0)
      EndIf
      
      Case #WM_KEYDOWN ,#WM_MENUSELECT
        If wParam=#VK_UP
          SendMessage_(GadgetID(1),#WM_VSCROLL,#SB_LINEUP,0)
          SendMessage_(GadgetID(2),#WM_VSCROLL,#SB_LINEUP,0)
        ElseIf wParam=#VK_DOWN
          SendMessage_(GadgetID(1),#WM_VSCROLL,#SB_LINEDOWN,0)
          SendMessage_(GadgetID(2),#WM_VSCROLL,#SB_LINEDOWN,0)
        EndIf 
   EndSelect
   
   ProcedureReturn #PB_ProcessPureBasicEvents
EndProcedure

SetWindowCallback(@WindowCallback())

Repeat
  Select WaitWindowEvent()
      
      Case #PB_Event_CloseWindow
            Quit = 1       
     
      Case #PB_Event_Gadget
          Select EventGadget()
           Case 1
               SetGadgetState(2, GetGadgetState(1))
           
           Case 2
               SetGadgetState(1, GetGadgetState(2))
                       
          EndSelect
  EndSelect
Until Quit = 1


Re: Sychronised Swimming

Posted: Sun Apr 22, 2012 10:46 pm
by electrochrisso
Good one RASHAD, That does the trick, gives me something to play with :) , I should have tried it your way first, silly me was trying to get it to work in the callback and I get stack overflow error. :lol:
Thanks

Re: Sychronised Scroll Bars Swimming

Posted: Sun Aug 11, 2013 10:14 pm
by IdeasVacuum
Just tried Danilo's method for two ListViews (which works great) with two TreeViews.
As they say in London, "not a sausage". i.e. does not work, do not know why.

Re: Sychronised Scroll Bars Swimming

Posted: Mon Aug 12, 2013 11:44 am
by IdeasVacuum
If I add this to the window's Callback:

Code: Select all

               If Msg = #WM_NOTIFY

                      pos = GetScrollPos_(GadgetID(#Tree01),#SB_VERT)
                            SetScrollPos_(GadgetID(#Tree02),#SB_VERT,pos,#True)

               EndIf
When #Tree01 is scrolled, the scrollbar of #Tree02 is sychronised with it perfectly - but the Tree itself is not moved :(

Edit: If, after #Tree01 is scrolled, the Window is resized slightly, #Tree02 is refreshed to match it's scrollbar position. So looked at using the RedrawWindow_() function, but scrolling a Tree does not make it the event gadget........

Edit2:Tried this in WindowCallback:

Code: Select all

If Msg = #WM_NOTIFY
                   iPosnNew = GetScrollPos_(GadgetID(#TreeFilesOrg),#SB_VERT)                
                   SetScrollPos_(GadgetID(#TreeFilesPrv),#SB_VERT,iPosnNew,#True) 
                   RedrawWindow_(GadgetID(#Tree02), #Null, #Null, #RDW_UPDATENOW) 
         EndIf
Still no joy........

Re: Sychronised Scroll Bars Swimming

Posted: Mon Aug 12, 2013 1:54 pm
by IdeasVacuum
Finally, an ugly hack that nearly works. The tree sizes are identical, so not sure why they synch perfectly at the top of the scroll limit but are a row out at the bottom of the scroll limit?

Code: Select all

Procedure WindowCallback(hWnd.i, Msg.i, WParam.i, LParam.i)
;----------------------------------------------------------
Static iPosnNew.i, iPosnOld.i

               If Msg = #WM_NOTIFY

                             iPosnNew = GetScrollPos_(GadgetID(#Tree01),#SB_VERT)
                          If(iPosnNew <> iPosnOld)

                                     SetScrollPos_(GadgetID(#Tree02),#SB_VERT,iPosnNew,#True)
                                      ResizeGadget(#Tree02,#PB_Ignore, #PB_Ignore, #PB_Ignore, GadgetHeight(#Tree01) + 1)
                                      ResizeGadget(#Tree02,#PB_Ignore, #PB_Ignore, #PB_Ignore, GadgetHeight(#Tree01))
                                     iPosnOld = iPosnNew
                          EndIf
               EndIf
               ProcedureReturn #PB_ProcessPureBasicEvents
EndProcedure

Re: Synchronised Scroll Bars Swimming

Posted: Wed Aug 14, 2013 3:48 pm
by JHPJHP
Not sure if this is what your looking for - if it is you had it mostly right originally :)

- works with both treeviews

Code: Select all

Global lpPrevWndFunc

Procedure TreeGadgetProc(hWnd, Msg, wParam, lParam)
  Result = CallWindowProc_(lpPrevWndFunc, hWnd, Msg, wParam, lParam)

  Select Msg
    Case #WM_VSCROLL
      Select hWnd
        Case GadgetID(0)
          ScrollPos = GetScrollPos_(GadgetID(0), #SB_VERT)
          SetScrollPos_(GadgetID(1), #SB_VERT, ScrollPos, #True)
          SendMessage_(GadgetID(1), #WM_SETREDRAW, #True, 0)
        Case GadgetID(1)
          ScrollPos = GetScrollPos_(GadgetID(1), #SB_VERT)
          SetScrollPos_(GadgetID(0), #SB_VERT, ScrollPos, #True)
          SendMessage_(GadgetID(0), #WM_SETREDRAW, #True, 0)
      EndSelect
  EndSelect
  ProcedureReturn Result
EndProcedure

If OpenWindow(0, 0, 0, 355, 180, "TreeGadget", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
  TreeGadget(0, 10, 10, 160, 160)
  TreeGadget(1, 180, 10, 160, 160, #PB_Tree_CheckBoxes | #PB_Tree_NoLines)
  lpPrevWndFunc = SetWindowLong_(GadgetID(0), #GWL_WNDPROC, @TreeGadgetProc())
  lpPrevWndFunc = SetWindowLong_(GadgetID(1), #GWL_WNDPROC, @TreeGadgetProc())
  
  For ID = 0 To 1
    For a = 0 To 10
      AddGadgetItem (ID, -1, "Normal Item "+Str(a), 0, 0)
      AddGadgetItem (ID, -1, "Node "+Str(a), 0, 0)
      AddGadgetItem(ID, -1, "Sub-Item 1", 0, 1)
      AddGadgetItem(ID, -1, "Sub-Item 2", 0, 1)
      AddGadgetItem(ID, -1, "Sub-Item 3", 0, 1)
      AddGadgetItem(ID, -1, "Sub-Item 4", 0, 1)
      AddGadgetItem (ID, -1, "File "+Str(a), 0, 0)
    Next
  Next
  Repeat : Until WaitWindowEvent() = #PB_Event_CloseWindow
EndIf

Re: Synchronised Scroll Bars Swimming

Posted: Wed Aug 14, 2013 4:25 pm
by IdeasVacuum
Well, that's exactly what I was looking for - I don't think my code was anywhere near it really. Thanks JHPJHP.

Re: Synchronised Scroll Bars Swimming

Posted: Thu Mar 13, 2014 1:14 pm
by Kwai chang caine
I want to synchronize two listicons.

I don't understand i have tested the code above of fluidbyte given by RASHAD and that not works here :cry:
http://www.purebasic.fr/english/viewtop ... 52#p378852

I have found also the code of DANILO but it's for two listview or one ListView and one ListIcon and apparently the behaviour is not the same
http://www.purebasic.fr/english/viewtop ... b7#p378855

Someone have an example for two ListIcons who works ??
Thanks in advance 8)