Sign in to follow this  
GroZZleR

Custom GUI : Issues with multiple windows.

Recommended Posts

GroZZleR    820
Hey all, My custom GUI solution is progressing nicely. I've got a small set of widgets working, which is more than enough to get me excited. While implementing control dragging I ran into a couple of issues that I'm hoping to get some insight on. Issue 1 - Multiple controls being dragged at once When dragging a control, if you pass overtop of another control, you'll begin to drag both. I'm looking for an elegant solution to prevent this. The only one I could think of would to have the GUI manager store the control that's being dragged and prevent any others from being dragged during this time. Issue 2 - Depth If a window with a button is sitting behind another window, when it comes time for the "hot zone" detection, the rear window's button event may fire if the mouse is in the right spot despite the fact it's being blocked by the window in the front. I'm sure I'm missing something very simple here but I just can't think of it. All insights welcome. Thanks!

Share this post


Link to post
Share on other sites
SimmerD    1210
1) Sounds good.

2) One approach to prevent this is to find each control that contains the mouse pointer, have each control know its Z order, and sort them in front to back order. Then pass the mouse event to each control in order. As soon as one returns 'true', the event is killed, and the other controls don't get updated.

The basic idea is for each control to have a chance to process it in front to back order, and for each one to tell you if it's been processed.

If you have only rectangular controls, you can determine for yourself which control the mouse lies in by intersecting all rectangles in front to back order.

Share this post


Link to post
Share on other sites
JY    289
Issue 1: sounds fair - or maybe just a flag to say that it's already dragging, not necessary to store the control.

Issue 2: window systems usually have what it called a z-order. That is a reference to how 'deep' the window is. When you check your 'hot zone' and you have more than window within it, simply take the one with the lowest z-order value (or highest depending on how you implement it).

Share this post


Link to post
Share on other sites
d000hg    1199
1)The window will only start dragging when you click it, and then move the mouse. So in your window's event handlers you can implement such logic. Having a focus window is useful anyway though.

2)Store the windows in depth-sorted order, and then check if a hit occurs starting at the front window. Then the first window which records a hit is the one you want. For this though the whole window must respond to hit-checking and then chefk its children (the buttons), rather than just checking the buttons.

I'm currently implementing a GUI myself so fel free to PM me about it.

Share this post


Link to post
Share on other sites
GroZZleR    820
Z-ordering seems like the solution I was looking for.

A follow-up though: How do you determine z-order? For instance, when you add all of the windows to the GUI manager, do you give a z-depth to all of the windows? When you click on one, what value do you give it? Obviously it'd have to be higher (or lower) than the rest, but how do you control the "maximum" z-value?

Thanks guys - this is really helping.

Share this post


Link to post
Share on other sites
LEET_developer    115
Another option is to use a window stack... slightly modified stack where you can remove from anywhere in the list but only push to the top.

so when a window gets focus, it goes on top of the stack. That way you can just pass your events down the stack and not have to worry about setting z-depth values. Then you can just draw all your windows from the bottom of the stack to the top. That will let you have some cool opacity effects without having to sort your windows (because the stack will keep them in order).

That's how mine works, the only problem I've seen so far is if you have alot of windows then there is a fillrate bottle neck, since you are drawing all parts of every window... with the z depth method you could draw from top to bottom and then the depth buffer would cull parts of the window saving you fillrate (but making the opacity effects harder).

Share this post


Link to post
Share on other sites
Telastyn    3777
I also use the 'object position is its orientation' like LEET here. I use a tree though, so actually the -last- object to meet the criteria [has the key bound, or is a hot-spot for the mouse event] is the 'target'. The tree has the added benefit of auto-magically moving and deleting sub-components automatically.

Obviously, this sort of arrangement is less ideal the less those benefits will be utilized.

Share this post


Link to post
Share on other sites
mfawcett    373
I go about it slightly differently. On mouse down the control that got the message now has mouse capture until mouse up, so no other control gets mouse messages while the button is down.

