Custom button shape - c++

I want to implement a simple volume up/down button using a custom bitmap, and all it's going to have is a simple hover effect, and mouse down effect.
My first idea was to process WM_MOUSEMOVE for the hover, and process WM_LBUTTONUP and WM_LBUTTONDOWN for the mouse down effect.
First, is this the easiest way to do this? I could superclass a button and just paint the bitmap and forget about the text...
Then I have the problem with the background, which should be transparent, I know I can use a mask by passing SRCAND to BitBlt, but this requires me to have 2 images for each button. Is there any way to do this with just one image? Like if I put a green background on it in my image editor, could I mask that out with a green screen like effect?

You need to create a regular button, and subclass it with SetWindowSubclass. The button must have the owner-draw style on it, which means in the parent WndProc, you are handling WM_DRAWITEM. As you correctly say, you will be BitBlt'ing or StretchBlt'ing the background of the image on.
For hover events you must do the following:
Have some sort of shared boolean between the parent and subclassed WndProc, eg. IsMousedOver
In the subclassed WndProc, process WM_MOUSEMOVE. When this message is hit, you should set IsMousedOver, then invalidate the control with InvalidateRect (this will trigger WM_DRAWITEM in the parent)
In the parent WndProc, if you receive WM_MOUSEMOVE, you should clear IsMousedOver
In WM_DRAWITEM in the parent WndProc, you should check IsMousedOver and BitBlt based on that state
If you want to process MouseUp/MouseDown, you can change the boolean to an int instead and have a tri-state. However, most implementations have MouseDown being the same as the regular button, and MouseUp is then simply the regular moused over background.
As for your query about 2 images, it may well be possible to do it with one but I haven't tried that before.

Related

Get control under mouse on GUI with WS_EX_TRANSPARENT style

I have a LayeredWindow GUI that contains some child's and all of them contains the WS_EX_TRANSPARENT style.
The style is used to be able to remove their background.
When i move the mouse over the GUI only the LayeredWindow receives the message WM_MOUSEMOVE.
I tried calling ChildWindowFromPointEx using the XY pos got from the WM_MOUSEMOVE lParam
to detect the control being hovered, but the API didn't recognize any of the controls belonging to the child GUI's.
Docs says:
The search is restricted to immediate child windows. Grandchildren and deeper descendants are not searched.
The other option i tried was EnumChildWindow and compare each control rect to the XY position of the message, this method is using around 1% of CPU only from moving the mouse.
I wonder if there's any 'better' option?
According to the Doc:Layered Windows
Hit testing of a layered window is based on the shape and transparency
of the window. This means that the areas of the window that are
color-keyed or whose alpha value is zero will let the mouse messages
through. However, if the layered window has the WS_EX_TRANSPARENT
extended window style, the shape of the layered window will be ignored
and the mouse events will be passed to other windows underneath the
layered window.
You could try to use GetCursorPos function to get the position of the mouse cursor, in screen coordinates.

Allow clicks to pass through window(hit-test transparency) on a non-transparent window

I have a layered window that I want all clicks to pass through no matter where the mouse is located. Some parts of it are color keyed, and others are not. Clicks pass through the window whenever the mouse is on a transparent part but whenever the mouse is on a non-transparent part, the window captures the click. An easy solution would be just to add the WS_EX_TRANSPARENT flag to the window but I DO NOT want to do that. I tried returning -1 on WM_NCHITTEST in WndProc since WM_NCHITTEST is called every time the mouse enters a non-transparent zone but that didn't work and clicks still didn't pass through non-color keyed areas of the window.
Thanks in advance

How to create a child window is transparent and the parent window is not transparent?

I want to create a window with two child windows. Only the background of the top child window is transparent. I can directly see the background of the parent window and not the content of other child windows.Like the picture, A is parent, B is child 1, C is child 2.The background of child 2 is the same as parent.enter image description here
A-a-m,
You paint background on child window by yourself.
There is method OnPaint (in MFC) or message WM_PAINT.
And you should draw transparent background (it means to draw nothing).
Does it work?
If you use non-standart framework to create windows, you should specify background is painted programmatically.
A few options to experiment with:
If in your open source UI framework you can remove the WM_PAINT
handler entry in the Def­Window­Proc then you could do that.
Handle the WM_PAINT message yourself and do nothing in the handler except clear any flag that indicates that the client area
needs to be re-drawn. You'll need to find something equivalent to
ValidateRect.
Both of these methods MIGHT cause non client areas like the window frame to artifact within the client area as you move window C around but I can't be sure.

MFC Unclickable Button (Running away from cursor on MouseMove)

How would i make a button that will change it's position on MouseMove Event if the cursor is close enough to the center of the button in MFC ?
WM_MOUSEMOVE is not delivered to the button if the cursor is not over it (and is not captured, but you don't want that). So you have to process WM_MOUSEMOVE in the parent dialog. If you want your button to be a self-contained control, you have to subclass the parent window upon button creation.
Subclassing, in this context, means:
- you retrieve and store the parent's window proc address with GetParent()->GetWindowLong(GWL_WNDPROC)
- you set it to your procedure with SetWindowLong()
- in the procedure, you call the parent's previous window proc, after handling WM_MOUSEMOVE the way you want.
The WM_MOUSEMOVE coordinates will be relative to the screen, but you'll probably want to track the button position relative to the window that contains it. Use the ScreenToClient method on the parent window to convert, then you can compare the coordinates to see if it's close. Then use MoveWindow to move the button.
If you track the mouse cursor position you can determine when the cursor gets close to or enters the button window rect. You can then use the SetWindowPos() function to reposition the button window in the parent window client area.

Use DrawText to update label

I am currently writing a program in c++ (no MFC) and want to update a label (win32 static control) using the win32 DrawText function. However when i call the function nothing is written to the label. I use the following code:
HDC devCon = ::GetDC(GetDlgItem(IDC_TITLE).m_hWnd);
RECT rect = {10, 10, 100, 15};
::DrawText(devCon, _T("TEST DC TEXT!!!"), -1, &rect, DT_NOCLIP);
::ReleaseDC(GetDlgItem(IDC_TITLE).m_hWnd, devCon);
As you see with the GetDlgItem(...) I am using ATL but that should not be a problem in my opinion. When I specify NULL in the GetDC method the text is drawn in the upper left corner of the screen as it is supossed to be since the method return the DC to the entire screen.
Why doesn't this work with the DC of the label?
Hope you folks can help me.
If you want to draw the text manually because setting the control text doesn't do what you want, then you need to tell Windows that you're doing that. Otherwise the control will draw itself over whatever you do whenever it needs to be redrawn.
To draw it yourself, mark your control as owner draw by setting the SS_OWNERDRAW style, and then handle the WM_DRAWITEM message to draw it in the window procedure of the parent window, or subclass the window and handle the WM_PAINT message in your new window procedure.
I guess that the text is drawn but at the next window message is set to the default text.
Try to set the text with SendMessage(..,WM_SETTEXT,...);
Use SetDlgItemText() to set the text for the control.
You are trying to paint directly onto the static control's device context.
This is not going to be so simple, because:
the control will repaint itself whenever it's update region is invalidated
usually the controls share the device context with the parent window, so what you are getting in GetDC(...) is actually your dialog's device context.
So, use SetDlgItemText, or SetWindowText to set the text of the window.
To use a custom font (or set the text/background color), handle the WM_CTLCOLORSTATIC message in your WindowProc.