win32 - How to draw a rectangle around a text string? - winapi

I am new to Win32 and trying to get a GDI based code in C++ ( for technical reasons don't want to use GDI+)
Edit: Simplied the question:
I need to draw a rectangle around the text that is drawn in the middle of the window.
- How can I populate the rectangle co-ordinates?
- Can any one help with the line - Rectangle(x1,y1,x2,y2)? - How to calculate these (x1,y1) & (x2,y2) values?
Thank you..
hdc = BeginPaint(hWnd, &ps);
GetClientRect(hWnd, &rcClient);
SelectObject(hdc, GetStockObject(DEFAULT_GUI_FONT));
SetTextColor(hdc, RGB(255, 0, 0));
DrawText(hdc, wstring(s.begin(),s.end()).c_str(), -1, &rectResult, DT_SINGLELINE | DT_CALCRECT);
DrawText(hdc, wstring(s.begin(),s.end()).c_str(), -1, &rcClient, DT_SINGLELINE | DT_CENTER | DT_VCENTER);
// Here I need help - How to I place the rectangle around the Text - which is drawn in the middle of the window?
// It looks like need to use - rectResult.bottom/top/left/right - but don't know how..
Rectangle(hdc, 0,0,100,100);

You don't actually have to center the text yourself. The GDI text output functions can do that for you if you pass the appropriate flags.
For example, if you call DrawText and pass the DT_CENTER flag, it will automatically draw the text in the middle of the specified rectangle (horizontally centered).
Assuming you have only a single line of text (which it sounds like you do), you can get it to automatically vertically center the text by passing the DT_SINGLELINE and DT_VCENTER flags.
So all you really have to do is write code to divide your window's client area up into 4 equal parts, and then pass those rectangles to the DrawText function. That's not terribly difficult. Put a pencil and paper to it if you can't visualize it in your head.
void PaintWindow(HWND hWnd)
{
// Set up the device context for drawing.
PAINTSTRUCT ps;
HDC hDC = BeginPaint(hWnd, &ps);
HPEN hpenOld = static_cast<HPEN>(SelectObject(hDC, GetStockObject(DC_PEN)));
HBRUSH hbrushOld = static_cast<HBRUSH>(SelectObject(hDC, GetStockObject(NULL_BRUSH)));
// Calculate the dimensions of the 4 equal rectangles.
RECT rcWindow;
GetClientRect(hWnd, &rcWindow);
RECT rc1, rc2, rc3, rc4;
rc1 = rc2 = rc3 = rc4 = rcWindow;
rc1.right -= (rcWindow.right - rcWindow.left) / 2;
rc1.bottom -= (rcWindow.bottom - rcWindow.top) / 2;
rc2.left = rc1.right;
rc2.bottom = rc1.bottom;
rc3.top = rc1.bottom;
rc3.right = rc1.right;
rc4.top = rc1.bottom;
rc4.left = rc1.right;
// Optionally, deflate each of the rectangles by an arbitrary amount so that
// they don't butt up right next to each other and we can distinguish them.
InflateRect(&rc1, -5, -5);
InflateRect(&rc2, -5, -5);
InflateRect(&rc3, -5, -5);
InflateRect(&rc4, -5, -5);
// Draw (differently-colored) borders around these rectangles.
SetDCPenColor(hDC, RGB(255, 0, 0)); // red
Rectangle(hDC, rc1.left, rc1.top, rc1.right, rc1.bottom);
SetDCPenColor(hDC, RGB(0, 255, 0)); // green
Rectangle(hDC, rc2.left, rc2.top, rc2.right, rc2.bottom);
SetDCPenColor(hDC, RGB(0, 0, 255)); // blue
Rectangle(hDC, rc3.left, rc3.top, rc3.right, rc3.bottom);
SetDCPenColor(hDC, RGB(255, 128, 0)); // orange
Rectangle(hDC, rc4.left, rc4.top, rc4.right, rc4.bottom);
// Draw the text into the center of each of the rectangles.
SetBkMode(hDC, TRANSPARENT);
SetBkColor(hDC, RGB(0, 0, 0)); // black
// TODO: Optionally, set a nicer font than the default.
DrawText(hDC, TEXT("Hello World!"), -1, &rc1, DT_CENTER | DT_SINGLELINE | DT_VCENTER);
DrawText(hDC, TEXT("Hello World!"), -1, &rc2, DT_CENTER | DT_SINGLELINE | DT_VCENTER);
DrawText(hDC, TEXT("Hello World!"), -1, &rc3, DT_CENTER | DT_SINGLELINE | DT_VCENTER);
DrawText(hDC, TEXT("Hello World!"), -1, &rc4, DT_CENTER | DT_SINGLELINE | DT_VCENTER);
// Clean up after ourselves.
SelectObject(hDC, hpenOld);
SelectObject(hDC, hbrushOld);
EndPaint(hWnd, &ps);
}

RECT rect={0,0,0,0};
const char *str="Test Text";
DrawText(hDC, str, strlen(str), &rect, DT_CALCRECT | DT_NOCLIP);
Rectangle(hDC,rect.left,rect.top,rect.right,rect.bottom);
DrawText(hDC, str, strlen(str), &rect, DT_CENTER | DT_VCENTER | DT_SINGLELINE|DT_NOCLIP);

Got it finally :) Thanks a lot Cody Gray for pointing me in right direction about this :)
GetTextExtentPoint32(hDC, str, strlen(str), &sz2);
rect2.top=rect2.bottom+sz2.cy;
rect2.right=rect2.top+sz2.cx;
Rectangle(hDC,rect2.left,rect2.top,rect2.right,rect2.bottom);
DrawText(hDC, str, -1, &rect2, DT_CENTER | DT_VCENTER | DT_SINGLELINE|DT_NOCLIP);

