August 11th, 2003

Scrollbars part 7 – Integrality

If you play around with resizing the window, you can get a fractional line to appear at the bottom of the screen. This is not normally a problem until you scroll to the very end of the list, say, by pressing the End key, at which point an ugly blank space appears at the bottom. This ugly blank space is particularly disturbing when the fractional line is very nearly an entire line, because it looks like there is an off-by-one bug in the code somewhere.

We can fix this by forcing the window size to be an exact integral multiple of the line height. Like adding scrollbars, there is the basic idea, followed by a lot of detail work to get it just right.

The basic idea is to enforce integrality in the window resize code. The right place to do this is in the WM_WINDOWPOSCHANGING handler, which allows you to adjust the placement of the window before it is actually moved. This avoids flicker.

We’ll break the bulk of the work into a helper function, which will prove useful later.

void AdjustSizeRectangle(HWND hwnd, WPARAM wmsz, LPRECT prc)
{
    RECT rc;
    int cyClient;
    int cyAdjust;
    /* Compute the resulting client height */
    SetRectEmpty(&rc);
    AdjustWindowRect(&rc, GetWindowStyle(hwnd), FALSE);
    cyClient = (prc->bottom - prc->top) - (rc.bottom - rc.top);
    /* Compute the number of fractional pixels */
    cyAdjust = cyClient % g_cyLine;
    /*
     *  Remove the fractional pixels from the top or bottom.
     */
    switch (wmsz) {
    case WMSZ_TOP:
    case WMSZ_TOPLEFT:
    case WMSZ_TOPRIGHT:
        prc->top += cyAdjust;
        break;
    default:
        prc->bottom -= cyAdjust;
        break;
    }
}

The WM_WINDOWPOSCHANGNG handler then check if the window size is changing, in which case we adjust the size rectangle to enforce integrality of the client area. We say that the adjustments should be taken from the bottom of the window.

BOOL OnWindowPosChanging(HWND hwnd, LPWINDOWPOS pwp)
{
    if (!(pwp->flags & SWP_NOSIZE)) {
        RECT rc = { 0, 0, pwp->cx, pwp->cy };
        AdjustSizeRectangle(hwnd, WMSZ_BOTTOM, &rc);
        pwp->cy = rc.bottom;
    }
    return 0;
}
    /* Add to WndProc */
    HANDLE_MSG(hwnd, WM_WINDOWPOSCHANGING, OnWindowPosChanging);
Topics
Code

Author

Raymond has been involved in the evolution of Windows for more than 30 years. In 2003, he began a Web site known as The Old New Thing which has grown in popularity far beyond his wildest imagination, a development which still gives him the heebie-jeebies. The Web site spawned a book, coincidentally also titled The Old New Thing (Addison Wesley 2007). He occasionally appears on the Windows Dev Docs Twitter account to tell stories which convey no useful information.

0 comments

Discussion are closed.