Archived

This topic is now archived and is closed to further replies.

Architekt

Designing a decent GUI

Recommended Posts

I''m in the process of designing some GUI widgets that I need specifically for my game. Some of the features I''m implementing are texture mapped widgets, and alpha blending among the non mundane. My main question though is in regards to event handling. One way I thought of how to do things was to have the user (most likely only me) register callback functions for each event they were interested in processing. A pointer to their function would then be stored and called when the proper event is triggered. But in terms of triggering the event...the first way to implement it that came to mind to me was something like this: In the DirectX mouse handling code, check for, e.g., a left mouse button down event. For each widget on screen, call their left mouse button down event, passing in the mouse coordinates. If the mouse coordinates lie within the bounds of the widget, then that event has been triggered, and the user''s callback code gets executed and returns the value of that callback. The process is much the same for every other callback. Now, it works, but I was wondering if there was a way to make this way simpler. Ideally, it would be cool if when a widget or dialog box was on the screen, if the dialog box (which contains all the widgets) could take over proecssing the mouse input, and keyboard input in the case of the edit box. By taking over the input, I could in the class have it check for the proper events and call teh right methods, instead of having the user get so involved in the process. Then, after the events have been processed, it could relay the event message or propogate it in some say so that just in case the user didn''t want to lose total control of their input abilities when dialog boxes are on the screen, they could still receive the input notifications. Just conceptually speaking (I''m not asking for source code), is there a way to at least get close to what I''m trying to achieve, or is the way I''m currently approaching it "as good as it gets"? Perhaps there''s a better method altogether. Thanks for your help, I greatly appreciate it.

Share this post


Link to post
Share on other sites
I've actually been working on the same thing. What I'm doing is this:

I have a CD3dTarget class that represents any class that is interested in keyboard / mouse / other messages. This class has a vector of CD3dTarget pointers that represents its' children. Each object has a Z order which represents if the window has focus (0 z order, or the topmost window) or is behind them. There is a Dispatch() member function in class CD3dTarget that is responsible for handling messages. The message handling starts in my window's WndProc like so:

    
LRESULT WINAPI CD3dWnd::WndProc(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam)
{
CD3dWnd *pD3d = (CD3dWnd*)GetWindowLongPtr(hWnd, 0);

if(pD3d == NULL)
return DefWindowProc(hWnd, nMsg, wParam, lParam);

if(pD3d->Dispatch(nMsg, wParam, lParam))
return DefWindowProc(hWnd, nMsg, wParam, lParam);

return 0;
}



The dispatch function looks like this:

          
bool CD3dTarget::Dispatch(UINT nMsg, WPARAM wParam, LPARAM lParam)
{
//PREDEFAULT PROCESSING

if(!PreProc(nMsg, wParam, lParam))
return false;

//DEFAULT PROCESSING

if(!DefProc(nMsg, wParam, lParam))
return false;

//DISPATCH TO CHILDREN

CD3dTarget *pChild = m_pChildren->GetFocus();
if(pChild != NULL)
return pChild->Dispatch(nMsg, wParam, lParam);

return true;
}


PreProc is another member function (virtual) that you can override in your derived class to handle special messages. My GUI (without overriding PreProc()) only deals with keyboard and mouse input, like so:


  
bool CD3dTarget::DefProc(UINT nMsg, WPARAM wParam, LPARAM lParam)
{
switch(nMsg){
case WM_MOUSEMOVE: return OnMouseMove(wParam, lParam);
case WM_LBUTTONUP: return OnLButtonUp(wParam, lParam);
case WM_LBUTTONDOWN: return OnLButtonDown(wParam, lParam);
case WM_LBUTTONDBLCLK: return OnLButtonDblClick(wParam, lParam);
case WM_MBUTTONUP: return OnMButtonUp(wParam, lParam);
case WM_MBUTTONDOWN: return OnMButtonDown(wParam, lParam);
case WM_MBUTTONDBLCLK: return OnMButtonDblClick(wParam, lParam);
case WM_RBUTTONUP: return OnRButtonUp(wParam, lParam);
case WM_RBUTTONDOWN: return OnRButtonDown(wParam, lParam);
case WM_RBUTTONDBLCLK: return OnRButtonDblClick(wParam, lParam);
case WM_KEYUP: return OnKeyUp(wParam, lParam);
case WM_KEYDOWN: return OnKeyDown(wParam, lParam);
case WM_CHAR: return OnChar(wParam, lParam);
default: return true;
}
}


In my GUI, there are only 3 levels of command targets. The Main D3D Window (CD3dWnd), the dialog boxes (CD3dDlg), and the controls (CD3dCtrl). Each of these classes is either directly or indirectly derived from CD3dTarget. In that hierarchy, I could have several dialog boxes each with several controls on-screen at one time, but the messages only get sent to whichever dialog and control have focus (0 z-order) at the time.

For controls where the dialog needs to be made aware of some events, I use pointers to member functions of those dialog classes. For example, when the 'ok' button is clicked, I'd like to handle that in the Dialog class, so I pass a pointer to OnOk() member function of my dialog class and call that from the button control when I determine that the button has been clicked.

I've basically taken the MFC classes and stripped them down and rewrote some portions with efficiency in mind. I do use a ton of virtual functions for events, but if you think about it, messages don't occur that often so the performance hit is minimal, and the clarity in code is significant. MFC handles messages with a message map which I've redone in virtual functions, and they handle telling a dialog box that a button was pressed by a process they call message reflection. Both of which are overkill for what I needed.

Anyway, I hope this helped somewhat.

Edited by - jonstelly on January 11, 2002 12:47:47 PM

Share this post


Link to post
Share on other sites