Transparent Tooltip Background [closed] - c

This question is unlikely to help any future visitors; it is only relevant to a small geographic area, a specific moment in time, or an extraordinarily narrow situation that is not generally applicable to the worldwide audience of the internet. For help making this question more broadly applicable, visit the help center.
Closed 10 years ago.
I have 2 regions in a window, each with their own tooltip.
these tooltips are custom drawn by handling the WM_PAINT message (to prevent flicker).
This is the creation of the tooltips:
tooltips[MAIN_GRAPH_TT].tthWnd = CreateWindowEx(WS_EX_TOPMOST,TOOLTIPS_CLASS,0,WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP | TTS_NOFADE,CW_USEDEFAULT, CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,0,0,g_hInst,0);
tooltips[SECONDARY_GRAPH_TT].tthWnd = CreateWindowEx(WS_EX_TOPMOST,TOOLTIPS_CLASS,0,WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP | TTS_NOFADE,CW_USEDEFAULT, CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,0,0,g_hInst,0);
This is initialisation of the tooltips:
if (tooltips[MAIN_GRAPH_TT].tthWnd)
{
lpfnOldTTProc = (WNDPROC)SetWindowLong(tooltips[MAIN_GRAPH_TT].tthWnd,
GWL_WNDPROC, (DWORD) TooltipProc);
SetWindowLong(tooltips[MAIN_GRAPH_TT].tthWnd, GWL_EXSTYLE, WS_EX_LAYERED|WS_EX_TOOLWINDOW);
SetLayeredWindowAttributes(tooltips[MAIN_GRAPH_TT].tthWnd,RGB(255,0,0),0,ULW_COLORKEY);
SendMessage(tooltips[MAIN_GRAPH_TT].tthWnd,CWM_SETWNDPROC,0,(LPARAM)new WNDPROC(lpfnOldTTProc));
}
if (tooltips[SECONDARY_GRAPH_TT].tthWnd)
{
lpfnOldTTProc = (WNDPROC)SetWindowLong(tooltips[SECONDARY_GRAPH_TT].tthWnd, GWL_WNDPROC, (DWORD) TooltipProc);
SetWindowLong(tooltips[SECONDARY_GRAPH_TT].tthWnd, GWL_EXSTYLE, WS_EX_LAYERED|WS_EX_TOOLWINDOW);
SetLayeredWindowAttributes(tooltips[SECONDARY_GRAPH_TT].tthWnd,RGB(255,0,0),0,ULW_COLORKEY);
SendMessage(tooltips[SECONDARY_GRAPH_TT].tthWnd,CWM_SETWNDPROC,0,(LPARAM)new WNDPROC(lpfnOldTTProc));
}
And this is the WM_PAINT of the custom tooltip WNDPROC:
case WM_PAINT:
{
const int FRAME_WIDTH = 1;
const int CORNER_DIAMETER = 10;
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd,&ps);
HDC hMemDC;
RECT cr;
GetClientRect(hWnd,&cr);
hMemDC = CreateCompatibleDC(hdc);
HBITMAP memBM = CreateCompatibleBitmap(hdc, cr.right-cr.left, cr.bottom-cr.top);
HBITMAP hOldBM = (HBITMAP) SelectObject(hMemDC,memBM);
//drawing start [draw to hMemDC]
{
FillSolidRect(hMemDC,0,0,cr.right-cr.left,cr.bottom-cr.top,RGB(255,0,0));
HPEN hFramePen = CreatePen(PS_SOLID,FRAME_WIDTH,BLACK);
HBRUSH hBGBrush = GetSysColorBrush(COLOR_INFOBK);
SetTextColor(hMemDC,GetSysColor(COLOR_INFOTEXT));
SetBkColor(hMemDC,WHITENESS);
SetBkMode(hMemDC,TRANSPARENT);
HBRUSH hOldBrush = (HBRUSH) SelectObject(hMemDC,hBGBrush);
HPEN hOldPen = (HPEN) SelectObject(hMemDC,hFramePen);
HFONT hOldFont = SelectFont(hMemDC,g_hFonts[FONT_TOOLTIP]);
RoundRect(hMemDC,cr.left,cr.top,cr.right,cr.bottom,CORNER_DIAMETER,CORNER_DIAMETER);
RECT textRec = cr;
textRec.left += FRAME_WIDTH*2;
textRec.right -= FRAME_WIDTH*2;
textRec.top += FRAME_WIDTH*2;
textRec.bottom -= FRAME_WIDTH*2;
if(hWnd == tooltips[MAIN_GRAPH_TT].tthWnd)
DrawText(hMemDC,tttBuffer[MAIN_GRAPH_TT],sizeof(tttBuffer),&textRec,DT_LEFT|DT_TOP);
else if(hWnd == tooltips[SECONDARY_GRAPH_TT].tthWnd)
DrawText(hMemDC,tttBuffer[SECONDARY_GRAPH_TT],sizeof(tttBuffer),&textRec,DT_LEFT|DT_TOP);
SelectObject(hMemDC,hOldBrush);
SelectObject(hMemDC,hOldPen);
SelectObject(hMemDC,hOldFont);
DeleteObject(hFramePen);
DeleteObject(hBGBrush);
}
//drawing end
BitBlt(hdc,
cr.left,
cr.top,
cr.right-cr.left, cr.bottom-cr.top,
hMemDC,
0,
0,
SRCCOPY);
SelectObject(hdc,hOldBM);
DeleteObject(memBM);
DeleteDC(hMemDC);
EndPaint(hWnd,&ps);
}
break;
The problem with these tooltips is, the corners (outside of the round rect) are supposed to be transparent, but i cant seem to get them to dissapear.
I have tried (naĆ­vely) to use HOLLOW_BRUSH to paint the background rect, but didnt work, and as you can see from the example i've tried using the layered window approach, again to no avail.
Can anyone help me get transparency for the background of my tooltips?
Here is a picture of the tooltip without transparency
[the corners have been recoloured white for visibility -- these are the parts that need to be transparent]
(Text blanked out)

