Jump to content
  • Advertisement
Sign in to follow this  
Murdocki

UML design gui

This topic is 2996 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,

i recently started working on an UML design of a to be created gui system. While doing this i used a widget system which i know to be standard. However while modelling this it sort of hit me there are too many things that dont make sense in this design. Here's the design so we know what we're talking about. The line up goes to the gui, the lines down come from the WidgetFactory, i left these parts out because they are irrelevant to the problems. If my understanding of the standard widget system is wrong which lead to this design to be incorrect please enlighten me.

So as i said i found many things to not make sense. The first of them all would be why should EVERY widget has children? This doesn't make sense because if i look at a button (which i think everyone agrees it "is a" widget) i dont see any use for children, the same goes for labels, input fields and other not yet modelled widgets like check boxes and radio buttons. For most of these widgets a certain use for children could be thought of however these uses aren't the same (eg a button might use children to see required fields whereas a panel uses children as container items). This line of thinking brought me to the question, what is the definition for a child? My thought would be it's an object owned by a parent( the panel usage described earlier). This definition of a child drives out the children from the Widget class to for example the Panel class.

Now i also found the types of widgets to be very different, for example a button/checkbox is very different from a panel/tabgroup. This lead me to the question, what is a widget? Not being able to answer this with the knowledge of other gui systems i made up this (for me new) definition: "A widget is an object used for representation of one gui element the user can interact with". Using this definition makes me think why should panels (and tabgroups for that matter) be widgets. With no answer to that i figured a panel needs to be out of the widget system and only needs to be used on a higher level as a container for widgets.

Combining the removal of children from the Widget class and the newly made up definition it kinda hit me that Widget now only is data, how's that for a super class. Thinking about the data approach of a Widget i remembered all the gui systems i used had an "active" and a "visible" flag. The visible flag made sense as in you should be able to hide every widget, the active flag however seems to me it's annother one of those parameters with multiple functions. For a textfield it sounds logic to have it be active if the user is typing in it, a button might be active if it's clickable / or maybe when the mouse hovers over it, a checkfield could be active if it's checked (but where do we leave the clickable part like a button?). So yeah again multiple uses of one variable. Up to this point (even on other systems not gui like) i've never used inheritance to inherit data, only functionality so i'm wondering if there are any standard thoughts about this, eg should it be used, in which cases may it be used, how should the data be described in the super class ("this variable defines if the widget is active" doesnt seem enough to me).

Anyway to wind this up before it becomes too long here are my questions:
1: Is my initial interpretation of "the standard"(as in most commonly used) widget system correct?
2: Why do i find children inside of the Widget class rather than in come container?
3: What are children? Should i allow multiple uses for these?
4: What is the standard definition of a widget and how is this description true for all the implementations of widget i mentioned.
5: How is the "active" flag used for widgets, and what should i do if i can find multiple uses for this in subclasses?
6: Inheritance for data:
6a: should it be used
6b: if so in which cases
6c: how do i describe the data in the super class?
Now maybe the most important question, i found this "standard" implementation of the widget system actually works okay on the short run (i made it allready):
7: Should i hammer this stuff out or should i just fully implement it and pray it keeps working?

So thanks in advance for answers to any questions raised here,
Murdocki

Share this post


Link to post
Share on other sites
Advertisement
Murdocki,

GUI requirements for any given system can always be different from each other and sometimes are very much tuned because of this for their special cases and needs. One of the reasons for the general case system that everything is a "widget" is to have a unified structure and data flow path that does not need endless special cases as you build onto it. Mileage may very but it gives you a good starting point most of the time.

As for wondering about children it is a way of grouping and directing logic and or having them move when you move a parent. Yes there can be other ways to do this but the idea basically stays the same. For example take a number input field that also has up/down arrows to inc or dec the value. Technically this is one "widget" but is made up of 3 subwidgets. So what you have is a top-level widget that encompasses the 3 sub ones for a total of 4. If you "disable" the top level widget all lower level widgets can stop taking input. Or you can have logic that has a min and max value the number field can have and will disable the inc or dec arrows as needed but the widget is still active as a whole.

But again like I said each GUI is usually based on what it is needed for. Like one of the systems I have done before worked better with the children being put into a subclass that still extended the widget.

Here is just a "small" sample of GUI parts that I have developed and used before in a released project:


class GuiWidget
{
protected:

Rect2d mRect;
Rect2d mClickRect;
bool mIsVisible;
bool mIsEnabled;
bool mWantsFocus;
bool mHasFocus;
bool mHasMouse;
string mToolTip;
Texture mToolPic;

public:

GuiWidget(void);
virtual ~GuiWidget(void);

void SetRect(Rect2d& rect);
Rect2d& GetRect(void);
void SetClickRect(Rect2d& rect);
Rect2d& GetClickRect(void);
bool IsInside(Point2d& point);
void SetVisible(bool visible);
bool IsVisible(void);
void SetEnabled(bool enabled);
bool IsEnabled(void);
bool WantsFocus(void);
void SetFocus(bool focus);
bool HasFocus(void);
void SetMouse(bool mouse);
bool HasMouse(void);
void SetToolTip(const string& tip);
const string& GetToolTip(void);
void SetToolPic(Texture& pic);
Texture& GetToolPic(void);
virtual bool IsGroup(void);
virtual void Render(void);
virtual bool OnInputEvent(InputEvent& event);
};