Related

Win32 - Change background color under ENTIRE 'Static' control area?

VS2019 c++
This is how I'm declaring a 'Static' label in my humble Win32 application:
hLabel = CreateWindowW(L"Static", L"My Application Text", WS_VISIBLE | WS_CHILD | ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL, 20, 50, 700, 50, hWnd, NULL, NULL, NULL);
Each line of text uses about 25 pixel worth vertically. So I effectively have room for two lines worth of text in this control since it is declared as 50 pixels high.
I would like to override the default colors used to paint the control, so I experiment with this code:
case WM_CTLCOLORSTATIC:
{
HDC hEdit = (HDC)wParam;
SetTextColor(hEdit, RGB(0, 255, 0));
SetBkColor(hEdit, RGB(0, 0, 255));
return 0;
}
This works "great" except it does not paint the entire area of the control?
As you can see above, the Text color is applied to the characters and the 'Background' color is applied not to the entire area of the control, enclosed by the WS_BORDER, but only to the extent of the characters. I start by filling the control with 100 characters. That's why you only see "one" line of text. My question is "how should I go about filling the entire are of the control"? Which is declared as having an area of 700 pixel wide by 50 pixel high.
I confess I searched around quite a bit but I feel I'm going in circle.
The answer is "42" -- kidding!
This is how I changed my code :
case WM_CTLCOLOREDIT:
{
HDC hEdit = (HDC)wParam;
SetTextColor(hEdit, RGB(0, 0, 255));
SetBkColor(hEdit, RGB(255, 255, 0));
SetBkMode(hEdit, TRANSPARENT);
BackEDIT = CreateSolidBrush(RGB(255, 255, 0));
return (INT_PTR)BackEDIT;
}
And then I added :
case WM_DESTROY:
DeleteObject(BackEDIT);
PostQuitMessage(0);
return 0;
So, adding a BRUSH and deleting the BRUSH. Works perfect!

MFC: Scrollbar gripper has a flashing cursor?

I am using the following code to draw a scrollbar gripper for resizing window.
// create a gripper in the bottom-right corner
if (m_bShowGripper && ((GetStyle() & WS_THICKFRAME) != 0))
{
SMovingChild s;
s.m_rcInitial.SetRect(-GetSystemMetrics(SM_CXVSCROLL), -GetSystemMetrics(SM_CYHSCROLL), 0, 0);
s.m_rcInitial.OffsetRect(rcClient.BottomRight());
m_hGripper = CreateWindow(_T("Scrollbar"), _T("size"), WS_CHILD | WS_VISIBLE | SBS_SIZEGRIP,
s.m_rcInitial.left, s.m_rcInitial.top, s.m_rcInitial.Width(), s.m_rcInitial.Height(),
this->GetSafeHwnd(), NULL, AfxGetInstanceHandle(), NULL);
ASSERT(m_hGripper != NULL);
if (m_hGripper != NULL)
{
s.m_hWnd = m_hGripper;
s.m_dXMoveFrac = 1.0;
s.m_dYMoveFrac = 1.0;
s.m_dXSizeFrac = 0.0;
s.m_dYSizeFrac = 0.0;
m_MovingChildren.push_back(s);
// put the gripper first in the z-order so it paints first and doesn't obscure other controls
::SetWindowPos(m_hGripper, HWND_TOP, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOSIZE | SWP_SHOWWINDOW);
}
}
Originally from here: How to create a resizable CDialog in MFC?. The code runs OK, but the gripper has a flashing block like cursor (figure below).
This issue can be addressed by commenting the last statement of ::SetWindowPos(...). But I am wondering what the real cause is and whether it is OK to remove this statement, or any better fix.

