Porting to current QuickGUI version

Zini

09-08-2009 08:32:25

I am in the process of porting a project from a (very) old version of QuickGUI to the current one (SVN) and it proves to be an even bigger nightmare than I expected.

I really don't want to sound ungratefully (after all you are providing us with a fully function GUI library for free). But are we done with major interface breaking changes? This may not all be so bad, if you have a small project using QuickGUI. But with a 120 000+ lines project and exactly zero porting documentation the situation is much more dramatic.
I guess it will take me almost a week to get everything running again and frankly spoken, I can't afford many more of these productivity drop-outs.

I hope this didn't sound to negative (I still like QuickGUI very much ;) ).

P.S.: Looks like I have lost two functions. They were present in the old version, but I can't find them in the current one. Both are Sheet-member functions: setFont, setGainFocusOnClick. Any hint, what happened to them?

Edit: and one more: What about GUIManager::injectTime?

Edit2: another one: Widget member function (I think): Widget::focus

Edit3: And now I am hitting a wall. Why is the destructor of LabelDesc protected?

You can't really expect your users to fiddle around with the default desc for every single widget, can you?

Modifying the default desc on a per-widget basis would be very bad. Scenario:

User creates label A, changes some desc attributes. User creates label B, changes some other desc attributes (reusing the changes from A).
Later user modifies the code for creating label A. And magically label B changes too, which is completely unintended and probably not easy to debug. Unless I am missing something, with the current setup you are inviting bugs into you code.

As far as I can see the only sane solution would be to either use the unmodified default desc (maybe good for sheets, but not for label) or make a local copy of the default desc and adjust the copy accordingly.

kungfoomasta

09-08-2009 19:52:29

Wow, the one time I don't copy my response into the copy/paste buffer, my internet connection goes out and I lose my response. Doh!

Basically I removed the InjectTime function because it wasn't really used. I created a TimerManager and Timer class to allow users to accomplish time-specific functionality. (The blinking TextCursor works with Timers) One thing I do plan to investigate at some point is whether I can create one global TextCursor class. Instead of each widget having its own cursor, I could just have one cursor, and have its position/skin changed on the fly. I don't think there are scenarios where there can be multiple blinking cursors at the same time.

Instead of Sheet::setFont, we have Root::getDefaultFontName/setDefaultFontName. We removed focus long ago, you were part of the voice in that change, because we only needed to track which widget was currently listening to the keyboard. For example, you should be able to click on a textbox, click on a button, type 'qwerty' in your keyboard, and see the text added to the textbox, which is not the last widget you clicked on. This is done by the following APIs:

Sheet::getKeyboardListener/setKeyboardListener
Widget::getConsumeKeyboardEvents/setConsumeKeyboardEvents

As for descs, there is a DescManager allowing creation and access of Descs. There is a 'resetToDefaults' method for every desc, allowing re-use and predictable behavior even if you make changes to the desc properties at some point. I've also added a ton of APIs allowing creation of widgets without using a desc at all. You don't have to use descs to create widgets if you don't want to, the main benefit of creating widgets with descs is that all the properties are supplied to the widget on construction of the widget, as opposed to creating a widget with all default properties, and then setting each of them after construction.

I haven't been doing too much work on QuickGUI lately, although I have been integrating it with my own personal project, and adding to the library here and there so I can accomplish certain effects on the UI. The next coolest feature I'd like to add to the library is support for animated skin elements, for example having buttons glow and fade with time as the mouse enters and leaves the button area. I still have to think of the best way to accomplish this, and I'm not sure when I will get around to it, probably as my project continues. Sorry for everybody waiting for the Editor.. I've chosen to focus more on my main project instead. I'll still continue to fix bugs and add feature here and there, just not as focused as I have been in the past.

Zini

09-08-2009 22:51:40

We removed focus long ago, you were part of the voice in that change, because we only needed to track which widget was currently listening to the keyboard

I remember now. That was very long ago.

Regarding injectTime: makes sense.

