Search code examples
apiwinapiwindow

Win 32 API set window minimum size to keep text fully visible


I am currently a beginner in win 32 API and am making a sample program. The problem I face is I can't find a proper way to set the window's minimum size in order for the text inside it (centered) to be visible when the window gets at its minimum. The text is fully visible in all the other cases, except when I reach a certain "critical" size, after which the text is either seen partially or not seen at all.

I found how to change the width and height of the window, as shown here:

LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)        
{
    switch (message)                  
    {
        case WM_DESTROY:
            PostQuitMessage (0);      
            break;

        case WM_GETMINMAXINFO: 
            ((MINMAXINFO*)lParam)->ptMinTrackSize.x =100; //apply custom min width
            ((MINMAXINFO*)lParam)->ptMinTrackSize.y =100;  //apply custom min height
            break;

        case WM_PAINT:
        {
            PAINTSTRUCT ps;
            HDC hdc = BeginPaint (hwnd, &ps);

            GetClientRect(hwnd,&rc);

            DrawText (hdc, TEXT ("Sample text here"), -1, &rc,DT_SINGLELINE | DT_CENTER | DT_VCENTER ) ;


            EndPaint (hwnd, &ps);
        }
        break;

        default:                      
            return DefWindowProc (hwnd, message, wParam, lParam);
    }

    return 0;
}

I think these lines should be changed, but I don't know what change needs to be made.

((MINMAXINFO*)lParam)->ptMinTrackSize.x =100; //apply custom min width
((MINMAXINFO*)lParam)->ptMinTrackSize.y =100;  //apply custom min height

My idea is that instead explicit float values which are assigned to x and y members of the POINT structure, a function should be called, that determines the text's minimum width and height.

Any help would be appreciated.

Below is the full code of this program.

#if defined(UNICODE) && !defined(_UNICODE)
#define _UNICODE
#elif defined(_UNICODE) && !defined(UNICODE)
#define UNICODE
#endif

#include <tchar.h>
#include <windows.h>
#include <stdio.h>


/*  Declare Windows procedure  */
LRESULT CALLBACK WindowProcedure (HWND, UINT, WPARAM, LPARAM);

/*  Make the class name into a global variable  */
TCHAR szClassName[ ] = _T("CodeBlocksWindowsApp");
RECT rc;
int WINAPI WinMain (HINSTANCE hThisInstance,
                 HINSTANCE hPrevInstance,
                 LPSTR lpszArgument,
                 int nCmdShow)
{
    HWND hwnd;               /* This is the handle for our window */
    MSG messages;            /* Here messages to the application are saved */
    WNDCLASSEX wincl;        /* Data structure for the windowclass */

    wincl.hInstance = hThisInstance;
    wincl.lpszClassName = szClassName;
    wincl.lpfnWndProc = WindowProcedure;      /* This function is called by windows */
    wincl.style = CS_DBLCLKS | CS_VREDRAW | CS_HREDRAW;                 /* Catch double-clicks */
    wincl.cbSize = sizeof (WNDCLASSEX);

    wincl.hIcon = LoadIcon (NULL, IDI_APPLICATION);
    wincl.hIconSm = LoadIcon (NULL, IDI_APPLICATION);
    wincl.hCursor = LoadCursor (NULL, IDC_ARROW);
    wincl.lpszMenuName = NULL;                 /* No menu */
    wincl.cbClsExtra = 0;                      /* No extra bytes after the window class */
    wincl.cbWndExtra = 0;                      /* structure or the window instance */

    wincl.hbrBackground = (HBRUSH) COLOR_BACKGROUND;

    if (!RegisterClassEx (&wincl))
        return 0;

    hwnd = CreateWindowEx (
       0,                   /* Extended possibilites for variation */
       szClassName,         /* Classname */
       _T("Code::Blocks Template Windows App"),
              /* Title Text */
       WS_OVERLAPPEDWINDOW, /* default window */
       CW_USEDEFAULT,       /* Windows decides the position */
       CW_USEDEFAULT,       /* where the window ends up on the screen */
       544,                 /* The programs width */
       375,                 /* and height in pixels */
       HWND_DESKTOP,        /* The window is a child-window to desktop */
       NULL,                /* No menu */
       hThisInstance,       /* Program Instance handler */
       NULL                 /* No Window Creation data */
       );

    ShowWindow (hwnd, nCmdShow);

    while (GetMessage (&messages, NULL, 0, 0))
    {
        TranslateMessage(&messages);
        DispatchMessage(&messages);
    }

    return messages.wParam;
}

LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)                  
    {
        case WM_DESTROY:
            PostQuitMessage (0);       
            break;

        case WM_GETMINMAXINFO:
            //window size/position is going to change
            //apply custom min width/height 350,50

            SIZE sz;
            HDC hdc = GetDC( hwnd );
            TCHAR* myString = TEXT("Sample Text!");
            HFONT oldFont, myFont; //create your own font
            //select your font into device context
            oldFont = (HFONT)SelectObject( hdc, myFont );
            //get font dimensions
            GetTextExtentPoint32( hdc, myString, _tcslen( myString ), &sz );

            ((MINMAXINFO*)lParam)->ptMinTrackSize.x = sz.cx;
            ((MINMAXINFO*)lParam)->ptMinTrackSize.y = sz.cy;

            //cleanup
            SelectObject( hdc, oldFont );
            DeleteObject( myFont );
            ReleaseDC( hwnd, hdc );
            break;

        case WM_PAINT:
            {
                PAINTSTRUCT ps;
                HDC hdc = BeginPaint (hwnd, &ps);
                GetClientRect(hwnd,&rc);
                DrawText (hdc, TEXT("Sample Text!"), -1, &rc, DT_SINGLELINE | DT_CENTER | DT_VCENTER | DT_WORDBREAK ) ;
                EndPaint (hwnd, &ps);
            }
            break;

        default:                      
            return DefWindowProc (hwnd, message, wParam, lParam);
    }

    return 0;
}

I apologize for the poor indentation.


Solution

  • I think that GetTextExtentPoint32 is what you want so you can calculate the text size and length. Here is the example showing how to use it.

    Just in case your text gets bigger dimensions than your screen, you could add DT_WORDBREAK flag to your DrawText call-see the documentation.

    You will need to create font and select it in your device context and CreateFontIndirect can help you-pay attention to the members of the LOGFONT structure so you can create font you wish. Here is one example of doing it.

    Try something like this:

    case WM_PAINT:
        {
            PAINTSTRUCT ps;
            HDC hdc = BeginPaint (hwnd, &ps);
    
            //create your own font
            HFONT oldFont, myFont; 
            // create logical font for menu
            LOGFONT lf = { sizeof(lf) };
            _tcscpy_s( lf.lfFaceName, ARRAYSIZE(lf.lfFaceName), L"Microsoft Sans Serif" );
            lf.lfHeight = -MulDiv( 12, GetDeviceCaps( hdc, LOGPIXELSY), 72);
            lf.lfWeight = FW_HEAVY;
            lf.lfItalic = FALSE;
            lf.lfUnderline = FALSE;
    
            myFont = CreateFontIndirect(&lf);
    
            //select your font into device context
            oldFont = (HFONT)SelectObject( hdc, myFont );
    
            GetClientRect(hwnd,&rc);
            DrawText (hdc, TEXT("Sample Text!"), -1, &rc, 
                DT_SINGLELINE | DT_CENTER | DT_VCENTER | DT_WORDBREAK ) ;
    
            //cleanup
            SelectObject( hdc, oldFont );
            DeleteObject( myFont );
    
            EndPaint (hwnd, &ps);
        }
        return 0L;
    
    case WM_GETMINMAXINFO: 
        {
            SIZE sz;
            HDC hdc = GetDC( hwnd );
            LPTSTR myString = TEXT( "Sample text here" );
    
            //create your own font 
            HFONT oldFont, myFont; 
    
            // create logical font for menu
            LOGFONT lf = { sizeof(lf) };
            _tcscpy_s( lf.lfFaceName, ARRAYSIZE(lf.lfFaceName), L"Microsoft Sans Serif" );
            lf.lfHeight = -MulDiv( 12, GetDeviceCaps( hdc, LOGPIXELSY), 72);
            lf.lfWeight = FW_HEAVY;
            lf.lfItalic = FALSE;
            lf.lfUnderline = FALSE;
    
            myFont = CreateFontIndirect(&lf);
    
            //select your font into device context
            oldFont = (HFONT)SelectObject( hdc, myFont );
    
            //get font dimensions
            GetTextExtentPoint32( hdc, myString, _tcslen( myString ), &sz );
    
            ((MINMAXINFO*)lParam)->ptMinTrackSize.x = sz.cx; 
            ((MINMAXINFO*)lParam)->ptMinTrackSize.y = sz.cy; 
    
            //cleanup
            SelectObject( hdc, oldFont );
            DeleteObject( myFont );
            ReleaseDC( hwnd, hdc );
        }
    break;
    

    If you have further questions leave a comment.

    Best regards.