Page 1 of 1

Scrollbar event work-around?

Posted: Tue Sep 18, 2007 7:18 pm
by Mistrel
I just submitted this bug for scrollbar events. On the project I'm currently working on I need to know when the scrollbar is activated to know when to update the work area. Does anyone know how work around this bug?

http://www.purebasic.fr/english/viewtopic.php?t=28797

Posted: Tue Sep 18, 2007 8:55 pm
by Xombie
Wow. A coding question that hasn't been answered yet. I'm shocked and amazed. I actually get to answer something @_@

I patched this together for you to try out. Unfortunately, I only know of callbacks giving you this ability.

Code: Select all

Global offset.POINT
Procedure TestCallback(Handle.l, uMsg.l, wParam.l, lParam.l)
   ;
   Protected lResult.l
   ;
   Protected AllowScroll.l
   ;
   Protected HoldInfo.SCROLLINFO
   ;
   If uMsg = #WM_HSCROLL
      ;{ User is scrolling a horizontal scrollbar.
      ; If lParam = GadgetID(0)
      ;/ This would be a place to test which scrollbar is used.
      HoldInfo\cbSize = SizeOf(SCROLLINFO)
      ;
      HoldInfo\fMask = #SIF_ALL
      ;
      GetScrollInfo_(lParam, #SB_CTL, @HoldInfo) 
      ;
      AllowScroll = #True
      ; Allow scrolling by default.
      If wParam & $FFFF = #SB_THUMBTRACK
         HoldInfo\nPos = HoldInfo\nTrackPos
      ElseIf wParam & $FFFF = #SB_LINERIGHT
         HoldInfo\nPos + 1
      ElseIf wParam & $FFFF = #SB_LINELEFT
         HoldInfo\nPos - 1
      ElseIf wParam & $FFFF = #SB_PAGERIGHT
         HoldInfo\nPos + 3
      ElseIf wParam & $FFFF = #SB_PAGELEFT
         HoldInfo\nPos - 3
      Else
         ; The scroll message is unhandled.
         AllowScroll = #False
         ; Do not allow the user to scroll.
      EndIf
      ;
      If AllowScroll
         ;
         If HoldInfo\nPos <> offset\X
            ;
            offset\X = HoldInfo\nPos
            ;
         Else
            ;
            ProcedureReturn #True
            ;
         EndIf
         ;
      Else
         ;
         ProcedureReturn #True
         ;
      EndIf
      ;
      ;}
   ElseIf uMsg = #WM_VSCROLL
      ;{ User is scrolling a vertical scrollbar.
      ; If lParam = GadgetID(1)
      ;/ This would be a place to test which scrollbar is used.
      HoldInfo\cbSize = SizeOf(SCROLLINFO)
      ; Store the size of the ScrollInfo structure.
      HoldInfo\fMask = #SIF_ALL
      ; All settings are needed.
      GetScrollInfo_(lParam, #SB_CTL, @HoldInfo) 
      ; Retrieve information on the scrollbar.
      AllowScroll = #True
      ; Allow scrolling by default.
      If wParam & $FFFF = #SB_THUMBTRACK
         HoldInfo\nPos = HoldInfo\nTrackPos
      ElseIf wParam & $FFFF = #SB_LINEUP
         HoldInfo\nPos - 1
      ElseIf wParam & $FFFF = #SB_LINEDOWN
         HoldInfo\nPos + 1
      ElseIf wParam & $FFFF = #SB_PAGEUP
         HoldInfo\nPos - 3
      ElseIf wParam & $FFFF = #SB_PAGEDOWN
         HoldInfo\nPos + 3
      Else
         ; The scroll message is unhandled.
         AllowScroll = #False
         ; Do not allow the user to scroll.
      EndIf
      ;
      If AllowScroll
         ;
         If HoldInfo\nPos <> offset\Y
            offset\Y = HoldInfo\nPos
         Else
            ProcedureReturn #True
         EndIf
         ;
      Else
         ; The user is not allowed to scroll to the desired position.
         ProcedureReturn #True
         ; Do not process the scrollbar event.
      EndIf
      ;}
   EndIf
   ;
   ProcedureReturn #PB_ProcessPureBasicEvents 
   ;
EndProcedure
If OpenWindow(0, 0, 0, 305, 140, "ScrollBarGadget", #PB_Window_SystemMenu | #PB_Window_ScreenCentered) And CreateGadgetList(WindowID(0)) 
   TextGadget       (2,  10, 25, 250,  20, "ScrollBar Standard  (start=50, page=30/100)",#PB_Text_Center) 
   ScrollBarGadget  (0,  10, 42, 250,  20, 0, 100, 30) 
   SetGadgetState   (0,  50)   ; set 1st scrollbar (ID = 0) to 50 of 100 
   TextGadget       (3,  10,115, 250,  20, "ScrollBar Vertical  (start=100, page=50/300)",#PB_Text_Right) 
   ScrollBarGadget  (1, 270, 10,  25, 120 ,0, 300, 50, #PB_ScrollBar_Vertical) 
   SetGadgetState   (1, 100)   ; set 2nd scrollbar (ID = 1) to 100 of 300 
   SetWindowCallback(@TestCallback())
   Repeat 
      Event=WaitWindowEvent() 
      EventGadget=EventGadget() 
      If Event=#PB_Event_Gadget And EventGadget=0 
         Debug "1 : "+Str(EventType())
      EndIf 
      If Event=#PB_Event_Gadget And EventGadget=1 
         Debug "2 : "+Str(EventType())
      EndIf 
   Until Event=#PB_Event_CloseWindow 
EndIf
You'd want to put your code in the "offset\X = HoldInfo\nPos" and "offset\Y = HoldInfo\nPos" block because that's where the position is actually updated.

Also, this will enable tracking with the scrollbar and allow you to update as the user tracks versus waiting until they release the mouse.

Good luck.

Posted: Tue Sep 18, 2007 9:03 pm
by Mistrel
As some questions do go unanswered I'm always grateful to anyone who tries to help.

Thank you! :)

I have a few other questions if you don't mind?

I'm having some trouble with DisableGadget(). After disabling a scrollbar modifying it with SetGadgetAttribute(#scrollbar,#PB_ScrollBar_PageLength,#window) enables it. This reaction is very undesirable. Is there another way through API flags to make the bar look disabled and override this?

Also, when I disable a gadget using DisableGadget() I have to set my own variable flag for each one if I want to check and to see if it's currently disabled. Is there a way to return whether a gadget has been disabled in this way? Maybe by setting an API disabled flag on the control and reading from that?

I think part of the code you've already provided can help but I'm unfamiliar with how to work with callbacks. Sorry if I'm asking an obvious question for an anwer you may already given me. :roll:

Posted: Tue Sep 18, 2007 9:14 pm
by Xombie
More code...

Code: Select all

Global offset.POINT 
Procedure TestCallback(Handle.l, uMsg.l, wParam.l, lParam.l) 
   ; 
   Protected lResult.l 
   ; 
   Protected AllowScroll.l 
   ; 
   Protected HoldInfo.SCROLLINFO 
   ; 
   If uMsg = #WM_HSCROLL 
      ;{ User is scrolling a horizontal scrollbar. 
      ; If lParam = GadgetID(0) 
      ;/ This would be a place to test which scrollbar is used. 
      HoldInfo\cbSize = SizeOf(SCROLLINFO) 
      ; 
      HoldInfo\fMask = #SIF_ALL 
      ; 
      GetScrollInfo_(lParam, #SB_CTL, @HoldInfo) 
      ; 
      AllowScroll = #True 
      ; Allow scrolling by default. 
      If wParam & $FFFF = #SB_THUMBTRACK 
         HoldInfo\nPos = HoldInfo\nTrackPos 
      ElseIf wParam & $FFFF = #SB_LINERIGHT 
         HoldInfo\nPos + 1 
      ElseIf wParam & $FFFF = #SB_LINELEFT 
         HoldInfo\nPos - 1 
      ElseIf wParam & $FFFF = #SB_PAGERIGHT 
         HoldInfo\nPos + 3 
      ElseIf wParam & $FFFF = #SB_PAGELEFT 
         HoldInfo\nPos - 3 
      Else 
         ; The scroll message is unhandled. 
         AllowScroll = #False 
         ; Do not allow the user to scroll. 
      EndIf 
      ; 
      If AllowScroll 
         ; 
         If HoldInfo\nPos <> offset\X 
            ; 
            offset\X = HoldInfo\nPos 
            ; 
         Else 
            ; 
            ProcedureReturn #True 
            ; 
         EndIf 
         ; 
      Else 
         ; 
         ProcedureReturn #True 
         ; 
      EndIf 
      ; 
      ;} 
   ElseIf uMsg = #WM_VSCROLL 
      ;{ User is scrolling a vertical scrollbar. 
      ; If lParam = GadgetID(1) 
      ;/ This would be a place to test which scrollbar is used. 
      HoldInfo\cbSize = SizeOf(SCROLLINFO) 
      ; Store the size of the ScrollInfo structure. 
      HoldInfo\fMask = #SIF_ALL 
      ; All settings are needed. 
      GetScrollInfo_(lParam, #SB_CTL, @HoldInfo) 
      ; Retrieve information on the scrollbar. 
      AllowScroll = #True 
      ; Allow scrolling by default. 
      If wParam & $FFFF = #SB_THUMBTRACK 
         HoldInfo\nPos = HoldInfo\nTrackPos 
      ElseIf wParam & $FFFF = #SB_LINEUP 
         HoldInfo\nPos - 1 
      ElseIf wParam & $FFFF = #SB_LINEDOWN 
         HoldInfo\nPos + 1 
      ElseIf wParam & $FFFF = #SB_PAGEUP 
         HoldInfo\nPos - 3 
      ElseIf wParam & $FFFF = #SB_PAGEDOWN 
         HoldInfo\nPos + 3 
      Else 
         ; The scroll message is unhandled. 
         AllowScroll = #False 
         ; Do not allow the user to scroll. 
      EndIf 
      ; 
      If AllowScroll 
         ; 
         If HoldInfo\nPos <> offset\Y 
            offset\Y = HoldInfo\nPos 
         Else 
            ProcedureReturn #True 
         EndIf 
         ; 
      Else 
         ; The user is not allowed to scroll to the desired position. 
         ProcedureReturn #True 
         ; Do not process the scrollbar event. 
      EndIf 
      ;} 
   EndIf 
   ; 
   ProcedureReturn #PB_ProcessPureBasicEvents 
   ; 
EndProcedure 
If OpenWindow(0, 0, 0, 305, 140, "ScrollBarGadget", #PB_Window_SystemMenu | #PB_Window_ScreenCentered) And CreateGadgetList(WindowID(0)) 
   TextGadget       (2,  10, 25, 250,  20, "ScrollBar Standard  (start=50, page=30/100)",#PB_Text_Center) 
   ScrollBarGadget  (0,  10, 42, 250,  20, 0, 100, 30) 
   SetGadgetState   (0,  50)   ; set 1st scrollbar (ID = 0) to 50 of 100 
   TextGadget       (3,  10,115, 250,  20, "ScrollBar Vertical  (start=100, page=50/300)",#PB_Text_Right) 
   ScrollBarGadget  (1, 270, 10,  25, 120 ,0, 300, 50, #PB_ScrollBar_Vertical) 
   SetGadgetState   (1, 100)   ; set 2nd scrollbar (ID = 1) to 100 of 300 
   ButtonGadget(4, 0, 0, 20, 20, "D")
   ButtonGadget(5, 20, 0, 20, 20, "S")
   ButtonGadget(6, 40, 0, 20, 20, "E")
   ButtonGadget(7, 60, 0, 20, 20, "?")
   SetWindowCallback(@TestCallback()) 
   Repeat 
      Event=WaitWindowEvent() 
      EventGadget=EventGadget() 
      If Event=#PB_Event_Gadget And EventGadget=0 
         Debug "1 : "+Str(EventType()) 
      EndIf 
      If Event=#PB_Event_Gadget And EventGadget=1 
         Debug "2 : "+Str(EventType()) 
      EndIf 
      If Event=#PB_Event_Gadget And EventGadget=4 And EventType() = #PB_EventType_LeftClick
         DisableGadget(0, #True)
      EndIf
      If Event=#PB_Event_Gadget And EventGadget=5 And EventType() = #PB_EventType_LeftClick
         SetGadgetAttribute(0, 35, 0)
      EndIf
      If Event=#PB_Event_Gadget And EventGadget=6 And EventType() = #PB_EventType_LeftClick
         DisableGadget(0, #False)
      EndIf
      If Event=#PB_Event_Gadget And EventGadget=7 And EventType() = #PB_EventType_LeftClick
         If GetWindowLong_(GadgetID(0), #GWL_STYLE) & #WS_DISABLED : Debug "Disabled" : Else : Debug "Enabled" : EndIf
      EndIf
   Until Event=#PB_Event_CloseWindow 
EndIf
So now there are 4 buttons at the top: D, S, E, ?

D disables the horizontal scrollbar. S sets the attribute and E enables the scrollbar. ? should output whether the scrollbar is enabled or not.

I'm not seeing the behavior you're seeing. I click D to disable the scrollbar and then S to set the attribute as you say and the scrollbar is still disabled.

So...? Is that not how you're doing it?

Posted: Tue Sep 18, 2007 9:30 pm
by Mistrel
You changed your last post/question since I was writing this one. I'm testing your code now..

This is what I said:

What I ultimately want (and hopefully I'm not trying to reinvent the wheel here) is a scrollbar area where the scrollbars are disabled instead of hidden if the window is resized large than the scroll area.

Here is my current project file. Please forgive the mess. I don't clean my code until I've got it working the way I want it.

When you run this example and you'll see that I have a scrollbar area gadget, two overriding scrollbars, and an overriding resize area in the corner. If you drag the bottom scrollbar you'll see that I can only update the scrollbar area on mouse up because that's the only event returned by WindowEvent.

The whole disabling thing has to do with how the scrollbars are disabled when the window size is larger than the scrollbar area in particular dimension. When I resize the disabled scrollbar as the window is resized I have to disable it again, so it would help to know if it's already in a disabled state. Otherwise I have to track their state in a unique variable for each scrollbar.

Code: Select all

Removed by author

Posted: Tue Sep 18, 2007 9:35 pm
by Mistrel
I'm not seeing the behavior you're seeing. I click D to disable the scrollbar and then S to set the attribute as you say and the scrollbar is still disabled.
Here is an example of the behavior I'm talking about. I don't know where you came up with "35" as an attribute.

Code: Select all

If OpenWindow(0, 0, 0, 305, 140, "ScrollBarGadget", #PB_Window_SystemMenu | #PB_Window_ScreenCentered) And CreateGadgetList(WindowID(0))
   ScrollBarGadget  (0,  10, 42, 250,  20, 0, 100, 30)
   DisableGadget(0,1)
   SetGadgetAttribute(0,#PB_ScrollBar_PageLength,50)
   Repeat
   	Event=WaitWindowEvent()
   Until Event=#PB_Event_CloseWindow
EndIf

Posted: Tue Sep 18, 2007 9:57 pm
by Xombie
Oh. Try this as a replacement for SetGadgetAttribute() - but only for your scrollbars.

Code: Select all

Procedure SetScrollAttribute(Gadget.l, Attribute.l, Value.l)
   ;
   Protected Handle.l
   ;
   Protected HoldInfo.SCROLLINFO 
   ;
   Protected IsDisabled.l
   ;
   If GadgetType(Gadget) <> #PB_GadgetType_ScrollBar : ProcedureReturn : EndIf
   ;
   Handle = GadgetID(Gadget)
   ;
   IsDisabled = GetWindowLong_(Handle, #GWL_STYLE) & #WS_DISABLED
   ;
   HoldInfo\cbSize = SizeOf(SCROLLINFO) 
   ; 
   HoldInfo\fMask = #SIF_ALL 
   ; 
   GetScrollInfo_(Handle, #SB_CTL, @HoldInfo) 
   ; 
   If Attribute = #PB_ScrollBar_Minimum
      ;
      HoldInfo\nMin = Value
      ;
   ElseIf Attribute = #PB_ScrollBar_Maximum
      ;
      HoldInfo\nMax = Value 
      ; 
   ElseIf Attribute = #PB_ScrollBar_PageLength
      ;
      HoldInfo\nPage = Value
      ;
   Else
      ;
      ProcedureReturn
      ;
   EndIf
   ;
   If IsDisabled : SetScrollInfo_(Handle, #SB_CTL, @HoldInfo, #False) : Else : SetScrollInfo_(Handle, #SB_CTL, @HoldInfo, #True) : EndIf
   ;
EndProcedure
(Edited: Try the code again, please. I forgot also allowing for enabled scrollbars and changing the lParam to Handle - old code :) )

Posted: Tue Sep 18, 2007 11:52 pm
by Mistrel
Thank you for your help. I have one more question regarding your callback example.

I need to know which gadget has been activated. I tried outputting the handle.l that is used within TestCallback but it doesn't match the GadgetID() of either scroll bar.

How can the callback be used to return the gadget number or gadget id of the active scrollbar?

I would also like to change the number of pixels/lines moved while pressing one of the directional arrows. By default my scrollbars are only moving one pixel at a time.

I don't see an attribute for this in the SCROLLINFO structure but I'll keep looking.

Posted: Wed Sep 19, 2007 12:21 am
by Xombie
Do you see the two lines in the callback that look like this...

Code: Select all

If lParam = GadgetID(1)
Just above that, add this line...

Code: Select all

Debug "PB Gadget ID: "+Str(GetDlgCtrlID_(lParam))
And that will tell you which Gadget ID is using the callback currently. lParam will contain the handle to the scrollbar for the scroll events. You can handle it by testing with "If lParam..." or "If GetDlgCtrlID_(...." and that's up to your coding style.

You can certainly adjust the amount you scroll by changing the values within this block...

Code: Select all

      If wParam & $FFFF = #SB_THUMBTRACK 
         HoldInfo\nPos = HoldInfo\nTrackPos 
      ElseIf wParam & $FFFF = #SB_LINERIGHT 
         HoldInfo\nPos + 1 
      ElseIf wParam & $FFFF = #SB_LINELEFT 
         HoldInfo\nPos - 1 
      ElseIf wParam & $FFFF = #SB_PAGERIGHT 
         HoldInfo\nPos + 3 
      ElseIf wParam & $FFFF = #SB_PAGELEFT 
         HoldInfo\nPos - 3 
      Else 
         ; The scroll message is unhandled. 
         AllowScroll = #False 
         ; Do not allow the user to scroll. 
      EndIf 
Adjust the amounts like "HoldInfo\nPos + 10" or whatever value you like under the #SB_...... constants. Hopefully those make sense for you.

Remember that there are two events - #WM_HSCROLL and #WM_VSCROLL and you need to handle both in a similar way.

Hope that helps!

Posted: Wed Sep 19, 2007 7:29 am
by Mistrel
It does! I've almost got it working perfectly but I ran into another problem.

Change line 313 from this

Code: Select all

ResizeGadget(scroll_area,0,0,WindowWidth(main_window)-25,WindowHeight(main_window)-25)
to this

Code: Select all

ResizeGadget(scroll_area,0,0,WindowWidth(main_window),WindowHeight(main_window))
The scroll area gadget is stealing focus from my scrollbars even though it's supposed to be behind them. I don't know what to do about this. :?

Code: Select all

Removed by author

Posted: Wed Sep 19, 2007 9:11 pm
by Xombie
Lack of sleep is making my brain dull. What do you mean exactly by "The scroll area gadget is stealing focus from my scrollbars even though it's supposed to be behind them. I don't know what to do about this." ?

More precisely "supposed to be behind them" ? Maybe if I knew the purpose of the application I'd be able to fully grasp what is going on.

Posted: Thu Sep 20, 2007 1:10 am
by Mistrel
I made a post about it here
http://www.purebasic.fr/english/viewtopic.php?t=28802

I've solved that problem with help some some people on the forum but I'm still working on getting rid of a few other bugs.

Posted: Thu Sep 20, 2007 2:25 am
by Mistrel
It's finished! Thank you very much for helping me with this, Xombie. I've learned a lot from working with your code. :D

http://www.purebasic.fr/english/viewtop ... 834#210834