Drawing semitransparent child window with image on parent window

I need to make bird animation in WS_OVERLAPPED window (as shown below). Animation is represented by 8 images:
The blue color in the image (which is RGB(0, 255, 255)) has to be transparent (see screenshot below).
I wanted to do this using CreateWindowEx() (bird would be represented by layered window) with WS_EX_LAYERED argument. Unfortunately bird must be WS_CHILD. Mixing WS_EX_LAYERED | WS_CHILD is not legal in Windows 7:
Windows 8: The WS_EX_LAYERED style is supported for top-level windows and child windows. Previous Windows versions support WS_EX_LAYERED only for top-level windows.
Final effect should look like this (I've already painted window's background - the only problem is the bird):
How can I achieve this effect? How to animate bird in parent window?
If you have any idea how to implement bird animation with transparent background color please share.
Since animation is done even when there's no interaction with the window, we'll need a timer:
case WM_CREATE:
// load resources
SetTimer(hwnd, 0, 250, NULL); // set timer to 250 ms
return 0;
...
case WM_DESTROY:
KillTimer(hwnd, 0);
// release the resources
return 0;
We can invalidate the whole window each timer tick, but it would be better to redraw only needed part. We'll also update the current frame number here:
case WM_TIMER:
frame_number++;
if (frame_number >= 8)
frame_number = 0;
RECT rc = { 30, 30, 80, 80 }; // a rectangle from (30,30) to (80,80)
InvalidateRect(hwnd, &rc, FALSE);
return 0;
Then, we draw the current frame in the WM_PAINT handler:
case WM_PAINT:
// draw the sky
SelectObject(hDCMem, hBird);
TransparentBlt(hDC, 30, 30, 50, 50, hDCMem, frame_number * 51, 0, 50, 50, RGB(0, 255, 255)); // 51 is 50 (side of a bird frame) + 1 (gap between the frames)
// draw the rest
return 0;
I've finally find out how to do it. It's quite tricky.
The full description of the solution is available here - winprog.org/tutorial/transparency.html. For polish readers here is great translation.
Simple idea in brief:
Giving bitmaps the appearance of having transparent sections is quite simple, and involves the use of a black and white Mask image in addition to the colour image that we want to look transparent.
The following conditions need to be met for the effect to work correctly: First off, the colour image must be black in all areas that we want to display as transparent. And second, the mask image must be white in the areas we want transparent, and black elsewhere. The colour and mask images are displayed as the two left most images in the example picture on this page.
Simple solution in brief:
#define TRANSPARENCY_COLOR RGB(0, 255, 255)
birdBmp = (HBITMAP) LoadBitmap(hInstance, MAKEINTRESOURCE(IDB_BITMAP1));
hbmpMask = CreateBitmapMask(birdBmp, TRANSPARENCY_COLOR);
Painting:
case WM_PAINT:
{
hdc = BeginPaint(hWnd, &ps);
HDC birdMaskHdc = CreateCompatibleDC(hdc);
BITMAP bmInfo;
GetObject(birdBmp, sizeof(bmInfo), &bmInfo);
HBITMAP hbmpOld = (HBITMAP) SelectObject(birdMaskHdc, hbmpMask);
BitBlt(hdc, 0, 0, bmInfo.bmWidth, bmInfo.bmHeight, birdMaskHdc, 0, 0, SRCAND);
SelectObject(birdMaskHdc, birdBmp);
BitBlt(hdc, 0, 0, bmInfo.bmWidth, bmInfo.bmHeight, birdMaskHdc, 0, 0, SRCPAINT);
SelectObject(birdMaskHdc, hbmpOld);
DeleteDC(birdMaskHdc);
EndPaint(hWnd, &ps);
break;
}
Cleaning:
case WM_DESTROY:
{
DeleteObject(hbmpMask);
DeleteObject(birdBmp);
PostQuitMessage(0);
break;
}
Function which is responsible for creating bitmap mask:
HBITMAP CreateBitmapMask(HBITMAP hbmColour, COLORREF crTransparent)
{
HDC hdcMem, hdcMem2;
HBITMAP hbmMask, hbmOld, hbmOld2;
BITMAP bm;
GetObject( hbmColour, sizeof( BITMAP ), & bm );
hbmMask = CreateBitmap( bm.bmWidth, bm.bmHeight, 1, 1, NULL );
hdcMem = CreateCompatibleDC( NULL );
hdcMem2 = CreateCompatibleDC( NULL );
hbmOld =( HBITMAP ) SelectObject( hdcMem, hbmColour );
hbmOld2 =( HBITMAP ) SelectObject( hdcMem2, hbmMask );
SetBkColor( hdcMem, crTransparent );
BitBlt( hdcMem2, 0, 0, bm.bmWidth, bm.bmHeight, hdcMem, 0, 0, SRCCOPY );
BitBlt( hdcMem, 0, 0, bm.bmWidth, bm.bmHeight, hdcMem2, 0, 0, SRCINVERT );
SelectObject( hdcMem, hbmOld );
SelectObject( hdcMem2, hbmOld2 );
DeleteDC( hdcMem );
DeleteDC( hdcMem2 );
return hbmMask;
}

