Scrollbars part 7 – Integrality

Date:August 11, 2003 / year-entry #19
Tags:code
Orig Link:https://blogs.msdn.microsoft.com/oldnewthing/20030811-00/?p=42933
Comments:    0
Summary: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....

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);


*DISCLAIMER: I DO NOT OWN THIS CONTENT. If you are the owner and would like it removed, please contact me. The content herein is an archived reproduction of entries from Raymond Chen's "Old New Thing" Blog (most recent link is here). It may have slight formatting modifications for consistency and to improve readability.

WHY DID I DUPLICATE THIS CONTENT HERE? Let me first say this site has never had anything to sell and has never shown ads of any kind. I have nothing monetarily to gain by duplicating content here. Because I had made my own local copy of this content throughout the years, for ease of using tools like grep, I decided to put it online after I discovered some of the original content previously and publicly available, had disappeared approximately early to mid 2019. At the same time, I present the content in an easily accessible theme-agnostic way.

The information provided by Raymond's blog is, for all practical purposes, more authoritative on Windows Development than Microsoft's own MSDN documentation and should be considered supplemental reading to that documentation. The wealth of missing details provided by this blog that Microsoft could not or did not document about Windows over the years is vital enough, many would agree an online "backup" of these details is a necessary endeavor. Specifics include:

<-- Back to Old New Thing Archive Index