StudentX
StudentX

Reputation: 2323

Center multiline text vertically in a window, pure winapi and c++

I have a window that needs redrawing multiline text on demand, text could be of variable length. So how to go about doing this ?

This is what I have for now and it doesn't work.

               RECT rc;

               GetWindowRect ( hwnd, &rc );



               int rectHeight = DrawText( hMemDc, text.c_str(), text.size(), &rc, DT_CALCRECT );                    // Get formating rectangle height


               int windowHight = rc.bottom - rc.top;
               int windowWidth = rc.right - rc.left;


               int yTop = rc.top + (  ( windowHight - rectHeight ) / 2  );
               int yBottom = yTop + rectHeight;

               int xLeft = rc.left + 20;
               int xRight = rc.right - 20;


               rc.top       = yTop;
               rc.bottom    = yBottom;
               rc.left      = xLeft;
               rc.right     = xRight;


               DrawText( hMemDc, text.c_str(), text.size(), &rc, DT_LEFT | DT_WORDBREAK );

Upvotes: 0

Views: 4989

Answers (2)

sergiol
sergiol

Reputation: 4335

I solved the problem drawing some debug lines to help me with the code:

                {
                    CPen p;
                    p.CreatePen(PS_SOLID, 0, RGB(0,0,255));
                    pDrawDC->SelectObject(&p);

                    pDrawDC->MoveTo(rc.TopLeft());
                    pDrawDC->LineTo(rc.BottomRight());
                }

                CRect rdebug(rc);

                int height= pDrawDC->DrawText(sLabel, &rdebug, DT_CENTER | DT_VCENTER | DT_WORDBREAK | DT_CALCRECT);

                {
                    CPen p;
                    p.CreatePen(PS_SOLID, 0, RGB(0,255,0));
                    pDrawDC->SelectObject(&p);

                    pDrawDC->MoveTo(rdebug.TopLeft());
                    pDrawDC->LineTo(rdebug.BottomRight());
                }

                int center_y= rc.CenterPoint().y;
                rc.top= center_y - height / 2;
                rc.bottom= center_y + height /2;

                {
                    CPen p;
                    p.CreatePen(PS_SOLID, 0, RGB(255,0,0));
                    pDrawDC->SelectObject(&p);

                    pDrawDC->MoveTo(rc.TopLeft());
                    pDrawDC->LineTo(rc.BottomRight());
                }

                pDrawDC->DrawText(sLabel, rc, DT_CENTER | DT_VCENTER | DT_WORDBREAK);

Notice the rDebug, CPen, LineTo and MoveTo lines are only for debug purposes.

I've got the following image:

debug image

Now, without the debug part, I could reduce the source-code footprint to:

                int height= pDrawDC->DrawText(sLabel, CRect(rc), DT_CENTER | DT_VCENTER | DT_WORDBREAK | DT_CALCRECT);

                int center_y= rc.CenterPoint().y;
                rc.top= center_y - height / 2;
                rc.bottom= center_y + height /2;

                pDrawDC->DrawText(sLabel, rc, DT_CENTER | DT_VCENTER | DT_WORDBREAK);

Upvotes: 0

James Holderness
James Holderness

Reputation: 23001

There are two problems in your code. First, you need to specify DT_WORDBREAK in the DT_CALCRECT call otherwise it won't wrap the text to fit the available width.

int rectHeight = DrawText( hMemDc, text.c_str(), text.size(), &rc,
  DT_CALCRECT|DT_WORDBREAK );

Second, the DT_CALCRECT call will override the rc variable with calculated rect, so your window height will be wrong and your centering won't work. Either save the rc variable before the DT_CALCRECT call, or call GetWindowRect again afterwards.

GetWindowRect ( hwnd, &rc );

Upvotes: 2

Related Questions