When dealing with controls and transparency, I found that WS_EX_TRANSPARENT exStyle can save you some time. Also, you may want to check out if the tooltip sends a WM_CTLCOLORSTATIC message to the main window, if it does you may want to send back a hollow (null) brush handle in response. You may also need to set the background mode to transparent at that point in time, by calling SetBkMode on the hDC the WM_CTLCOLORSTATIC message provides.
Sorry for not testing any of this, but it works well with static and group controls. The only control up to now I evidenced is unresponsive to this method is the checkbox button, which I was unable to set background transparency for.
Regards.

You can use SetWindowRgn to make parts of a window transparent (create a region using CreateRoundRectRgn).
Alternatively, you can use SetLayeredWindowAttributes to use true alpha blending to make parts of the window transparent.

Related

Making some parts of the window transparent in WinAPI

I want to make only certain(rectangular) parts of the window transparent.
I have set the window as WS_EX_LAYERED and the WM_PAINT function is as follows:
case WM_PAINT:;
RECT rect;
GetWindowRect(hwnd, &rect);
HDC hdc = GetDC(hwnd);
HDC hdc1 = CreateCompatibleDC(hdc);
HBITMAP hBitmap = CreateCompatibleBitmap(hdc, rect.right - rect.left, rect.bottom - rect.top);
SelectObject(hdc, hBitmap);
SIZE size = {rect.right - rect.left, rect.bottom - rect.top};
BLENDFUNCTION blendFunc = {AC_SRC_OVER, 0, 0, AC_SRC_ALPHA};
UpdateLayeredWindow(hwnd, hdc1, NULL, &size, hdc, NULL, RGB(0, 0, 0), &blendFunc, ULW_ALPHA);
DeleteObject(hBitmap);
DeleteDC(hdc);
ReleaseDC(hwnd, hdc1);
break;
I tried creating a child window, but that doesn't seem to work if I change it's opacity through SetLayeredWindowAttributes, possibly because it mimics the parent windows opacity. I am currently trying to use UpdateLayeredWindow to make certain parts transparent, but I can't even make the entire part transparent using UpdateLayeredWindow.
What is the general way to make certain parts of the window transparent?
I suggest you could refer to the Doc:Using Layered Windows
To have a dialog box come up as a translucent window, first create the
dialog as usual. Then, on WM_INITDIALOG, set the layered bit of the
window's extended style and call SetLayeredWindowAttributes with the
desired alpha value.
The third parameter of SetLayeredWindowAttributes is a value that ranges from 0 to 255, with 0 making the window completely transparent and 255 making it completely opaque.
In order to use layered child windows, the application has to declare itself Windows 8-aware in the manifest. Refer to the thread: How to use WS_EX_LAYERED on child controls