//----------

class GuiGroup : public GuiWidget
{
protected:

typedef std::list<GuiWidget*> WidgetList;
typedef WidgetList::iterator WidgetItr;
typedef WidgetList::reverse_iterator WidgetRevItr;

WidgetList mWidgets;

public:

GuiGroup(void);
virtual ~GuiGroup(void);

void AddWidget(GuiWidget* widget);
void RemoveWidget(GuiWidget* widget);
GuiWidget* GetWidgetAt(Point2d& point);
bool IsGroup(void);
void Render(void);
};

//----------

class GuiButton : public GuiWidget
{
protected:

typedef Callback_<GuiButton> ButtonCallback;

GuiBrush mDefaultBrush;
GuiBrush mPressedBrush;
GuiBrush mHighlightBrush;
GuiBrush mDisabledBrush;
ButtonCallback mButtonCallback;
bool mIsPressed;

public:

GuiButton(void);
virtual ~GuiButton(void);

void SetDefaultBrush(const string& brush);
void SetPressedBrush(const string& brush);
void SetHighlightBrush(const string& brush);
void SetDisabledBrush(const string& brush);
void SetButtonCallback(ButtonCallback callback);
void SetPressed(bool pressed);
bool IsPressed(void);
void Render(void);
bool OnInputEvent(InputEvent& event);
};

//----------

class GuiSlider : public GuiWidget
{
protected:

typedef Callback_<GuiSlider> SliderCallback;

GuiBrush mThumbBrush;
Rect2d mThumbRect;
Rect2d mThumbClickRect;
float mValue;
float mMinValue;
float mMaxValue;
float* mExternValue;
bool mIsDragging;
SliderCallback mSliderCallback;

public:

GuiSlider(void);
~GuiSlider(void);

void SetThumbBrush(const string& brush);
void SetThumbRect(Rect2d& rect);
void SetThumbClickRect(Rect2d& rect);

void SetMinValue(float min);
void SetMaxValue(float max);
void SetValue(float value);
void SetExternValue(float* value);
void SetSliderCallback(SliderCallback callback);

void Render(void);
bool OnInputEvent(InputEvent& event);
};

//----------

class GuiListBox;

class GuiListBoxItem : public GuiWidget
{
protected:

friend class GuiListBox;

GuiListBox* mListBox;
string mLabel;
void* mData;

public:

GuiListBoxItem(void);
~GuiListBoxItem(void);

void Render(void);
bool OnInputEvent(InputEvent& event);
};

class GuiListBoxView : public GuiGroup
{
protected:

friend class GuiListBox;

GuiListBox* mListBox;

public:

GuiListBoxView(void);
~GuiListBoxView(void);

void Render(void);
bool OnInputEvent(InputEvent& event);
};

class GuiListBox : public GuiWidget
{
protected:

friend class GuiListBoxItem;
friend class GuiListBoxView;

typedef Callback_<GuiListBox> ListBoxCallback;

typedef std::vector<GuiListBoxItem*> ListBoxItemList;

ListBoxItemList mItems;
GuiListBoxItem* mActiveItem;
GuiListBoxView mBoxView;
FTGLPixmapFont* mFont;
ListBoxCallback mListBoxCallback;
bool mIsOpen;

public:

GuiListBox(void);
~GuiListBox(void);

void AddItem(const string& label, void* data = 0);
void* GetData(void);
void SetListBoxCallback(ListBoxCallback callback);

void Render(void);
bool OnInputEvent(InputEvent& event);
};

//----------

struct GuiToolTip
{
GuiWidget* mOverWidget;
Point2d mCursorPos;
bool mVisible;
DWORD mTime;

GuiToolTip(void)
{
mOverWidget = NULL;
mVisible = false;
mTime = 0;
}
};

