groupbox as parent, win32 C

Started by
8 comments, last by szecs 12 years, 8 months ago
Hi all!

This should be a simple win32 GUI task. I want to have a group of controls within a groupbox and they can be hidden/ disabled/ resized as a single unit. I want it to be as simple as possible. I could make it work but I'm getting a bit tired of all the hacks I do to make something work.

The solutions I can think of:

1. The groupbox is only a decoration. The groupbox and the controls are children of the same main window.
This means that all the visibility, states and positions of the controls have to be handled manually, and their relative positions to the groupbox need to be stored (preferable as the "userdata" of the control).
It's a bit hacky solution IMHO

2. The controls are actually children of the groupbox, so the visibility and the positions are handled "automatically". This way, I need to (?) subclass the groupbox and pass on (or handle on the spot) the WM_COMMAND messages with PostMessage to the main window. This sort of works too, but I'm not very comfortable about it, maybe more messages need to be handled or there are cases when this setup fails.

I feel that there's got to be a simple and "standard" way to achieve what I want. Something that makes the controls children but the system messages are passed to the main window.

Thanks in advance for any hints!
Advertisement
All right them, number 2 it will be. My GUI is a bit simpler than a general GUI, for example keyboard navigation is not possible through the widgets, so the "panel" I'm working on is like a toolbar (keyboard focus has to be on the main window). Maybe this makes the task easier and I don't have to worry about corner cases.

But I still would appreciate some feedback.
I can't for the life make the visibility work.

I want to have the following window hierarchy:
[attachment=4827:hierarchy.JPG]
Some controls ("edge"/"poly"/"element" toggles) affect the visibility (show, hide) of the other controls as well as the positions of the group boxes. I control these through [font="Courier New"]EnumChildWindows[/font] with the main window handle. I use [font="Courier New"]ShowWindow[/font] for every single control in [font="Courier New"]EnumChildWindows[/font].

Everything works fine (positions are fine, behaviour is fine), only the drawing is fucked up. I use [font="Courier New"]WS_CLIPCHILDREN[/font] for the main window but not for the child windows. To my understanding, this would mean that the whole "panel" with the widgets should be redrawn fine (the "panel" window, which was created before the other widgets, should be rendered over the old screen).

This worked fine when all widgets were children of the "panel window", it works when the whole main window is repainted (activated for example), It even works if I resize the main window, but not when I toggle the said controls. I call [font="Courier New"]EnumChildWindows[/font] whenever I toggle those, it's the same [font="Courier New"]EnumChildWindows[/font] I call when the main window is resized.

Image: at the moment, only the controls (first image) in the "selection" group are children of the groupbox. The others (like the selection controls for the "poly selection") are children of the "panel" window. The images show only this panel window.
[attachment=4828:bug.JPG]
The 5th image is when I click "poly" then "element", the 6th is when I click "element" right after "edge". It's clear that the widgets don't get "removed", maybe the "panel" window doesn't repaint?

Maybe EnumChildResizeProc is not enumerating the child windows in the order I expect?

I have no idea where the bug is, I'll try to debug it somehow, but this little thing totally stopped me.
Well, It seems that it's Z order dependant, and that the topmost child is enumerated first. Then I don't get how can it be rendered fine at all, if WS_CLIPCHILDREN is not set. This whole behaviour seems so random for me. Maybe I should stick with the hacky as shit manual fucking around and not building this fucked up hierarchy.
Make a background child window that you put both the group box and the controls in. The group box will only render its edges if I remember correctly, and doesn't have any fill color at all, so you must make sure the background is properly rendered before the group box. Clipchildren is perhaps interfering with this, causing the group box to have no background.
Try using clipsiblings for the group box and make sure the Z-order has it at the bottom, while keeping both the group box and the controls in the same owner window which paints the background. I'm mostly guessing though..
Try making a dialog box resource with a group box and some controls and use Spy++ to see how they are parented and what styles are used.

EDIT: Checking out the standard Windows graphics properties with Spy++, which has group boxes with buttons and text in them, it seems to be done that way.
Note that if you allow resizing you might get flickering. I don't think the group box was ever meant to be resized, since it's dependent on another window to draw its background.
All controls are on top of a background child window ("panel" window).
I create it like this:
[source]LRESULT CALLBACK RightToolbarProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_COMMAND:

//if( HIWORD(wParam) == 0 )
{ MainMenuFunc(LOWORD(wParam),HIWORD(wParam));
InvalidateRect(MainWindow.hWnd,&(MainWindow.WindowRect),GL_TRUE);
}
return 0;