how can I redraw only the rectangle in my app

I'm using Visual Studios Win32 to develop a control app for an ARINC 629 bus. It works fine for an event-driven action such as a mouse click, but I need it to update in real-time when I'm receiving data over the ARINC 629 bus.
I've learned that Win32 is not the best environment for graphics that need to be continuously updated, but my app is working fairly well except for some flicker.
It seems most of the suggestions on how to reduce flicker don't address when a function is called continuously.
Double-buffering does not seem to help much.
In this case, if I could just redraw the rectangle and not the entire client area, I could reduce or eliminate the flicker.
Here is a code snippet:
void twirling_baton_left_channel(HDC hdc, HWND hWnd, unsigned int left_chan_data_receive)
{
LPRECT lpRect;
RECT rect_chan_one;
lpRect = &rect_chan_one;
lpRect->left = 0;
lpRect->top = 0;
lpRect->right = 0x01ba;
lpRect->bottom = 0x01c6;
static unsigned int wait_to_redraw_left_chan = 0;
status_baton_one.left = 0xb2;
status_baton_one.top = 0x28;
status_baton_one.right = 0x11c;
status_baton_one.bottom = 0x4b;
/* draw the running indicator */
if(wait_to_redraw_left_chan > 5)
{
RedrawWindow(hWnd, lpRect, NULL, RDW_INVALIDATE); //RedrawWindow((HWND)hdc, NULL, NULL, RDW_ERASE);
wait_to_redraw_left_chan = 0;
}
wait_to_redraw_left_chan++;
//SelectObject(ps.hdc, GetStockObject(DC_BRUSH));
if(left_chan_data_txrx)
{
SetDCBrushColor(hdc, RGB(0, 255, 0)); // green
Rectangle(hdc, status_baton_one.left, status_baton_one.top, status_baton_one.left+30, status_baton_one.top+20);
}
else
{
SetDCBrushColor(hdc, RGB(128,128,128)); //grey
Rectangle(hdc, status_baton_one.left, status_baton_one.top, status_baton_one.left+30, status_baton_one.top+20);
}
}
I'm trying to use the RedrawWindow() command to only redraw the box defined by the 'Rectangle' function, but it causes the entire client area to be redrawn, which causes a flicker since it is being called continuously.

plain winapi c GUI changing static text background

I am using plain winapi c to create a GUI, I am new to this language and am struggling with something many might think is basic. Could someone please explain to me how I change the background colour for static text because currently is transparent. The code I am using for the text is:
hwndStatic = CreateWindow(TEXT("static"), TEXT(""),
WS_CHILD | WS_VISIBLE,
10, 70, 90, 25, hwnd, NULL, g_hinst, NULL);
In general, you change the drawing of static text controls by handling WM_GETCTLCOLORSTATIC.
In that handler, you can change things about the DC, like the text color, background mode, background color, even the font that's selected.
You can also return a handle to a GDI brush (using a cast to get it by the type system). The control will erase itself first with the brush and then draw the text.
The callback will happen for all static controls that are children of the current window, so you first test to see if it's the child you care about.
For example:
case WM_CTLCOLORSTATIC:
HWND hwnd = (HWND) lParam;
if (hwnd == hwndStatic) {
HDC hdc = (HDC) wParam;
::SetTextColor(hdc, RGB(0xFF, 0, 0)); // set the text to red
::SetBkMode(hdc, OPAQUE);
::SetBkColor(hdc, RGB(0x00, 0xFF, 0x00)); // set background to green
HBRUSH hbrBackground = ::GetSysColorBrush(COLOR_WINDOW);
return (INT_PTR) hbrBackground;
}
return 0;
This shows several things you can do. You probably don't want to do all of them, but it can be educational to see them all in action.
Note that if you create a brush to return, you have to keep track of it and delete it later. I've avoided this issue by relying on GetSysColorBrush. The system owns those, so you shouldn't delete them. You can also use GetStockObject for system GDI objects that you don't have to manage. But if you need a custom color, you'll have to use CreateSolidBrush and then clean it up.
Respond to the WM_CTLCOLORSTATIC message in your program and have it return a brush object of the proper color.
I've slightly modified the example from the link:
case WM_CTLCOLORSTATIC:
{
HWND hWnd = (HWND) lParam;
if (hWnd == hMyStatic)
{
HBRUSH hbrBkgnd = CreateSolidBrush(RGB(0,0,0));
return (INT_PTR)hbrBkgnd;
}
return 0;
}

