How to control the window (scrolls) with #WS_VSCROLL ?

Just starting out? Need help? Post your questions and find answers here.
User avatar
zxtunes.com
Enthusiast
Enthusiast
Posts: 375
Joined: Wed Apr 23, 2008 7:51 am
Location: Saint-Petersburg, Russia
Contact:

How to control the window (scrolls) with #WS_VSCROLL ?

Post by zxtunes.com »

Code: Select all

OpenWindow(0,0,0,500,500,"TEST",#PB_Window_SystemMenu|#PB_Window_MinimizeGadget|#WS_VSCROLL|#WS_HSCROLL|#PB_Window_SizeGadget)

Repeat
  EventID = WaitWindowEvent()
Until EventID = #PB_Event_CloseWindow
it looks like ScrollAreaGadget, but not scrolling region. What enables the real time consuming to paint a image in the window (if I understood correctly). But I do not understand how to manage the scroll.

(I'm trying to simulate viewport in the window)
srod
PureBasic Expert
PureBasic Expert
Posts: 10589
Joined: Wed Oct 29, 2003 4:35 pm
Location: Beyond the pale...

Re: How to control the window (scrolls) with #WS_VSCROLL ?

Post by srod »

Scrolling is a quite complex business, especially if you scroll in response to the user dragging the scroll thumb etc. A scroll-area gadget essentially cheats and moves an inner container around in response to user actions etc.

Who am I kidding, it is a very complex business if not taking the scroll-area gadget kind of route! :wink:

The first thing to know is that Windows will not provide much in the way of assistance in this. It will not, for example, reposition the scrollbox in response to the user dragging the scroll thumb. This is because many applications will not scroll by a fixed amount whenever a scroll button is clicked. Imagine, for example, a word processing app with lines of text which vary in height. A vertical scroll may well then need to scroll by different amounds depending on, for example, the height of the top-most line etc. The only thing Windows will do, aside from sending you the relevant scroll messages, is paint the scrollbars!

The second thing to know, which is related to the first, is that Windows will most definitely not scroll the contents of a window for you in direct response to a user dragging a scroll thumb or hitting a scroll button etc. This is because it cannot second guess by how much you wish to scroll the window by! So this is really just a restatement of the above! :)

Now, there are functions which you can call which can assist. Functions such as ScrollWindowEx_() which will scroll a chunk of your window (it probably just BitBlt's it the required amount) and can invalidate that part of the window which has been left 'exposed' following the scroll. It is then down to your app's paint routine to fill in the 'void'.

If you wish/need to pursue this then I can point you towards my 'Scroller' utility (visit the nxSoftware site). The relevant page on the site goes into more detail regarding windows scrolling and the utility itself shows how to implement windows scrolling in a generic way. There are a few demos, including a scrolling image gadget built atop Scroller. You will of course note that Scroller, whilst it does scroll a windows contents where possible, does still rely upon the host application to paint the underlying window contents (or that part left exposed by the scroll etc.) Windows cannot do that for you. You will see then that some of the demos themselves are quite complex since they have to utilise their own painting routines - especially the scrolling image gadge.

What Scroller does not do is scroll child controls. That is, if you place a button on your window, for example, and scroll the window - then the button will stay put. It will not move. You have to add that yourself. I believe there is a flag for use with ScrollWindowEx_() which can do that. However, Scroller does not always use this function in it's scrolling routines.

When all said and done, I advise using a Scroll area gadget if possible. You can even paint directly onto the scroll area's inner container and let the Scroll area then worry about scrolling etc. This can be very effective. Just bear in mind the 32767 pixel limit to PB's ScrollArea scrolling abilities. The inner container I have mentioned cannot have dimensions in excess of this limit. It is a Windows limitation.

I hope this helps.
I may look like a mule, but I'm not a complete ass.
User avatar
zxtunes.com
Enthusiast
Enthusiast
Posts: 375
Joined: Wed Apr 23, 2008 7:51 am
Location: Saint-Petersburg, Russia
Contact:

Re: How to control the window (scrolls) with #WS_VSCROLL ?

Post by zxtunes.com »

Srod thanks for such extensive explanatory.
Yes I tried to use ScrollArea, it works but flickers. I move ImageGadget during movement scrool bar.

If the nobility as to avoid it (to cancel copying or to change for its procedure)

And here still is trouble with window resize. That that the window varies, and scroll remains on a place.

Therefore also it was pleasant to me window with #WS_VSCROLL that there scroll it is independently arranged under the size of a window.

Code: Select all


Procedure refresh()
  
  x = GetGadgetAttribute(0, #PB_ScrollArea_X)
  y = GetGadgetAttribute(0, #PB_ScrollArea_Y)
  ;GrabImage(0, 1, x,y, wh, ww)
  ResizeGadget(1, x, y, #PB_Ignore, #PB_Ignore)
  SetGadgetState(1,ImageID(0))
  
EndProcedure

Procedure SubClassProc(hwnd, msg, wParam, lParam)
  oldproc = GetProp_(hwnd, "oldproc")
  
  Select msg
    Case #WM_NCDESTROY
      RemoveProp_(hwnd, "oldproc")
      
    Case #WM_VSCROLL
      refresh()
      
    Case #WM_HSCROLL
      refresh()
      
  EndSelect   
  ProcedureReturn CallWindowProc_(oldproc, hwnd, msg, wParam, lParam)
EndProcedure

ww = 512
wh = 384
zoom = 16

If OpenWindow(0, 0, 0, ww, wh, "ScrollAreaGadget", #PB_Window_SystemMenu | #PB_Window_ScreenCentered|#PB_Window_SizeGadget)
  
  hwnd = ScrollAreaGadget(0, 0, 0, ww,wh, 256*zoom, 192*zoom, 3, #PB_ScrollArea_Raised)
    
  SetProp_(hwnd, "oldproc", SetWindowLongPtr_(hwnd, #GWL_WNDPROC, @SubClassProc()))
    
  CreateImage(0, ww, wh)
  ImageGadget(1,0,0, ww, wh, ImageID(0))
  refresh()
  CloseGadgetList()
  
  
  Repeat: Until WaitWindowEvent()=#PB_Event_CloseWindow
EndIf
srod
PureBasic Expert
PureBasic Expert
Posts: 10589
Joined: Wed Oct 29, 2003 4:35 pm
Location: Beyond the pale...

Re: How to control the window (scrolls) with #WS_VSCROLL ?

Post by srod »

Yes you'd be better off dispensing with the image gadget and drawing the image directly to the scroll-area's inner container.

Afraid I haven't time right now to hack up some code.
I may look like a mule, but I'm not a complete ass.
srod
PureBasic Expert
PureBasic Expert
Posts: 10589
Joined: Wed Oct 29, 2003 4:35 pm
Location: Beyond the pale...

Re: How to control the window (scrolls) with #WS_VSCROLL ?

Post by srod »

Okay, you twisted my arm!

The following is quite crude (being a quick hack) and probably does not do what you are after. However, it may get you started if you decide to try and work with a ScrollArea's inner container directly. I basically blt an image directly to this container. Note how I subclass, not the scroll-area, but it's inner container. Note also the #WM_ERASEBKGND message handler which removes all default erasing from the container as performed by Windows itself. We can do this because my paint handler paints the resized image to the entire client area of the container. If this was not the case then I would also have to undertake some custom erasing. To avoid flicker you would erase only those parts of the container not being painted upon. I also add the #WS_CLIPCHILDREN style to the scroll area.

Result = no flicker when scrolling the image.

This is very inefficient and sloppy code for a number of reasons, not the least of which is the fact that I blt the entire image whenever painting is required. In a proper app I would take steps to blt only that part of the image matching the invalidated portion of the client area etc.

As I say though, it may help.

Code: Select all

Procedure SubClassProc(hWnd, uMsg, wParam, lParam)
  Protected result, oldProc, ps.PAINTSTRUCT, width, height, hdcNew, oldImage
  oldproc = GetProp_(hWnd, "oldproc")
  Select uMsg
    Case #WM_NCDESTROY
      RemoveProp_(hwnd, "oldproc")
      result = CallWindowProc_(oldproc, hWnd, uMsg, wParam, lParam)
      
    Case #WM_ERASEBKGND
      result = 1
      
    Case #WM_PAINT
      hdc = BeginPaint_(hWnd, ps)
      If hdc
        hdcNew = CreateCompatibleDC_(hdc)
        If hdcNew
          width = GetGadgetAttribute(0, #PB_ScrollArea_InnerWidth)
          height = GetGadgetAttribute(0, #PB_ScrollArea_InnerHeight)
          ResizeImage(0, width, height)
          oldImage = SelectObject_(hdcNew, ImageID(0))
          BitBlt_(hdc, 0, 0, width, height, hdcNew, 0, 0, #SRCCOPY)
          SelectObject_(hdcNew, oldImage)
          DeleteDC_(hdcNew)
        EndIf
        EndPaint_(hWnd, ps)
      EndIf
      
    Default 
      result = CallWindowProc_(oldproc, hWnd, uMsg, wParam, lParam)
      
  EndSelect   
  ProcedureReturn result
EndProcedure

ww = 512
wh = 384
zoom = 16

CreateImage(0, ww, wh)
  
  If OpenWindow(0, 0, 0, ww, wh, "ScrollAreaGadget", #PB_Window_SystemMenu | #PB_Window_ScreenCentered|#PB_Window_SizeGadget)
  
  hwnd = ScrollAreaGadget(0, 0, 0, ww,wh, 256*zoom, 192*zoom, 3, #PB_ScrollArea_Raised)
  CloseGadgetList()
  SetWindowLongPtr_(hWnd, #GWL_STYLE, GetWindowLongPtr_(hWnd, #GWL_STYLE)|#WS_CLIPCHILDREN)
  hWnd = FindWindowEx_(hWnd, 0, "PureScrollAreaChild",0)
  SetProp_(hWnd, "oldproc", SetWindowLongPtr_(hWnd, #GWL_WNDPROC, @SubClassProc()))
    
  Repeat: Until WaitWindowEvent()=#PB_Event_CloseWindow
EndIf
**EDIT : if you are looking for a good way of zooming images etc. then I would recommend that you take a look at enhanced metafiles. They are easy to use and very very useful in this kind of situation. :wink:
I may look like a mule, but I'm not a complete ass.
User avatar
zxtunes.com
Enthusiast
Enthusiast
Posts: 375
Joined: Wed Apr 23, 2008 7:51 am
Location: Saint-Petersburg, Russia
Contact:

Re: How to control the window (scrolls) with #WS_VSCROLL ?

Post by zxtunes.com »

Srod Thanks for the help.
I experimented your program this way and that. But she or eats 100 % of the processor or flickers. Mysticism what that. :oops:

I'm beginning to think that it is easier to make a window with no scroll bar (Cheating), or draw scroll bar itself as the image. :o
RASHAD
PureBasic Expert
PureBasic Expert
Posts: 4953
Joined: Sun Apr 12, 2009 6:27 am

Re: How to control the window (scrolls) with #WS_VSCROLL ?

Post by RASHAD »

I do not know if I understood you correctly but....
This code for how to manage the scroll in a window client area
Adapt it for your needs and check MSDN for how to scroll bmp images

Code: Select all


Global xNewPos,yNewPos,xCurrentScroll,yCurrentScroll,fScroll,xDelta,yDelta,r.RECT,hScale,vScale

Procedure WndProc(hwnd, uMsg, wParam, lParam)
      GetClientRect_(WindowID(0),r.RECT)
      hScale = (r\right - 250)/100
      vScale = (r\bottom - 350)/100
;       SetScrollPos_(WindowID(0),#SB_VERT,yNewPos,1)
;       SetScrollPos_(WindowID(0),#SB_HORZ,xNewPos,1)
;       xCurrentScroll = GetScrollPos_(WindowID(0),#SB_HORZ)
;       yCurrentScroll = GetScrollPos_(WindowID(0),#SB_VERT)
      result = #PB_ProcessPureBasicEvents 

Select uMsg
        Case #WM_HSCROLL

            Select wParam & $FFFF
               ;User clicked the scroll bar shaft left of the scroll box. 
                Case #SB_PAGEUP 
                    xNewPos = xCurrentScroll - 10

               ;User clicked the scroll bar shaft right of the scroll box. 
                Case #SB_PAGEDOWN
                    xNewPos = xCurrentScroll + 10
 
               ;User clicked the left arrow. 
                Case #SB_LINEUP 
                    xNewPos = xCurrentScroll - 1
  
                ;User clicked the right arrow. 
                Case #SB_LINEDOWN 
                    xNewPos = xCurrentScroll + 1
 
                ;User dragged the scroll box. 
                Case #SB_THUMBPOSITION 
                    xNewPos = wParam >> 16 & $FFFF
                    
           EndSelect 
               
                
            xDelta = (xNewPos - xCurrentScroll)*hScale 
            fScroll = 1            
            xCurrentScroll = xNewPos
            
            If xNewPos <= 0
               xNewPos = 0
               xCurrentScroll = 0
               xDelta = 0
            ElseIf xNewPos >= 100
               xNewPos = 100
               xCurrentScroll = 100
               xDelta = 0
            EndIf

;  
;             // Scroll the window. (The system repaints most of the 
;             // client area when ScrollWindowEx is called; however, it is 
;             // necessary To call UpdateWindow in order To repaint the 
;             // rectangle of pixels that were invalidated.) 
            ScrollWindowEx_(WindowID(0),xDelta,0,0,0,0,0,#SW_ERASE)            
            SetScrollPos_(WindowID(0),#SB_HORZ,xNewPos,fScroll)
            ;UpdateWindow_(WindowID(0))
 
            si.SCROLLINFO
            si\cbSize = SizeOf(si) 
            si\fMask  = #SIF_POS 
            si\nPos   = xCurrentScroll 
            SetScrollInfo_(WindowID(0),#SB_HORZ, @si, 1)
            


            
            
        Case #WM_VSCROLL 
;         { 
;             int xDelta = 0; 
;             int yDelta;     // yDelta = new_pos - current_pos 
;             int yNewPos;    // new position 
;  
            Select wParam & $FFFF 

                ;User clicked the scroll bar shaft above the scroll box. 
                Case #SB_PAGEUP 
                    yNewPos = yCurrentScroll - 10
 
                ;User clicked the scroll bar shaft below the scroll box. 
                Case #SB_PAGEDOWN 
                    yNewPos = yCurrentScroll + 10
 
                ;User clicked the top arrow. 
                Case #SB_LINEUP 
                    yNewPos = yCurrentScroll - 1
 
                ;User clicked the bottom arrow. 
                Case #SB_LINEDOWN 
                    yNewPos = yCurrentScroll + 1
 
                ;User dragged the scroll box. 
                Case #SB_THUMBPOSITION 
                    yNewPos = wParam >> 16 & $FFFF
                    
          EndSelect
 
 
            ;Set the scroll flag To TRUE. 
            fScroll = 1 
 
            ;Determine the amount scrolled (in pixels). 
            yDelta = (yNewPos - yCurrentScroll)*vScale
 
            ;Reset the current scroll position. 
            yCurrentScroll = yNewPos
            
           If yNewPos <= 0
               yNewPos = 0
               yCurrentScroll = 0
               yDelta = 0
           ElseIf yNewPos >= 100
               yNewPos = 100
               yCurrentScroll = 100
               yDelta = 0
           EndIf             

            ScrollWindowEx_(WindowID(0),0,yDelta,0,0,0,0,#SW_ERASE)            
            SetScrollPos_(WindowID(0),#SB_VERT,yNewPos,1)
            ;UpdateWindow_(WindowID(0))     
 
            si.SCROLLINFO 
            si\cbSize = SizeOf(si) 
            si\fMask  = #SIF_POS 
            si\nPos   = yCurrentScroll 
            SetScrollInfo_(WindowID(0),#SB_VERT,@si,1)
     
     
     Case #WM_NOTIFY
               
      
     Case #WM_SIZE,#WM_MOVE,#WM_PAINT
           
            

   EndSelect   
  ProcedureReturn result 
EndProcedure



LoadImage(0, "girl2.bmp")
OpenWindow(0,0,0,600,600,"TEST",#PB_Window_SystemMenu|#PB_Window_MinimizeGadget|#PB_Window_MaximizeGadget|#WS_VSCROLL|#WS_HSCROLL|#PB_Window_SizeGadget|#PB_Window_ScreenCentered)
ImageGadget(0,10,10,250,350, ImageID(0))
DisableGadget(0,1)

SetWindowCallback(@WndProc())

Repeat
  EventID = WaitWindowEvent()
Until EventID = #PB_Event_CloseWindow

Egypt my love
srod
PureBasic Expert
PureBasic Expert
Posts: 10589
Joined: Wed Oct 29, 2003 4:35 pm
Location: Beyond the pale...

Re: How to control the window (scrolls) with #WS_VSCROLL ?

Post by srod »

Rashad, there are all sorts of repainting issues with that code here.
zxtunes.com wrote:Srod Thanks for the help.
I experimented your program this way and that. But she or eats 100 % of the processor or flickers. Mysticism what that. :oops:

I'm beginning to think that it is easier to make a window with no scroll bar (Cheating), or draw scroll bar itself as the image. :o
Yes, I told you it was very inefficient code. I resize the image whenever #WM_PAINT fires for one! I then blt the entire image rather than just that corresponding to the update rectangle etc. That's what you get for a quick hack! It is a tried and tested method however and somewhat easier than dealing with ScrollWindowEx_() and the like. Interstingly though the sloppy code I posted above uses next to no cpu time here, even when scrolling! It really shouldn't use that much cpu time even with the sloppiness thrown in! :)

I'll point you again to my Scroller utility if you want to see a scrolling image gadget without using a ScrollArea etc.
I may look like a mule, but I'm not a complete ass.
Post Reply