case WM_RBUTTONDOWN:
case WM_LBUTTONDOWN:
case WM_MBUTTONDOWN:
SetFocus(MainWindow.hWnd);
return 0;
}

//return 0;
return DefWindowProc(hwnd, msg, wParam, lParam);
}

void CreateRightSideToolBar(win_state_type *win)
{
hWndRightToolbar = NULL;
WNDCLASSEX wc;
char *class_name = "right toolbar class";

memset(&wc,0,sizeof(wc));

wc.cbSize = sizeof(WNDCLASSEX);
wc.style = 0;
wc.lpfnWndProc = RightToolbarProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = win->hInstance;
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE+1);
wc.lpszMenuName = NULL;
wc.lpszClassName = class_name;
wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);

if(!RegisterClassEx(&wc))
{
MessageBox(NULL, "Window Registration Failed!", "Error!",
MB_ICONEXCLAMATION | MB_OK);


return;
}


hWndRightToolbar = CreateWindowEx(0,
class_name,
(LPCTSTR) NULL,
WS_CHILD,
0,0,0,0,
win->hWnd,
(HMENU) (int) (RIGHT_TOOLBAR_ID),
win->hInstance,
NULL);


ShowWindow(hWndRightToolbar, SW_SHOW);
}[/source]

It's the main window that gets resized then the "panel window" is MoveWindow-ed. I wanted to use MoveWindow for the groupboxes too.

The initial Z-order is okay, because everything is rendered fine. If the main window is occluded then repainted, the result is fine. It seems that the "panel window" is not repainted for some totally obscure reason (for me at least). I'm calling exactly the same code when changing the button states and when handling the main window resize. Maybe I don't handle a message in the windproc I posted? Maybe I should handle WM_PAINT, but then why does it repaint properly sometimes?
I don't understand exactly what you want to achieve, but if possible make a separate panel window for every configuration you want to show, and then just hide the current panel and show the other panel when you change configuration. If not, try force repaint everything by hiding then showing the panel window again.
Ha! Hiding then showing the panel did it! Thanks very much!

BTW I want to expand/shrink groupboxes, "scrolling" the whole GUI and stuff like that, and this hierarchy will certainly make my life easier with that.

Thanks again.
There is no better way to say this so I'll just say it: your GUI is hideous! I strongly encourage you to either use a property grid control (for example, see this for a pure win32 implementation), or layout your controls in an external resource editor (for example, see resedit). Also, I think you will find the following to be VERY useful:

Windows User Experience Interaction Guidelines

As for hiding and scrolling controls in units, the standard is to place controls on separate dialogs. Inside a dialog, you can group controls together logically using group boxes. The controls grouped inside a group box are children of the dialog, not the group box - so in effect the group box provides visual grouping only.

Typically, each dialog will contain controls related to a particular function, and the user is presented with one such dialog at a time. The application then decides which dialog to display to the user based on selection, context, or whatever. The most common way is to use a tab control. When the user selects a tab, the application shows the corresponding dialog, which will be a child of the tab control), and hide all other dialogs. An alternative but similar approach is to use a tree control, and display the corresponding dialog when the user selects a tree item. In both cases, you will typically create one big dialog containing either a tab control filling its entire area, or a tree control (typically to the left of the dialog) and an empty area next to it to display the relevant child dialogs.

Sorry I didn't read the entire thread, so I apologize if the above is irrelevant. Hope it helps.

Thanks for the hint, but it's not a dialog type GUI, nor a property grid like GUI.

It is the main GUI panel for an 3D mesh editor (you can check it out in my journal), so I need to access the GUI and the "client area" (3D) simultaneously without either of them stealing focus or blocking program flow like a dialog.
So in behaviour, the GUI is more like a tool window (no keyboard navigation). Maybe it's not the best user experience-wise as it looks like a dialog, I'm planning to replace the text buttons with icons, but that's not to come yet.

It's not a property grid either, because most of the widgets are command widgets that perform actions, not properties.

IMHO tab controls won't be the best choice instead of the toggles, because those toggles mean states, which are very important, they affect the whole behaviour of the program, the render of the 3D view etc, and it's even possible that none of them are toggled, which is another state.

So it is a very context dependant GUI and I admit: it is heavily inspired (...) by 3ds MAX's interface, which I find quite useful for the needs of my program.

Maybe the layout is not the best yet, but first task is to get it work, then I can make it fancier.


Thanks for the links anyway, because some time in the future, I will get to the point when I will need property grids.


As for the problem: I didn't need to hide then show the panel, after refractoring and properly implementing the visibility control (thanks to the hierarchy), everything works as expected now.

This topic is closed to new replies.

Advertisement