Scrollbar similar to Windows'

Started by
1 comment, last by captain_crunch 8 years ago

I have a working scrollbar in my UI, but I am not completely satisfied with it because it does not behave like the Windows' scrollbar in the following respect:

When I drag the scrollbar thumb in Windows, the scrolling stops when the thumb meets the end of the trough. But in my implementation, I need to continue dragging the mouse after the thumb is at the end because it is the mouse cursor that controls scrolling. Only when the mouse cursor is at the end of the trough does the scrolling stop.

I have worked on this problem for a while but cannot crack it. Everything else is working except this feature.

Here is some of my my code:


 protected void OnThumbMove(MouseEventArgs args)
        {
            if (this.draggingThumb)
            {
                int yPosition; // the cursor position in the trough
               
                int beforeY = this.thumb.Y;  // Get the position from before move
                yPosition = this.thumb.Y + dragPoint - topButton.Height; // dragpoint is the relative position that the cursor stays attached to the thumb while dragging
                int yDiff = args.Position.Y - this.lastLocation.Y;

                yPosition += yDiff;
                              
                // probably needs some scaling code here so the thumb only moves in a limited range...
                      

                ScrollTo(MouseToScrollPosition(yPosition));
                            

                // set the new location after clamping:
                this.lastLocation.Y += this.thumb.Y - beforeY;
            }
        }
Advertisement

Let me take a shot at it: (this is just me thinking it through mentally, not having done it before)

First, we have to know how large the scrollbar "groove" is (excluding the up/down scrollbar buttons), which is determined by the parent widget. Let's say, "grooveHeight = 213 pixels" as an arbitrary number. This groove represents 100% of the content in the scrollable region, including the content offscreen.

Next, we need to know how large the scrollbar "thumb" is. The thumb represents only the content onscreen, so we need to know the amount of total content (totalContentHeight = 462 pixels), and the amount of content that can be visible onscreen at once (visibleRegionHeight = 230 pixels), and get the percentage between them (visibleRegionHeight / totalContentHeight = 0.5), which means "scrollbarThumbHeight = (grooveHeight * 0.5) = 106 pixels".

(Note: you want to have the thumb have a minimimum size - say, 10-15 pixels, otherwise it could get so small it's too hard to drag (or even impossible if it becomes 0 pixels)).

Now when you drag the thumb, you don't let the thumb's top go above 0, and you don't let the thumb's top go below (grooveHeight - thumbHeight).

To figure out what region of the screen is visible, you get the thumb's top position (say, at 61 pixels from the top of the groove), and divide it by the total groove height (213 pixels) to get a percentage (0.29).

Then, you multiply that percentage against the total content height (which we said was 462 pixels, giving us topOfVisibleRegion = 134), which is the top (pixel-wise) of the visible region, with the bottom of the visible region being (topOfVisibleRegion + visibleRegionHeight).

Basically, the scrollbar groove and thumb are at a different scale than the page they represent. The scrollbar's groove represents the entire content area, but must fit into the visible content area, and so must be scaled down. The thumb (scaled at the same scale as the groove) represents the visible content area, but it obviously can't fill the visible content area, or it'd be filling the entire groove and have no groove-space to scroll.

Internally, you'd want to use a measurement that is fine enough for the entire totalContentHeight, so you should store your "position" at that scale (i.e. total content pixel scale), and only scale it down for display purposes, or when calculating the new position based off a mouse-drag. That way, when you programmatically set the location of the bar (for example, in response to the scroll wheel or PageUp/PageDown, or in response to clicks on the scrollbar's up/down buttons), you can do so with finer precision than merely the amount of height the parent widget happens to give.

The mistake I seemed to have made was in the ratios. I have a property Range which defines the portion of the panel that cannot be viewed. This property should have the same ratio to the panel height as the gap (area not occupied by the thumb) has to the scrollbar height.

This topic is closed to new replies.

Advertisement