Jump to content
  • Advertisement
Sign in to follow this  
BrickInTheWall

C - Win32 - managing custom buttons.

This topic is 3009 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hi guys, I'm in a bit of a pickle here on how to continue. I'm writing a Win32 application and want to program everyting C-style and without using resources. It's working well so far. I have an owner drawn button (BS_OWNDERDRAW style)...drawing it works fine. These buttons will represent points in a coordinate system that the user can drag around. Also, I want the user to be able to add new points(buttons) whenever and wherever he wants. This is all no problem, but I'm really not sure how I should store all of these buttons. Also, every new button would need an individual ID wouldn't it? Otherwise if two buttons had the same ID and I were to intercept a "drag" action, I'd be moving both buttons eventhough only one is clicked. I was thinking of maybe using a linked list. I could try to generate ID's by incrementing a variable each time the user creates a point. If I have 10 points-> 1,2,3,4,5,6,7,8,9,10 ...if the user deletes no. 5-> 1,2,3,4,5 (formerly 6), 6 (formerly 7) etc... Any advice is appreciated. Cheers!

Share this post


Link to post
Share on other sites
Advertisement
I'm not sure how you structure your controls and such, and by that I mean, whether the buttons sit directly inside the window, or if they sit inside a parent control, but a more ideal solution I think would be to use what's built into the Windows API already. That is, EnumChildWindows and EnumChildProc. If you use these on the parent control, you could generate a list quickly or perform actions on the buttons themselves.

Share this post


Link to post
Share on other sites
Thanks, thats definately a good piece of help when handling messages that apply to all buttons at once. But my buttons positions don't depend on how big the window is for example. If a button needs to be redrawn, I'll get a WM_DRAWITEM message but still need to figure out which button it is (i.e. get it's ID) and then associate this ID with the corresponding point structure that stores info about the point button (it's color etc.). I have an idea, but it seems kind of overkill. I'm worried about ID ambiguity, for example that I might get into problems because a newly created buttons gets an ID (number) that maybe some static text box might already have. I'm not sure if thats bad, but I don't want that happening. I thought I'd have an array of 100 fields only for pointers to Point structures on the heap. Therefore the button ID's can start at 400 for example and go to 500, and I know this, so I can give everything else that needs an ID something outside of this range. I keep a variable that stores the number of points currently created.

If I create a point: malloc(sizeof(Point)) etc ...put a point on the heap, initialize it with the data specified by the user (e.g. color, visible etc.).
pointContainer[numOfPoints] = pointerToTheNewPoint. Then create the window...
CreateWindowEx(0, "button", "", WS_CHILD | WS_VISIBLE (or not) | BS_OWNERDRAW, x, y, width, height, hWnd, (HMENU)numOfPoints), ....);

Then when I get a WM_OWNERDRAW message, I can just use the ID to draw the appropriate button: drawButton(hDC, pointContainer[ID]....);

The only problem I see in this is that when I delete a button, I have to set all the ID's of the buttons behind the one that was deleted, so that they are in increasing order...and also decrement numOfPoints...that way I know that when I am using the ID I get sent, on the correct button in the array.
If I wanted to give a window that is already in use a new ID, would this require destroying it first and then recreating it with the new ID?

Share this post


Link to post
Share on other sites
This might be a stupid idea (I tinker in C).
Have a simple array. I guess you won't have a million elements. Maximum 5000.
So I would have an item_type item[5000]; with a used flag.

Start to fill item. If you delete an item, mark it unused. If you add a new item, search for the first unused item in the array and place the new item there. Just like in a multi-bullet-shooter bullet-vector (I guess you get it).

As for ID:s, maybe the ID simply could be the position in the array.

But I'm not a real programmer. I'm just tinkering and producing ugly code.
(And I hope that helps)

Share this post


Link to post
Share on other sites
Believe it or not but I had actually considered what you are suggesting since it's a method I like using when for example generating a random order of elements and need to fill all slots, and I think it's actually quite effitient. Though I'm no pro, I think this could work quite well.

Share this post


Link to post
Share on other sites
Just use a vector or malloc if you're working in c only. But instead of deleting elements and having to shift to fill the gap, you swap the element you want to delete with the element at the end, then erase the element at the end. You can't use this if you care about the order of the elements, but otherwise it's extremely efficient.

Share this post


Link to post
Share on other sites
Quote:
Original post by BrickInTheWall
... still need to figure out which button it is (i.e. get it's ID)


If you have the HWND for the button you can use GetWindowLong to retrieve the id.

e.g.

DWORD id = GetWindowLong(hwnd, GWL_ID);

Quote:
Original post by BrickInTheWall
... and then associate this ID with the corresponding point structure that stores info about the point button (it's color etc.).


You can use the id as the index into a lookup table or as the index key for some other kind of searchable container - list, queue, etc.

Quote:
Original post by BrickInTheWall
I'm worried about ID ambiguity, for example that I might get into problems because a newly created buttons gets an ID (number) that maybe some static text box might already have. I'm not sure if thats bad, but I don't want that happening.


It sounds to me like you need to rethink how you've arranged the interface. Do you really need to create new buttons? Maybe you can get by with re-instancing buttons or toggling the onscreen visibility of buttons. You don't need to create a new button if you simply want to change the text displayed on the buttons. Iirc, SetWindowText should do the job.

Quote:
Original post by BrickInTheWall
If I wanted to give a window that is already in use a new ID, would this require destroying it first and then recreating it with the new ID?


No. See SetWindowLong.


Share this post


Link to post
Share on other sites
I've chosen a method similar to szecs suggested. I have an array of pointers to point objects on the heap and if I delete one (using free) I just set that pointer to NULL, and when I create a new button, it goes in the first space of the array that is NULL. Also as suggested, I'm using the ID to access the array. Everything there works fine.
What I'm currently fighting with it trying to set the cursor to IDC_HAND when the user "mouseovers" on any of the buttons.
The button ID's can go from 800 to 900...here is my WM_SETCURSOR case:


int id = GetDlgCtrlID((HWND)wParam);

if (id >= 800 && id < 900)
SetCursor(LoadCursor(GetModuleHandle(NULL), IDC_HAND));
else
return DefWindowProc(hWnd, msg, wParam, lParam);

I'm not sure if calling DefWindowProc is ok here but it seemed logical to me since I was getting weird cursor behavior before.
I debugged the if (...) case gets called exactly when I want it to, but for some reason the cursor doesn't change. It flickers a bit (I think...it just looks like it but I can't see the cursor I want). Is something resetting the cursor? Or is it not being set at all? I've done a little research and have read that setting the window class cursor to NULL would help, though I'm not sure if this is true for WM_SETCURSOR cases generated by mouse overs on a windows child windows (my button controls).
Anyone have any experience with this?
Thanks for the help so far. I thought I'd ask here instead of starting a new thread.

Share this post


Link to post
Share on other sites
According to MSDN the cursor is changed, but will be changed back (to the type, with which you registered the window class) as soon as you move the mouse.

To change permanently:
SetCursor(cursor);
SetClassLong(hWnd,GCL_HCURSOR, cursor);

and change back manually when you have to, of course

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!