SetBkMode(hdc, TRANSPARENT) doesn't work

When I use SetBkMode(hdc, TRANSPARENT); in the code below, I got the following effect when I resize the main window (and hence when the child receives the WM_PAINT message):
The problem is : When I resize the main window, The old area of "Find:" shoule be erased, I guess. But it just remains there.
If I don't use SetBkMode(hdc, TRANSPARENT);, I don't have this problem. It looks like:
, i.e it has white background. Furthermore, if I use SetBkMode(hdc, TRANSPARENT);, it looks like the same as above, before I resize the main window. So I don't think SetBkMode(hdc, TRANSPARENT); works here.
the hwnd is a static child with style SS_BITMAP.
Do you know why this issue occurs?
switch (message) {
case WM_PAINT:
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
SelectObject(hdc, gDefaultGuiFont);
SetBkMode(hdc, TRANSPARENT);
RECT rc;
GetClientRect(hwnd, &rc);
DrawText(hdc, _TR("Find:"), -1, &rc, DT_SINGLELINE | DT_CENTER | DT_VCENTER);
EndPaint(hwnd, &ps);
return 0;
.............
}
Try to use "fixed" rectangles. For example
RECT rc;
GetClientRect(hwnd, &rc);
rc.left += ...; rc.top += ...; // shift up-left point
DrawText(hdc, _TR("Find:"), -1, &rc, DT_SINGLELINE | DT_LEFT | DT_TOP);
The idea is you draw text in wrong position (once) and in right position (twice) while backgound updated only once. Can't say more on part of code.
The problem is that windows is not updating the control (in time) that's behind your static control, you are now responsible for it's contents. So you want to use the background provided by the parent. Well just ask the parent to draw it for you in the child window:
RECT rc;
GetClientRectRelative(m_hWnd, GetParent(m_hWnd), &rc);
SetWindowOrgEx(m_mdc, rc.left, rc.top, NULL);
SendMessage(GetParent(m_hWnd), WM_PAINT, (WPARAM)(HDC)m_mdc);
SetWindowOrgEx(m_mdc, 0, 0, NULL);
In which
bool GetClientRectRelative(HWND hWnd, HWND hWndRelativeTo, RECT *pRect)
{
RECT rcWnd, rcRelativeTo;
if (!GetClientRect(hWnd, &rcWnd) ||
!ClientToScreen(hWnd, (POINT*)&rcWnd) ||
!ClientToScreen(hWnd, (POINT*)&rcWnd + 1) ||
!GetClientRect(hWndRelativeTo, &rcRelativeTo) ||
!ClientToScreen(hWndRelativeTo, (POINT*)&rcRelativeTo) ||
!ClientToScreen(hWndRelativeTo, (POINT*)&rcRelativeTo + 1))
return false;
pRect->top = rcWnd.top - rcRelativeTo.top;
pRect->left = rcWnd.left - rcRelativeTo.left;
pRect->right = rcWnd.right - rcRelativeTo.left;
pRect->bottom = rcWnd.bottom - rcRelativeTo.top;
return true;
}
Now draw anything you like, I suggest you'd use the TRANSPARENT background mode.
Please create all your child windows with the styles WS_CLIPCHILDREN and WS_CLIPSIBLINGS, then these problems will become apparent immediately and you avoid flicker.

How to display an image in full screen borderless window in openCV