class GuiManager
{
private:

typedef std::vector<GuiResource> ResourceList;
typedef ResourceList::iterator ResourceItr;

static GuiManager* mSingleton;
ResourceList mResources;
GuiGroup mWidgets;
GuiWidget* mFocusWidget;
GuiWidget* mOverWidget;
GuiWidget* mDownWidget;
Point2d mCursorPos;
GuiBrush mCursorBrush;
GuiBrush mCursorDeleteBrush;
bool mIsCursorVisible;
bool mIsMouseDragging;
GuiToolTip mToolTip;
FTGLPixmapFont* mFont;

public:

GuiManager(void);
~GuiManager(void);

static GuiManager* Instance(void);
bool Initialize(void);
void Shutdown(void);
void Render(void);
void Render2(bool del = false);
void AddResource(const string& name, const string& texture, Point2dF& uvpos, Size2dF& uvsize);
void ValidateResource(GuiResource* resource);
GuiResource* FindResource(const string& name);
void DrawBrush(Rect2d& rect, GuiBrush& brush);
void DrawTexture(Rect2d& rect, Texture& texture);
void DrawTexture(Rect2d& rect, Texture& texture, Rect2dF& uvrect);
void AddWidget(GuiWidget* widget);
void RemoveWidget(GuiWidget* widget);
void SetFocusWidget(GuiWidget* widget);
void SetCursorPosition(Point2d& point);
Point2d GetCursorPosition(void);
void SetCursorVisible(bool visible);
bool IsCursorVisible(void);
bool OnInputEvent(InputEvent& event);
};






EDIT: realized this might help make more sense of the style callbacks I used:


/*
Deep Dark Voodoo Black Magic:

All class methods must be thiscall with no thiscallvararg or virtuals allowed.
All functions must take "TYPE*" as their input and must return nothing "void".
The callback's primary use is for user interface state change notifications.
*/


template <class TYPE> class Callback_
{
public:

typedef void (*Proc)(TYPE*);

private:

void* mObject;
void* mMethod;
Proc mProc;

public:

Callback_(void)
{
mObject = NULL;
mMethod = NULL;
mProc = NULL;
}

Callback_(void* object, void* method)
{
mObject = object;
mMethod = method;
mProc = NULL;
}

Callback_(Proc proc)
{
mObject = NULL;
mMethod = NULL;
mProc = proc;
}

void operator()(TYPE* data)
{
if(mObject && mMethod)
{
void* object = mObject;
void* method = mMethod;

_asm
{
mov ecx, [object];
push data;
call [method];
}
}
else if(mProc)
{
mProc(data);
}
}
};

//----------

template <class TYPE> Callback_<TYPE> Callback(void (*proc)(TYPE*))
{
return Callback_<TYPE>(proc);
}

inline void* ToVoidStar(void* dummy, ...)
{
va_list list;
void* value;

va_start(list, dummy);
value = va_arg(list, void*);
va_end(list);

return value;
}

template <class TYPE, class MISC> Callback_<TYPE> Callback(MISC* object, void (MISC::*method)(TYPE*))
{
void* methodptr = ToVoidStar(0, method);

return Callback_<TYPE>(object, methodptr);
}





[Edited by - Corman on July 4, 2010 1:53:49 PM]

Share this post


Link to post
Share on other sites
Hi corman,

thanks for your reply but here's some questions about it :)

you say a numeric input field would be a root widget with 3 children, do you define these buttons and the input field as children or are they simply member variables? seeing you used the children in your example as a composition i'm still wondering how this fits into a Panel, they aren't actually "part of" it there. Also i'm wondering where you handle the logic (callbacks) of for example your increment and decrement buttons, would that be inside the numeric input field so it can apply it's restrictions?

anyway, i do like what you've done in the example code with the GuiGroup. Makes you be able to choose which to inherit when you continue adding to the system :)

Share this post


Link to post
Share on other sites
You can always take a look at Qt's implementation. While I don't agree with every decision they made, I think their system is pretty solid.

1: I think their definition of a widget is the following: "A widget is an object used for representation of one or more gui elements".
If you start handling widgets that can be acted with in a different manner than widgets that cannot be interacted with, then you will have cluttered code.

2: GUI systems often use some sort of message system. If each widget can have children then it's really easy to disable or hide an entire hierarchy: The disabled widget simply stops passing messages to children.

3: I would say that children are widgets that sit INSIDE a parent. But you should also think about the programmer. QListView subclasses QFrame, although the view is inside the frame. I certainly don't want to create a QFrame for every QListView that requires a frame.

4: I don't think there is a standard. Widgets are what you require them to be.

5: I've only written a basic GUI system for my engine, however I didn't require an active flag. There is an active widget that receives special events (mostly input related). If that widget doesn't want to handle them, the parent may do so (etc...).

6: In my implementation I used the following approach: What are the common requirements for a widgetI?
-It can be painted
-It can be hidden
-It can receive input
-It can be clicked
-It can have children
-It can have a parent
-It has a size
-It has a position
Basically I thought about all those "can's" and then weighted how often they occur and how relevant they are. Ofcourse a widget can have a frame, but most widgets don't in my case & opinion.
Everything that was still on the list went straight into my widget class.

7: This also depends. Do YOU want to hammer it down? What do you enjoy? Is this is a personal project? Do you want a perfect system (that may never be finished :D) or a working system?

Share this post


Link to post
Share on other sites
allright thanks for your help guys, i have made up my mind so i can work out the design and then implement :)

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!