Search code examples
windowswinapicomboboxautosize

how do I remove this margin from text's size measured with GetTextExtentPoint32()?


I'm using GetTextExtentPoint32() to measure the text size and calculate the width and height of my combobox. However, it seems to be adding margin proportional to the amount of text, that I'd to get rid of. It seems the larger the string is, the larger margin I get on right. How do I it doesn't add this margin? a small one is acceptable but look at the second image, the big empty space on right.

With small strings, I find this margin acceptable:

enter image description here

However, with big ones, I get this big white space on right side, that I'd to get rid of:

enter image description here

I'm measuring the text size like this:

              int len = wcslen(s);
              HDC dc = GetDC(hwndCombo);
              SIZE sz;
              assert(GetTextExtentPoint32(dc, s, len, &sz));
              ReleaseDC(hwndCombo, dc);
              int weight = sz.cx + 10;
              int height = sz.cy;
              assert(SetWindowPos(hwndCombo, HWND_TOP, 0, 0, weight, height, SWP_NOMOVE));

full code:

#pragma comment(lib, "user32.lib")
#pragma comment(lib, "Comctl32.lib")
#pragma comment(lib, "Gdi32.lib")
#pragma comment(lib, "Comdlg32.lib")

#define WIN32_LEAN_AND_MEAN
#define UNICODE
#define _UNICODE

#include <windows.h>
#include <winuser.h>
#include <assert.h>

#define COUNTOF(a) (sizeof(a)/sizeof(a[0]))

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int largestIndex();
HFONT getDefaultFont();
void SetDefaultFont(HWND hwnd);

HFONT hDefaultSystemFont;

HINSTANCE g_hinst;

const wchar_t *items[] =
{ 
  L"Windows", L"Mac", 
  L"FreeBSD", L"Arch",
  L"aaaaaa!!!!!!!aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa#",
  L"foo",
  L"baaaa"
};
HWND hwndCombo;

enum
{
  ID_COMBO = 10,
  ID_BTN1,
};

int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
                    PWSTR lpCmdLine, int nCmdShow) {

    HWND hwnd;
    MSG  msg ;    
    WNDCLASSW wc = {0};
    wc.lpszClassName = L"Application";
    wc.hInstance     = hInstance ;
    wc.hbrBackground = GetSysColorBrush(COLOR_3DFACE);
    wc.lpfnWndProc   = WndProc ;
    wc.hCursor       = LoadCursor(0,IDC_ARROW);

    g_hinst = hInstance;
  
    RegisterClassW(&wc);
    hwnd = CreateWindowW(wc.lpszClassName, L"",
                  WS_OVERLAPPEDWINDOW | WS_VISIBLE,
                  100, 100, 300, 170, 0, 0, hInstance, 0);  


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

    return (int) msg.wParam;
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, 
        WPARAM wParam, LPARAM lParam) {

    switch(msg)
    {
    
        case WM_CREATE:
        {
              hwndCombo = CreateWindowW(L"Combobox", NULL, 
                    WS_CHILD | WS_VISIBLE | CBS_DROPDOWN,
                    10, 10, 60, 110, hwnd, (HMENU) ID_COMBO, g_hinst, NULL);
              for (int i = 0; i < COUNTOF(items); i++ )
              {
                SendMessageW(hwndCombo, CB_ADDSTRING, 0, (LPARAM) items[i]);
              }
              SetDefaultFont(hwndCombo);

              HWND btn1 =
              CreateWindowW(L"Button", L"Click me", 
                    WS_CHILD | WS_VISIBLE,
                    5, 40, 90, 25, hwnd, (HMENU) ID_BTN1, g_hinst, NULL); 
              SetDefaultFont(btn1);
        }
        break;

        case WM_COMMAND:
        {
          switch(LOWORD(wParam))
          {
                case ID_BTN1:
                {
                  int i = largestIndex();
                  wchar_t *s = (wchar_t*) items[i];
                  int len = wcslen(s);
                  HDC dc = GetDC(hwndCombo);
                  SIZE sz;
                  assert(GetTextExtentPoint32(dc, s, len, &sz));
                  ReleaseDC(hwndCombo, dc);
                  int weight = sz.cx + 10;
                  int height = sz.cy;
                  assert(SetWindowPos(hwndCombo, HWND_TOP, 0, 0, weight, height, SWP_NOMOVE));
                }
                break;

                default: 
                  break;
            }

        }
        break;

        case WM_DESTROY:
            DeleteObject(hDefaultSystemFont);
            hDefaultSystemFont = NULL;
            PostQuitMessage(0);
            break; 
    }
  
    return DefWindowProcW(hwnd, msg, wParam, lParam);
}

int largestIndex()
{
  int m = 0;
  int idx = -1;

  for(int i = 0; i < COUNTOF(items); i++) {
    int l = wcslen(items[i]);
    if(l > m) {
      m  = l;
      idx = i;
    }
  }

  return idx;
}



HFONT getDefaultFont()
{
  if(hDefaultSystemFont == NULL) {
    NONCLIENTMETRICS ncm;
    ncm.cbSize = sizeof(NONCLIENTMETRICS);
    SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICS), &ncm, 0);
    hDefaultSystemFont = CreateFontIndirect(&ncm.lfMessageFont);
  }
  return hDefaultSystemFont;
}


void SetDefaultFont(HWND hwnd)
{
    SendMessage(hwnd, WM_SETFONT, (LPARAM) getDefaultFont(), TRUE);
}

Solution

  • You are using different font for measurements.

    Try

    int len = wcslen(s);
    HDC dc = GetDC(hwndCombo);
    SIZE sz;
    // Set correct font
    HFONT old = SelectObject(dc,hDefaultSystemFont);
    // or
    // HFONT control_font = (HFONT)SendMessage(hwndCombo, WM_GETFONT, 0, 0);
    // HFONT old = SelectObject(dc,control_font);
    assert(GetTextExtentPoint32(dc, s, len, &sz));
    // Restore font for cleanup
    SelectObject(dc,old);
    ReleaseDC(hwndCombo, dc);
    int weight = sz.cx + 10;
    int height = sz.cy;