child window blinking

My application is dll which is injected into 3rd party GUI application. There dll creates child window containing few controls on the main window of the application. The problem is that my window and its controls blink when the main window is redrawn. I've already read many articles about similar problems but could not fix it. Here is some code
hwndContainer = CreateWindowEx( WS_EX_TOPMOST, PANEL_CLASS_NAME, "", WS_CHILD | WS_VISIBLE, 0, 0, width, height, hwnd, 0, g_hInstance, cd );
CreateWindowEx( WS_EX_TOPMOST, "button", "Click me", WS_CHILD | WS_VISIBLE | WS_TABSTOP, x, y, w, h, hwndContainer, 0, 0, 0 );
...
case WM_PAINT:
PAINTSTRUCT ps;
HDC hdc = BeginPaint( hwndPanel, &ps );
RECT r;
GetClientRect( hwndPanel, &r );
SelectObject( hdc, hpenBorder );
SelectObject( hdc, GetStockObject( NULL_BRUSH ) );
RoundRect( hdc, 0, 0, r.right, r.bottom, 5, 5 );
EndPaint( hwndPanel, &ps );
return 0;
I tried adding WS_CLIPCHILDREN to the main window style, processed WM_PAINT of the main window etc. Any thoughts?
You may give WS_CLIPSIBLINGS a try... It's been a long time since I've done anything with window styles directly but I seem to remember this style required to help reduce redraw flicker in non-top level windows. Good luck.

how to use DrawText() to write text in a given window whose handle is known?

I want to know how to write text on a particular window starting at a given location in the window using the Windows API.
For example if the coordinates within the window where the text is to be written are (x,y) = (40,10) then what do I need to do to write a line of text to a particular window at that location in the window?
Suppose your window name is "hwnd" and the text which u want to write on that window at x,y coordinate is say stored in "message" where
LPCWSTR message=L"My First Window"; then
RECT rect;
HDC wdc = GetWindowDC(hwnd);
GetClientRect (bgHandle, &rect) ;
SetTextColor(wdc, 0x00000000);
SetBkMode(wdc,TRANSPARENT);
rect.left=40;
rect.top=10;
DrawText( wdc, message, -1, &rect, DT_SINGLELINE | DT_NOCLIP ) ;
DeleteDC(wdc);
Thats it.. remember this is just one example.
I am hoping this a more complete answer...
void OnPaint(HWND hWnd)
{
RECT rect;
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
GetClientRect(hWnd, &rect);
SetTextColor(hdc, RGB(0xFF, 0x00, 0x00));
SetBkMode(hdc, TRANSPARENT);
rect.left = 40;
rect.top = 10;
DrawText(hdc, L"Hello World!", -1, &rect, DT_SINGLELINE | DT_NOCLIP);
SelectObject(hdc, oldPen);
DeleteObject(hPen);
EndPaint(hWnd, &ps);
}
This would then be called from the WM_PAINT message in the WndProc.

Resources