Page 1 of 2

ScrollBar events only on release

Posted: Sun Sep 12, 2010 12:23 pm
by Trond
When dragging a scrollbar on Windows XP it doesn't give any events until releasing the mouse button. On Linux it gives events while dragging.

Code: Select all

#W = 512
#H = 384

OpenWindow(0, 0, 0, #W, #H, "", #PB_Window_ScreenCentered | #PB_Window_SystemMenu | #PB_Window_SizeGadget | #PB_Window_MinimizeGadget | #PB_Window_MaximizeGadget)
ScrollBarGadget(0, 10, 10, 200, 23, 0, 100, 20)

Repeat
  Select WaitWindowEvent()
    Case #PB_Event_Gadget
      Select EventGadget()
        Case 0
          Debug GetGadgetState(0)
      EndSelect
    Case #PB_Event_CloseWindow
      Break
  EndSelect
ForEver

Re: ScrollBar events only on release

Posted: Sun Sep 12, 2010 7:02 pm
by Fluid Byte
It's been that way since years. Practically since scrollbars were first introduced in PureBasic.

So not a bug at all, a missing feature at best.

Re: ScrollBar events only on release

Posted: Mon Sep 13, 2010 10:46 am
by Fred
That's because Windows uses a blocking callback for that, it's a limitation for sure, but not a bug.

Re: ScrollBar events only on release

Posted: Mon Sep 13, 2010 10:55 am
by srod
But the parent callback will still receive #WM_VSCROLL (or #WM_HSCROLL) notifications as the scroll is underway Fred.

Re: ScrollBar events only on release

Posted: Mon Sep 13, 2010 11:12 am
by Fred
Sure, but it won't exit the callback -> you won't have an event in the PB loop until the user release the button.

Re: ScrollBar events only on release

Posted: Mon Sep 13, 2010 11:25 am
by srod
I always imagined that PB's event loop was somehow 'invoked' by each Window proc, but now that I think about it, yes I understand.

Re: ScrollBar events only on release

Posted: Wed Dec 21, 2011 6:25 pm
by Trond
I still can't get this scrollbar thing to work properly. Look:

Code: Select all

Procedure WndProc(WindowID, Msg.i, wParam.i, lParam.i)
  Select Msg
    Case #WM_HSCROLL
      Debug GetGadgetState(0)
  EndSelect
  ProcedureReturn #PB_ProcessPureBasicEvents
EndProcedure

W = 512
H = 384
OpenWindow(0, 0, 0, W, H, "", #PB_Window_ScreenCentered | #PB_Window_MaximizeGadget | #PB_Window_MinimizeGadget | #PB_Window_SizeGadget)

ScrollBarGadget(0, 10, 10, 200, 17, 0, 5, 1)

;OldProc = SetWindowLongPtr_(GadgetID(0), #GWLP_WNDPROC, @WndProc())
SetWindowCallback(@WndProc())

Repeat
  Event = WaitWindowEvent()
  Select Event
    Case #PB_Event_CloseWindow
      CloseWindow(0)
      Break
  EndSelect
ForEver
Drag the thumb one notch to the right, and it shows 0 a second time. Before releasing, drag it back to position 0 - now it debugs 1! Release the mouse button and it shows 0 twice!

Or drag it slowly to the right. Even when fully jammed into the right side, it just won't display 4 - until the mouse button is released and it shows 4 twice!

Re: ScrollBar events only on release

Posted: Wed Dec 21, 2011 6:52 pm
by srod
I am not on my Windows machine right now but what you are describing is what I would expect.

Don't use a PB callback Trond, subclass the window instead - or at the very least do not return #PB_ProcessPureBasicEvents for the #WM_HSCROLL message. The thing is that Windows will not move the scrollbox in response to user actions and depends instead on your #WM_HSCROLL handler using SetScrollInfo_() etc. to actually move the thumb (tracking aside). Because you do not actually use SetScrollInfo_() you are seeing the wrong values debugged. By using #PB_ProcessPureBasicEvents, you are giving PB's default handler the chance to take over which will of course move the scrollbox, but not before you have debugged the (incorrect) value.

As for the repeated debugs, this is happening because #WM_HSCROLL is called multiple times whilst the user is dragging the thumb (scroll code #SB_THUMBTRACK) and again when the user releases the thumb (#SB_THUMBPOSITION). You need to examine the scrollcode which sits in the lower 16-bits of wParam.

I can hack some code up for you if you wish?

Re: ScrollBar events only on release

Posted: Wed Dec 21, 2011 7:01 pm
by srod
Try this :

Code: Select all

Global gOldProc 

Procedure WndProc(WindowID, Msg.i, wParam.i, lParam.i)
  result = CallWindowProc_(gOldProc, WindowID, Msg, wParam, lParam)
  Select Msg
    Case #WM_HSCROLL
      scrollCode = wParam & $ffff
      If scrollCode <> #SB_THUMBPOSITION And scrollCode <> #SB_ENDSCROLL
        Debug GetGadgetState(0)
      EndIf
  EndSelect
  ProcedureReturn result
EndProcedure

W = 512
H = 384
OpenWindow(0, 0, 0, W, H, "", #PB_Window_ScreenCentered | #PB_Window_MaximizeGadget | #PB_Window_MinimizeGadget | #PB_Window_SizeGadget)

ScrollBarGadget(0, 10, 10, 200, 17, 0, 5, 1)

gOldProc = SetWindowLongPtr_(WindowID(0), #GWLP_WNDPROC, @WndProc())

Repeat
  Event = WaitWindowEvent()
  Select Event
    Case #PB_Event_CloseWindow
      CloseWindow(0)
      Break
  EndSelect
ForEver
Here, the sublass proc calls the previous handler (the one installed by PB) to handle, in particular, the scrolling events. PB's handler will use SetScrollInfo_() to actually move the scrollthumb etc. and our procedure simply retrieves this new value via GetGadgetState(). Comment out the result = ... line and you will see that no movement of the scrollthumb takes place (tracking aside) which is the default behaviour for Windows.

Re: ScrollBar events only on release

Posted: Wed Dec 21, 2011 7:55 pm
by Trond
Wow, thank you! :!: :!:

I tried a PB window callback, and I tried subclassing the gadget, but I didn't think of subclassing the window. That being said, subclassing the gadget works, when I use your technique of calling the old callback at the start instead of at the end. I never would have figured that out.

Re: ScrollBar events only on release

Posted: Wed Dec 21, 2011 8:05 pm
by srod
Trond wrote:Wow, thank you! :!: :!:

I tried a PB window callback, and I tried subclassing the gadget, but I didn't think of subclassing the window. That being said, subclassing the gadget works, when I use your technique of calling the old callback at the start instead of at the end. I never would have figured that out.
Yes I do that so that PB can move the scrollbox for us, otherwise GetGadgetState() will return the incorrect value. Personally, I don't call the old callback at all for #WM_HSCROLL and instead move the scrollbox myself the appropriate distance using SetScrollInfo_() etc.

Re: ScrollBar events only on release

Posted: Fri Dec 23, 2011 1:26 pm
by charvista
@Trond
It looks like you asked the same question as me, in following your other topic http://www.purebasic.fr/english/viewtop ... 88#p369888 (ScrollBarGadget can't reach max?).
DoubleDutch gave me an answer that gave me a solution from RASHAD through a search in the forum: http://www.purebasic.fr/english/viewtop ... nt#p353306.
The difference with srod's solution is that RASHAD's code is portable (no APIs), but using a simple Thread.
Code below is from RASHAD, I modified it to have two scrollbars, and see if it can work with one Thread.

Code: Select all

;(c) Rashad 2011-05-22
; this will show in 'real-time' the current scrollbar position

Procedure Scrollbar(parameter)
    Repeat   
        SetGadgetText(1,Str(GetGadgetState(2)))
        SetGadgetText(4,Str(GetGadgetState(5)))
        Delay(40)
    ForEver
    
EndProcedure

OpenWindow(0, 0, 0, 305, 140, "ScrollBarGadget", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
    
    Vstart.i=50
    TextGadget       (0,  10,15, 220,  20, "VScrollBar Position : ",#PB_Text_Right)
    TextGadget       (1,  230,15, 20,  20, Str(Vstart),#PB_Text_Right)
    ScrollBarGadget  (2, 270, 10,  25, 120 ,0, 101, 1, #PB_ScrollBar_Vertical)
    SetGadgetState   (2,Vstart)
    
    Hstart.i=20
    TextGadget       (3,  0,90, 120,  20, "HScrollBar Position : ",#PB_Text_Right)
    TextGadget       (4,  120,90, 20,  20, Str(Hstart),#PB_Text_Right)
    ScrollBarGadget  (5, 0, 110,  270, 20 ,0, 101, 1)
    SetGadgetState   (5,Hstart)
    
    AddKeyboardShortcut(0, #PB_Shortcut_Up,10)
    AddKeyboardShortcut(0, #PB_Shortcut_Down,11)
    Thread = CreateThread(@Scrollbar(), 15)
    ThreadPriority(Thread, 1)
    Repeat
        Select WaitWindowEvent(1)     
            Case #PB_Event_CloseWindow
                Quit = 1       
                
            Case #PB_Event_Menu
                Select EventMenu()
                    Case 10
                        SetGadgetState(2,GetGadgetState(2) - 1)
                        SetGadgetText(1,Str(GetGadgetState(2)))
                        
                    Case 11
                        SetGadgetState(2,GetGadgetState(2) + 1)
                        SetGadgetText(1,Str(GetGadgetState(2)))
                        
                EndSelect
                
            Case #PB_Event_Gadget
                Select EventGadget()
                    Case 2
                        
                EndSelect       
                
        EndSelect
    Until Quit = 1
CloseWindow(0)

Different approaches, same result.
Cheers!

Re: ScrollBar events only on release

Posted: Fri Dec 23, 2011 2:38 pm
by RASHAD
Using Windows CallBack

Code: Select all

Global Backg1,Backg2,OldVPos,OldHPos

Backg1 = CreateSolidBrush_($BDFFFF)
Backg2 = CreateSolidBrush_($DFFFDD)

Procedure WndProc(hwnd, uMsg, wParam, lParam)  
  result = #PB_ProcessPureBasicEvents
  Select uMsg
 
    Case #WM_CTLCOLORSCROLLBAR
        If lParam = GadgetID(0)
          Result = Backg1
        Else
          Result = Backg2
        EndIf          

    Case #WM_MOUSEWHEEL
      If (wParam >> 16 & $FFFF) <> $FF88
        SetGadgetState(1,GetGadgetState(1) - 1)
      Else
        SetGadgetState(1,GetGadgetState(1) + 1)
      EndIf
      
    Case #WM_KEYDOWN ,#WM_MENUSELECT
        
        Select wParam
            Case #VK_UP
              SetGadgetState(1,GetGadgetState(1) - 1)
              
            Case #VK_DOWN
              SetGadgetState(1,GetGadgetState(1) + 1)
            
            Case #VK_LEFT
              SetGadgetState(0,GetGadgetState(0) - 1)
            
            Case #VK_RIGHT
              SetGadgetState(0,GetGadgetState(0) + 1)            
           
        EndSelect                  
  EndSelect
  
        If IsGadget(0) And OldHPos <> GetGadgetState(0)
           OldHPOs = GetGadgetState(0)
           Debug "H.ScrollBar Pos.: "+Str(OldHPos)
        ElseIf IsGadget(1) And OldVPos <> GetGadgetState(1)
           OldVPOs = GetGadgetState(1)
           Debug "V.ScrollBar Pos.: "+Str(OldVPos)
        EndIf
  
  ProcedureReturn result 
EndProcedure

OpenWindow(0, 0, 0, 300, 250, "", #PB_Window_ScreenCentered | #PB_Window_SystemMenu | #PB_Window_SizeGadget | #PB_Window_MinimizeGadget | #PB_Window_MaximizeGadget)
ScrollBarGadget(0, 10, 10,200,18, 0, 100, 1)
ScrollBarGadget(1, 10, 40, 18,200, 0, 100, 1,#PB_ScrollBar_Vertical)
Frame3DGadget(2, 9, 9, 203, 21, "",#PB_Frame3D_Double)
Frame3DGadget(3, 9, 39, 21, 203, "",#PB_Frame3D_Double)
SetWindowCallback(@WndProc())

Repeat
  Select WaitWindowEvent()
    Case #PB_Event_Gadget
      Select EventGadget()
        Case 0
        
        Case 1

      EndSelect
       
    Case #PB_Event_CloseWindow
      Quit = 1
      
  EndSelect
Until Quit = 1


Re: ScrollBar events only on release

Posted: Fri Dec 23, 2011 7:41 pm
by Kwai chang caine
Rashad wrote:Using Windows CallBack
Waoouuuh !!! :shock: 8)

Re: ScrollBar events only on release

Posted: Fri Dec 23, 2011 7:51 pm
by charvista
Waoouuuh !!! :shock: 8) :mrgreen: Rashad said once, KCC and me, we are trouble makers hahaha :lol:
Seriously now 8) . Rashad, excellent example. Nicely presented with CreateSolidBrush_ and Frame3DGadget. :D