Regarding DescManager: I guess I can see were this is leading to, even though the createDesc function is grossly underdocumented (no worries, I'll look at the source). But I don't understand why the whole Desc business is so complicated. As far as I understand it a Desc is a simple struct with a default copy-constructor, that could easily be created on the stack.

Maybe I should shut up here or else I will give you ideas for another API breaking change.

kungfoomasta

10-08-2009 01:40:07

lol!

Its basically a struct, but there are functions that help it lend to serialization. Since descs by nature are mainly data oriented, I can define how that data is written to / read from disk.

Another complication I had is that each widget has to have its own desc; the desc used to create a widget is not the widget's underlying desc (where the widget stores its data). If that were the case, modifying the desc would modify values the widget dependent on... seems like a security risk to me.

I'd be interested in a better solution, but probably not interested in implementing it. The desc system seems adequate to me. (time will tell)

Zini

10-08-2009 08:29:29

I'd be interested in a better solution, but probably not interested in implementing it. The desc system seems adequate to me. (time will tell)

Well, you could use plain structs without the need to let any kind of manager handle them. The widget would take such a struct and make an internal copy.

Currently you have a very elaborate generic desc system, but at the desc manager you still have to manually handle each type, which makes the generic system kinda pointless.

I noticed that descs can have IDs and they can be accessed by their ID through the desc manager. This could be implemented by giving the struct a setId function, that would register it with the manager (the struct's destructor would then automatically de-register it).

Edit: More missing functions: How do you change the position of a widget? setPosition is gone and I can't find any replacement. And how do I disable borders for specific widgets?

Edit2: And since we are at it: There is a superfluous typedef in QuickGUIEventArgs.h:

class _QuickGUIExport EventArgs
{
public:
typedef enum Type
{
TYPE_DEFAULT = 0,
TYPE_WIDGET ,
TYPE_MOUSE ,
TYPE_KEY ,
TYPE_SCROLL ,
TYPE_TEXT
};


Please remove it (produces annoying warning messages when compiling)

Edit3: I have some problems with the TextArea widget. It scrolls, as it is supposed to do (when using the mouse wheel), but it does not have a scroll bar. Furthermore I can't make it scroll to the bottom of the text (scrollToBottom doesn't scroll fully down).

Edit4: How do I stop a window from being resizeable? I can't find any function for it.

Edit5: what happend to Image::setTexture? I have a RenderTexture, that I want to show in a window. QuickGUI::Image doesn't seem to be up to the task anymore.

Edit6: QuickGUI::ColourValue? What is wrong with the Ogre class? And you can't even convert between them.

Edit7: re: setDefaultFont: So there so no way anymore to set default-fonts on a per-sheet basis?

kungfoomasta

10-08-2009 21:17:07

1. setPosition is there, but protected by default. It should be set as public in other widget class definitions, which widget are you trying to position where it doesn't let you? I made a lot of functions protected in the Widget class, and made them public in certain derived classes. This prevents users from trying to set the position of a MenuItem, for example.

2. This is just about removing the word "typedef" right? I can do this

3. I will try to make time to test this out, both the missing scrollbar and the scrollToBottom API.

4.


/**
* Convenience methods allowing all sides to enable/disable resizing using the mouse cursor.
*/
void setResizeFromAllSides(bool resizable);
/**
* If set true, the bottom border of this widget can be used with the mouse cursor to resize the widget.
*/
void setResizeFromBottom(bool resizable);
/**
* If set true, the left border of this widget can be used with the mouse cursor to resize the widget.
*/
void setResizeFromLeft(bool resizable);
/**
* If set true, the right border of this widget can be used with the mouse cursor to resize the widget.
*/
void setResizeFromRight(bool resizable);
/**
* If set true, the top border of this widget can be used with the mouse cursor to resize the widget.
*/
void setResizeFromTop(bool resizable);


5. Odd, I must have renamed it. Sorry, its now:


/**
* Sets the name of the image file to be displayed. For example, name could could be
* qgui.image.png.
* NOTE: Image must be listed within an Ogre resource directory.
* NOTE: Setting the name to "" will result in only the background showing.
*/
void setImage(const Ogre::String& name);


Also, remember to tell the image to update every frame, can do it in the desc or after creation via this API:


/**
* Sets whether or not the Image will be redrawn every frame.
* NOTE: This also causes the Images parent window to be re-drawn, which can be costly.
* The main use for this is for RTT. (Render to Texture)
*/
void setUpdateEveryFrame(bool update);


6. I don't like it either, but I'm trying to eventually remove Ogre from all QuickGUI header files, so that a project that includes QuickGUI doesn't have to include Ogre as a dependency. Might sound silly at first, but in my project I have an engine X, and a Game Y, and Game Y has X as a dependency, but as a side effect Game Y also has Ogre as a dependency, because QuickGUI has include directives to Ogre classes. To get around this I have to duplicate these base Ogre classes, like Vector and ColourValue.

7. No, this functionality doesn't really exist anymore, although you could change the default font when you change sheets in non-QuickGUI code. I wanted to detach widgets from being so dependent on the Sheet it was connected to, especially since on creation, Widgets don't know what Sheet they belong to, they are detached, and then get attached after creation.

Zini

10-08-2009 22:54:40

1. setPosition is there, but protected by default. It should be set as public in other widget class definitions, which widget are you trying to position where it doesn't let you? I made a lot of functions protected in the Widget class, and made them public in certain derived classes. This prevents users from trying to set the position of a MenuItem, for example.

I had the problem with QuickGUI::Label (though I found a workaround for that). Currently QuickGUI::TextBox gives me problems.

6. I don't like it either, but I'm trying to eventually remove Ogre from all QuickGUI header files, so that a project that includes QuickGUI doesn't have to include Ogre as a dependency. Might sound silly at first, but in my project I have an engine X, and a Game Y, and Game Y has X as a dependency, but as a side effect Game Y also has Ogre as a dependency, because QuickGUI has include directives to Ogre classes. To get around this I have to duplicate these base Ogre classes, like Vector and ColourValue.

By removing all Ogre types from the QuickGUI-headers you don't remove the dependency on Ogre. You still need to link with Ogre. Unless you want to go down the same route as CEGUI (not advisable), you will always have a dependency on Ogre, so you don't gain anything from duplicating Ogre types. And it makes using QuickGUI a lot harder.


Guess I have enough hints to get going again (mostly). Thanks. Could you comment on the border issue too, please?

And how do I disable borders for specific widgets?

Currently the layout of every single window in my application is messed up and I suspect the borders are the cause, so I want to get rid of them.

kungfoomasta

10-08-2009 23:51:27

QuickGUI does depend on Ogre and always will, but if you use a lot of forward declaration and only include Ogre in .cpp files, other libraries that include QuickGUI will not need to include Ogre. (I believe this is the Pimpl idiom, or some related concept) Basically, I want to remove any #include "Ogre... lines from all QuickGUI headers, and that is sufficient. (to further clarify, I don't plan to make QuickGUI rendering agnostic)

For example my Engine has Havok as a dependency, and my Game has my engine as a dependency, yet the Game does not depend on Havok. I encapsulated that library pretty well. :)

For the border question, you can modify the Skin definition of the Window. Set the border values to 0. (qgui.skinTypes, search for "Window default", then set the border values to 0)

I'm not sure if this will fix your problem, since the borders are included in the size of your widgets. If you want a window to be 50 pixels wide, it will be 50 pixels wide, including borders. However the borders do affect the client dimensions of the widget. (Widget::getClientDimensions)

Also keep in mind that specifying a border of 0 will not cause borders to disappear. Basically you have 1 texture, and you tell QuickGUI how much of the edges are for the border, how much is the main background. With 0 border size, the entire texture is used as the main background. Also, with 0 border size, you obviously cannot resize widgets.

Zini

11-08-2009 08:28:08

I'm not sure if this will fix your problem, since the borders are included in the size of your widgets. If you want a window to be 50 pixels wide, it will be 50 pixels wide, including borders. However the borders do affect the client dimensions of the widget. (Widget::getClientDimensions)

Not sure about it any more either. I thought my widgets would be too large and therefore would pop up scrollbars, that I don't want. But it could be the other way around. Maybe the unwanted scrollbars pop up for some reason and take away space, so the widgets don't fit anymore. I vaguely remember that we had a problem like this before with the old version too. Will have to investigate ...

Edit: That indeed seems to be the problem (at least with the first two windows, that I got working again so far). I think there was a function to disable/enable the use of scrollbars, but I can't find it any more.

For example my Engine has Havok as a dependency, and my Game has my engine as a dependency, yet the Game does not depend on Havok. I encapsulated that library pretty well.

Sorry, but the dependency relation is transitive. If the game depends on the engine and the engine depends on Havok, then the game depends on Havok. There is no way around this.

It looks like you are trying to isolate your client (the game code) from the libraries and frameworks you are using. Pimpl idiom and forward declaration can help to reduce coupling and therefore compile time. Furthermore these techniques help to reduce re-compiling of client code, when the code the client is depending on changes. Personally I think this is a bit overkill in your case. But okay. Your code, your design decisions.

However I utterly fail at seeing the point in creating custom low level types as a replacement for already existing low level types. All you do is to move them into a different namespace and to change the name of the header to include. This does not improve anything at all.

kungfoomasta

11-08-2009 18:58:25

My Engine Gaia has the following libs (.so files in linux, I believe) that it links against:

OgreMain_d.lib
OgreTerrain_d.lib
OIS_d.lib
QuickGUI_d.lib
enet_d.lib
OgreOggSound_d.lib
libboost_thread-vc90-mt-gd-1_38.lib
hkBase.lib
hkSerialize.lib
hkSceneData.lib
hkInternal.lib
hkGeometryUtilities.lib
hkVisualize.lib
hkCompat.lib
hkpCollide.lib
hkpConstraintSolver.lib
hkpDynamics.lib
hkpInternal.lib
hkpUtilities.lib
hkpVehicle.lib
hksCommon.lib


(anything beginning with 'hk' is Havok)

My Game RotM has the following libs it links against:

OgreMain_d.lib
OIS_d.lib
Gaia_d.lib
QuickGUI_d.lib
libboost_thread-vc90-mt-gd-1_38.lib
enet_d.lib


And yet my game can create physics shapes via Gaia, and set the gravity, etc. etc. without even knowing what Havok is. In fact, if my design was better, the Game should only require Gaia_d.lib, and not OIS, Boost, ENet, etc.

About scrollbars, you can disable them, it must be done before creation of the widget.

myContainerDesc->containerwidget_supportScrollBars = false;

You're right that all I'm doing is basically mirroring Ogre classes and moving them into QuickGUI namespace. I don't see any better way to remove Ogre includes from QuickGUI headers. I guess if I just made a QuickGUI::ColorValue from the start there wouldn't be any problems. :P