I want to display an image in OpenCV in a full screen borderless window.
In other words, only the image pixels will appear, without menu, toolbar, or window background.
Using imshow() or cvShowImage() don't enable it:
The window grows to be full screen
in width but not in height. It misses few pixels.
I could not make it borderless even by changing settings of window
handler.
I think that the problem is rooted in cvNamedWindow() method which creates main WS_OVERLAPPED window, then creates a child and all functions like imshow() or cvGetWindowHandle() operate on the child.
Thus even windows command:
SetWindowLong((HWND)cvGetWindowHandle(winName), GWL_STYLE, WS_VISIBLE | WS_EX_TOPMOST | WS_POPUP);
Doesnt help, since the child cannot become borderless WS_POPUP. Someone got a workaround?
Maybe, showing opencv mat to window
without using opencv built in methods
Or some kind of windows trick
P.S. I tried the following code:
cvMoveWindow("AAA",0,0);
cvSetWindowProperty("AAA", CV_WINDOW_FULLSCREEN, CV_WINDOW_FULLSCREEN);
// Also I tried this:
HWND hwnd = (HWND)cvGetWindowHandle("AAA");
RECT windowRect;
windowRect.left = 0;
windowRect.top = 0;
windowRect.right = cxScreen; //Display resolution
windowRect.bottom = cyScreen; //Display resolution
AdjustWindowRect(&windowRect,WS_VISIBLE,false);
long p_OldWindowStyle = SetWindowLongPtr(hwnd,GWL_STYLE,WS_POPUP);
SetWindowPos(hwnd,HWND_TOP,0,0,windowRect.right,windowRect.bottom,SWP_FRAMECHANGED | SWP_SHOWWINDOW);
SetWindowLong(hwnd, GWL_STYLE, WS_VISIBLE | WS_EX_TOPMOST | WS_POPUP);
Have you issued cvShowImage() to display the window? Because it seems you are not doing it. Anyway, you might want to call the win32 API for this instead, so add a call to ShowWindow(hwnd, SW_SHOW); after SetWindowPos().
If your current call to SetWindowPos() doesn't do the trick, check this answer: Hide border of window, if i know a handle of this window
I recommend you doing your tests without calling cvSetWindowProperty() at first, just to make sure you can find a method that works.
Just a note, if you check modules/highgui/src/window_w32.cpp you can see how OpenCV creates windows on Windows.
EDIT:
The following code implements the tips I gave before and bypasses the problems the OP reported. The trick is NOT using cvGetWindowHandle() to retrieve the windows' handle and use directly win32 API for that: FindWindow()
IplImage* cv_img = cvLoadImage("test.jpg", CV_LOAD_IMAGE_UNCHANGED);
if(!cv_img)
{
printf("Failed cvLoadImage\n");
return -1;
}
cvNamedWindow("main_win", CV_WINDOW_AUTOSIZE);
cvMoveWindow("main_win", 0, 0);
cvSetWindowProperty("main_win", CV_WINDOW_FULLSCREEN, CV_WINDOW_FULLSCREEN);
cvShowImage("main_win", cv_img);
//HWND cv_hwnd = (HWND)cvGetWindowHandle("main_win");
//if (!cv_hwnd)
//{
// printf("Failed cvGetWindowHandle\n");
//}
//printf("cvGetWindowHandle returned %p\n", *cv_hwnd);
HWND win_handle = FindWindow(0, L"main_win");
if (!win_handle)
{
printf("Failed FindWindow\n");
}
SetWindowLong(win_handle, GWL_STYLE, GetWindowLong(win_handle, GWL_EXSTYLE) | WS_EX_TOPMOST);
ShowWindow(win_handle, SW_SHOW);
cvWaitKey(0);
cvReleaseImage(&cv_img);
cvDestroyWindow("main_win");
This code will make the window created by OpenCV borderless, but you still might have to tweak one thing or another to make this operation perfect. You'll see why. One idea is to resize the window and make it the size of the image.
EDIT:
Well, since you stated:
writing a demo might be very hard
I also decided to do this last part for you, since I'm such a nice guy =]
This is a small improvement of the code above:
HWND win_handle = FindWindow(0, L"main_win");
if (!win_handle)
{
printf("Failed FindWindow\n");
}
// Resize
unsigned int flags = (SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER);
flags &= ~SWP_NOSIZE;
unsigned int x = 0;
unsigned int y = 0;
unsigned int w = cv_img->width;
unsigned int h = cv_img->height;
SetWindowPos(win_handle, HWND_NOTOPMOST, x, y, w, h, flags);
// Borderless
SetWindowLong(win_handle, GWL_STYLE, GetWindowLong(win_handle, GWL_EXSTYLE) | WS_EX_TOPMOST);
ShowWindow(win_handle, SW_SHOW);
And on my system it displays exactly what you asked on the question.

Resources