Dragging aside, you wouldn't want to press down on a button, then mouse over a different button and have it highlighted IMHO.

Click on a window's minimize button, then drag off and release and it cancels the action.

Do the same thing but mouse over something else that would normally receive mouse input, you'll see it doesn't get the input.

I mimic that behavior in my UI. You may want different results.

Share this post


Link to post
Share on other sites
HellRiZZer    308
Exactly like people say, you should store your controls in a stack and traverse it from end to start to figure out if first one (hence, highest Z order!) has been hit and should process the message. For example:
[SOURCE]
int CGUIElement::OnLMouseUp(UINT x, UINT y)
{
CGUIElement *IterElement = end();
set_ptr(IterElement);

while(IterElement !=NULL)
{
if(IterElement->OnLMouseUp(x, y)) // If one element finds use to the mouse_down event, do not let others to use it
return 1;
IterElement = prev();
}

if(GetType() !=GUI)
{
if(PointInRect(GetRect(), x, y))
{
GetGUI()->PostMessage(this, NULL, GUI_Message_AquireFocus);
return 1;
}
}

return 0;
}
[/SOURCE]


There you go, this code traverses children list from the end to start (but draws from start to end!). Each time you add a new child, it obviously goes to the end of the list.

Good luck.

Share this post


Link to post
Share on other sites
Sphet    631
I think most of the items here have been covered, but I'll throw my two cents in.

I've used a number of GUI systems across multiple operating systems and in games. Having written a couple of GUI systems myself, here's how I did it.


All widgets are derived from a base object. When I begin a new system, I always start with my base widget. A Widget always has a virtual Draw() function for drawing the widget. It also has a Dimensions variable which stores its rectangular dimension. It also has a HitTest function which returns true or false if a point is inside it's dimensions. The widget also has a list of child widgets - these are child widgets owned by the widget such as buttons and list boxes, etc.

I then have a widget manager. This widget manager is a list of Top Level Widgets, or Windows. This list is always sorted in a back to front order.

Every frame I iterate over the Widget Manager's list. I call Widget::Draw() on the widget, and in Widget::Draw() I iterate over all of Widget's children and call Widget::Draw() on them, which in turn would recursively call all the child widget's widgets, and on and on. In this way, a WindowWidget which contains a ButtonWidget would get drawn.

When doing hit testing, I traverse the Widget Manager's list in reverse order, from front to back. Like Widget::Draw, I call Widget::HitTest() on each item, and inside the Widget::HitTest function I call Widget::HitTest on all of the widget's children. I keep doing this until a widget returns true from the HitTest.

Using a generic Widget base class and storing the relationship as child Widgets makes the HitTest and Draw functionality consistent - there's no global code, all objects implement Hit Test and Drawing functionality local to the Widget type.

Does that make sense?

Anyways, I do have a demo somewhere and could probably find it in a couple of days if need be.

- S

Share this post


Link to post
Share on other sites
GroZZleR    820
The stack is working perfectly for me, thanks all. Just one more question regarding events and I should be all settled to implement the rest of my widgets.

Let's say you have an event like MouseOver. If you're over a button, I feel the window should still be handling it. However, if you're dragging a button (let's say) the window shouldn't receive the drag message. I don't see how to set that kind of thing up under the "search your children, if nothing happened, you perform it" style.

Here's what the BaseGuiWindow's ProcessEvent's looks like:

public void ProcessEvents(bool activeWindow, int mouseX, int mouseY)
{
bool handled = false;

for(int i = _children.Count - 1; i >= 0; --i)
{
IGuiControl control = (IGuiControl)_children[i];
handled = control.ProcessEvents(activeWindow, mouseX, mouseY);

if(handled)
break;
}

if(handled == false)
{
// Test for dragging, mouse over, whatever.
// If it's handled, set it to true.
}

return handled;
}



I must be missing something. I don't see how you could have an event be processed by the child and the parent.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this