C++ Win32 Owner Drawn Button's Down State - c++

I had this working in my previous program that got lost when my hard drive crashed.
I vaguely remember how I did it, but haven't been able to find any solutions on google or msdn.
case WM_DRAWITEM: {
// pointer to items draw structure and the id
LPDRAWITEMSTRUCT pDIS = (LPDRAWITEMSTRUCT)lParam;
int ctlResID = pDIS->CtlID;
if (ctlResID == IDC_TEST) {
// button drawn here to pDIS->hDC // this works fine
// now I want to draw the button differently if it is in its down state (holding the button down or w/e)
}
Is what I have, I think I remember one of the values in the draw item struct tells, but I cant find anything about it online.
Does anyone happen to know how to do this?
Thanks.

Related

Drawing a custom mouse cursor

I am in need of drawing a custom mouse cursor using C++ for my application. I am using a device to track hand movements and translate them into a cursor position on the screen.
However, for this application the system cursor may or may not be displayed, so I need to somehow draw onto the screen. I also need to interact with any window. Because of this, I'm not sure of my course of action - I believe I can create a "transparent" window to draw on while passing clicks/drags through onto the next window but am not sure how to go about this.
EDIT: Clarification - This application might be run on a touchscreen device where the cursor is hidden by some means. I'm trying my best to create this application as "hands-off" of the host system as possible - this program is meant to be a drag and drop application that will just work on a system with none or very very minimal actions done to the settings of the host system itself.
Currently, I have the following which draws a cursor at the location of the "mouse" but I cannot figure out how to refresh the screen or draw over the old "cursor" before drawing the new one. This causes a repeated cursor across the screen:
// Some thread
DWORD WINAPI Cursor(LPVOID lpParam) {
auto dc = GetDC(NULL);
std::optional<POINT> point;
while (true) {
if (point.has_value()) {
auto p = point.value();
RoundRect(......); // Paint over old cursor
}
POINT p;
if (GetCursorPos(&p)) {
point.emplace(p);
RoundRect(......); // Paint new cursor
}
}
I've read that simply calling another RoundRect on the same location will clear it but that's obviously not the case or I'm doing it wrong.
I believe my program can have super privileges if it needs to. Working with WINAPI seems to be kind of a hassle compared to this kind of stuff on linux, regarding permissions of working with different windows and allowing clicks and such to go through....
Any suggestions for how to proceed?

Nested panel not scrolling as expected. Why not?

I am creating a GUI using C++ and winapi. I have been able to work out a lot of details, but I'm having a bit of trouble with the scrolling of panels within another panel. I know how to do this in c#, but with c++ I am defeated.
I have created class which generates a panel for a generic "Topic" and I can designate the name of the topic (e.g., "Topic A"). I have create a separate class which generates a Panel to contain all of these topics. That is the TopicHolder. So I pass in a vector with the names as strings. I then generate new Topic objects, each with their own GUI control. I then update the layout of TopicHolder, including all of the Topics, giving them x and y locations based on their standard size.
Everything looks more or less like I want it to initially. If this was c# the topic holder would scroll and the various topics would scroll in and out of sight depending on the exact portion of the larger panel which was visible. In this case, however, scrolling has absolutley no effect. This is my first time working with scrolling on a c++ gui, so I might be missing something.
Below are some of the key code segments. I'd welcome any helpful tips.
Thanks
Adding the topics
void TopicHolder::SetTopics(std::vector<std::string> t)
{
for(unsigned i=0;i<t.size();i++)
{
//I give the topic the HWND of the TopicHolder so it can
//be properly parented on that GUI element
Topic* tmp = new Topic(myHWND, t[i]);
vectorOfTopics.push_back(tmp);
}
UpdateLayout(holderRect);
}
Handling topic layout relative to TopicHolder
void TopicHolder::UpdateLayout(RECT r)
{
int buffer = 5;//buffer between elements
int x = buffer;
int y = buffer;
for(unsigned i=0;i<vectorOfTopics.size();i++)
{
RECT tmp;
tmp.left=x;
tmp.top=y;
tmp.right = r.right-(2*buffer);
tmp.bottom = y+Topic::HEIGHT;//standard height per topic
vectorOfTopics[i].Layout(tmp);
y+= Topic::HEIGHT+buffer;
}
}
Layout in Topic object
void Topic::Layout(RECT r)
{
SetWindowPos(myHWND, //this is the HWND of the topic
HWND_TOP, r.left, r.top, Topic::WIDTH, Topic::HEIGHT, SWP_SHOWWINDOW);
}
-------------------Edit-----------------------------------------
Grr... Okay, so C++ has no support for this as many of you have explained. I have (painfully) been learning to set up my scroll parameters. Mostly it is working as expected, but I still have a problem with the scrollbar. I expected the scrollbar to show up on the right side of the panel and leave the rest of the space for my Topic panels. Instead it is slightly offset and the rest of the area is painted a light gray rather than the background color of the TopicHolder. Right now it's just irritating, but I would like to see it resolved. Again, any help would be appreciated.
Starting y position of topics in UpdateLayout always remains 5, so no scrolling happens. You should keep some offset value and change it if scrolling up and down happens so y will start from this offset.
You need to call SetScrollPos() to change the scrolling position or the window contents.
i.e:
// hwnd is your control window, SB_VERT refers to its verti8cal scrollbar.
SetScrollPos(hwnd, SB_VERT, topicHeight * topicToBringToTop, TRUE);
InvalidateRect(hwnd, NULL, TRUE); // redraw the control.

How to move an object with the mouse in c++?

I have a problem trying to create a script that moves an object with the mouse. The object is supposed to move when I hold the mouse button (which button does not really matter, so let us leave it there. I use SDL, so I have no trouble implementing this). I could of course have a variable moving = true when I start the move and then update position until moving = false, but this seems to be possibly messy. What I want is instead to be able to call a function grab_piece and then let the piece follow the mouse until I call the release function (I am creating a chess game, from where piece comes).
So the ideas, and relevant code this far: The idea is to assign a pointer to the mouse x and y positions and this would of course move the piece at the same time as the mouse. However, this seems tedious since shifting around pointers seems to be highly error prone. Is it possible to do this in a safe way? Otherwise I am open for other proposals.
Example code
SDL_Event e; //Event queue more or less
while (!quit) //Main loop, a little simplified
while (SDL_PollEvent( &e ) != 0){
if (e.type == SDL_QUIT){
quit = true;
}
if(e.type == SDL_MOUSEMOTION){
SDL_GetMouseState(&mouseX, &mouseY);
//do irrelevant things
} else if (e.type == SDL_MOUSEBUTTONDOWN){
//get mouse position
SDL_GetMouseState(&mouseX, &mouseY);
//get the board position
mouseBinPos = misc::screen2idx(mouseX, mouseY);
//location in the piece vector (hard coded)
currentPieceIdx = chess_board.getMappedPiece(mouseBinPos);
if (currentPieceIdx>=0){
chess_board.getPiece(currentPieceIdx)->grab_piece(mouseX, mouseY);
}
} else if ((e.type == SDL_MOUSEBUTTONUP)) {
//will release the piece and snap to nearest bin. Not implemented yet.
snap2bin(in args);
}
}
}
void Piece::grab_piece(int mouseX, int mouseY){
//Set pixel positions
setPixX(mouseX);
setPixY(mouseY);
}
All the variables are integers except the event queue, the board and the pieces which is a struct, user defined class and user defined class.
This is actually more a design issue, so I do not require executable code. More tips on how to do the design, if I should assign a pixX and pixY should be pointers to mouseX and mouseY and so on.
If anyone have an idea I would be happy. I have tried to describe the problem as good as possible, but if you have any questions please comment. I would describe further attempts if I had any, but I do not really know where to start. Also, this is not work, or school and I have been thinking about this for some days now and not found a good way to do it. So I am not just trying to get away easily.
Your ideas about pointers seem ill-defined, and are probably leading you down a path that isn't useful.
You need to use the same event loop as always when you're moving a piece. That means that MOUSEMOTION sometimes has to adjust the location of a piece, and sometimes not. That means that somehow you need to track the "moving a piece" state.
Here's the basic recipe:
On button down, save the idx of the piece that you've clicked on, maybe in a variable called "piece_being_dragged", or something. You also need to save the offset of mouse pointer to object center, so that the object doesn't recenter itself right on the pointer. You'll see what I mean when you get there.
On button up, zero the drag state variables, and handle the "done moving" logic.
On mousemotion, when piece_being_dragged is non-zero, look the piece up from the index, and set the new location based on the mouse pointer, and on the previously saved center offset.
Is this all a little messy? Yes. Is it necessary? Yes.

Improve code for WM_PAINT and WM_CTLCOLORSTATIC handlers

INTRODUCTION AND RELEVANT INFORMATION:
I have implemented complex painting of the main window’s background and its child static controls.
The picture below shows how it looks.
Static controls have SS_NOTIFY style, which is important to mention, as certain things happen when user clicks on them.
At this point, actions activated when clicking on them, are not relevant.
Both main window, and static controls, have gradient backgrounds, which were made through usage of GradientFill(...) API.
Top banner of the main window is created with gray brush, and the grid lines were created with LineTo(...) and MoveTo(...) API.
Map on the orange static control, and the top left logo are EMF files, top right logo is PNG file, and other pictures are bitmaps.
Orange static control has 4 child static controls which are owner drawn and also have SS_NOTIFY style.
It was the only way I could have thought of, which enabled me to draw the control the way it was asked of me ( if I can improve on this, please suggest it, I will accept any reasonable suggestion ).
In order to paint the orange static control, I have decided to paint its background in WM_CTLCOLORSTATIC handler, and to owner draw child static controls in a subclass procedure.
Notifications received from child static controls are also handled in the orange static controls subclass procedure, since I didn’t know how to forward them to the parent window, but are omitted since they are also irrelevant at this moment.
I have decided to provide link to the demo project, instead of making this post quite lengthy with code snippets.
I have tried to submit demo application as small and simple as it is possible.
I did not skimp on the commentaries, so I believe everything is well covered and explained in source code.
If there are still questions please leave a comment and I will reply as soon as possible ( usually immediately, or in the same day, at least ).
Here is the link to the demo project:http://www.filedropper.com/geotermistgrafika_1
Important update:
/==========================================================/
Text bellow in square brackets was the original part of the question, but is now omitted since the project had memory leaks.The above link links to an improved version.
[ Updated in response to member xMRi's comment: This link should be fine: http://www.filedropper.com/geotermistgrafika ]
/==========================================================/
I work on Windows XP, using MS Visual Studio C++ and pure Win32 API.
One note: since Express edition of VS has no resource editor, resource file and resource header were created using ResEdit from here: http://www.resedit.net/.
PROBLEM:
When I resize my window, static controls slightly flicker.
MY EFFORTS TO SOLVE PROBLEM:
I believe that my code has no memory leaks-therefore I doubt this is the problem, but being inexperienced, I would highly appreciate if my assumption can be somehow confirmed.
I think that I have properly handled WM_ERASEBKGND, and I have excluded styles CS_VREDRAW and CS_HREDRAW from my window class-therefore flickering should not be caused because of this.
I have forgot to mention, that my window has WS_CLIPCHILDREN style, so I am mentioning that now, in response to the comment bellow made by member Roger Rowland.
I have implemented double buffering for both handlers, in order to avoid flickering.
QUESTIONS:
How can I modify code in demo project to get rid of flickering?
I need advice on how to optimize both WM_PAINT and WM_CTLCOLORSTATIC handlers, so my painting code gets more efficient and faster.
A small note for second question:
I was thinking to improve my code by drawing the entire picture on the main window’s background, and to put transparent static controls on top of the part of the picture that corresponds that static controls background.
That way, I would only return NULL_BRUSH in my WM_CTLCOLORSTATIC handler, and do all the work in the WM_PAINT.
Am I on the right track with this idea? Could this work ?
Thank you.
Regards.
Firstly, your App is leaky as hell. Haven't looked for leaks, but most of them should be in WM_CTLCOLORSTATIC as you forget to delete HBITMAP's(use this neat freeware http://www.nirsoft.net/utils/gdi_handles.html to look for gdi leaks).
Secondly, your code is way to big. I noticed that you didn't use functions, maybe because you don't know what they are capable of. For example I would use:
void DrawBackground(HDC &hDC, SOMEINFOSTRUCT GradientInfo, LPCTSTR Text);
to simplify your code a lot.
Anyway enough of lecturing, let's go back to your problem. In WM_CTLCOLORSTATIC you must return brush, you want to paint background with. What you're doing now is painting background manually using Bitblt(), then return NULL brush and program paints it on your already painted background. Instead of painting it yourself, let the brush do the job.
Simply instead of the last Bitblt() use CreatePatternBrush(), but then you need to take care of this Brush and here is what you should do:
HBRUSH TempBrush = NULL; //Create global brush
//Some Code....
case WM_CTLCOLORSTATIC:
{
if (TempBrush != NULL)
{
DeleteObject(TempBrush);
TempBrush = NULL;
}
//Let's skip to the end....
GradientFill( MemDC, vertex3, 3, &gTriangle, 1,
GRADIENT_FILL_TRIANGLE );
TempBrush = CreatePatternBrush(bmp);// these 3 line should be at the
//end of every if
DeleteDC(MemDC); // or put them once outside if's
DeleteObject(bmp); // also if you delete HDC first, you don't need to
//unselect hbitmap
}
return (LRESULT)TempBrush;
}
break;
case WM_CLOSE:
{
if (TempBrush != NULL)
{
DeleteObject(TempBrush);
TempBrush = NULL;
}
//.......

How to change looks of a disabled button and edit control?

Enabled
Disabled
When I disable a button ( Created with BS_BITMAP style flag ) it changes its look (Please see the above images), same thing happens with edit controls.
How do I make the controls not change when disabled ?
I can do that by subclassing the control, but is there an easier way ?
I don't want to subclass the controls just for that, if possible.
You do not need to subclass the control in order to do this, although I'd say it would be much cleaner. The alternative to set the BS_OWNERDRAW style and handle the WM_DRAWITEM message. That means you're taking over all drawing, but that's okay since you don't want it to look like a normal button anyway.
I could not agree more with Jonathan Potter's observation that it is extremely bad UI design to fail to indicate to the user which buttons are enabled and which ones are not. There are multiple ways to do this, but not doing it is not a viable option. Fortunately, it is easy to do with WM_DRAWITEM, since it tells you the button's current state.
So make the WM_DRAWITEM message handler look like this (in the parent window's window procedure):
case WM_DRAWITEM:
{
const DRAWITEMSTRUCT* pDIS = reinterpret_cast<DRAWITEMSTRUCT*>(lParam);
// See if this is the button we want to paint.
// You can either check the control ID, like I've done here,
// or check against the window handle (pDIS->hwndItem).
if (pDIS->CtlID == 1)
{
// Load the bitmap.
const HBITMAP hBmp = LoadBitmap(hInst, MAKEINTRESOURCE(IDB_BITMAP1));
// Draw the bitmap to the button.
bool isEnabled = (pDIS->itemState & ODS_DISABLED) == 0;
DrawState(pDIS->hDC,
nullptr,
nullptr,
reinterpret_cast<LPARAM>(hBmp),
0, 0, 0, 0, 0,
DST_BITMAP | (isEnabled ? DSS_NORMAL : DSS_DISABLED));
// Delete the bitmap.
DeleteObject(hBmp);
// Draw the focus rectangle, if applicable.
if ((pDIS->itemState & ODS_FOCUS) && ((pDIS->itemState & ODS_NOFOCUSRECT) == 0))
{
DrawFocusRect(pDIS->hDC, &pDIS->rcItem);
}
// Indicate that we handled this message.
return TRUE;
}
break;
}
Naturally, you could optimize this code further by loading the bitmap a single time and caching it in a global object, rather than loading and destroying it each time the button needs painting.
Note that I've used the DrawState function, which can draw bitmaps either in a "normal" (DSS_NORMAL) or "disabled" (DSS_DISABLED) state. That simplifies the code considerably, and allows us to easily handle the disabled state, but unfortunately the result looks a little bit ugly. That's because the DrawState function converts the bitmap to monochrome before applying any effects other than normal.
You probably don't like that effect, so you'll need to do something else. For example, use two separate images, one for the enabled state and the other for the disabled state, and draw the appropriate one. Or convert your normal color image into grayscale, then draw that for the disabled state.
And if the custom-drawing code runs too slowly, you can optimize it even further by checking the value of pDIS->itemAction and only re-drawing the necessary portions.
Then, once you think you've got everything all polished and efficient, the inevitable bug reports will start to roll in. For example, keyboard accelerators are not supported. Then, once you add support for these, you'll need to indicate that in the UI. That will be difficult with a bitmap that already contains the text; the only way to draw a letter underlined is to draw the text yourself. This all proves that owner-draw is way too much work. Just let Windows draw the controls the normal way, don't break everything for your users just because some designer thinks it "looks cool".