setUseBorders() question

Jekteir

25-03-2008 17:48:50

Will setUseBorders(false) on my default GUI sheet (which I'm using) mean that whenever I create a new widget within that sheet, it won't start with borders? That would be ideal. I looked for this on manager also, but it wasn't there. I don't want QuickGUI creating and then destroying borders for all my widgets when I have no intention of using 'em...


Jek

kungfoomasta

25-03-2008 18:49:47

Its not an inheritted property, its specific to the widget that makes the call. I should make this function protected in the Sheet, because its a widget that has no borders.

In the current system, widgets will detect if border textures are available for them, and create them available. If you call setUseBorders(false), it will not create borders even if they are available for creation.

Jekteir

25-03-2008 19:27:56

But to call setUseBorders(false) the widget already has to be created, right (because setUseBorders is a member function of that widget)? Which means, presumably, that the borders will already have been created (along with the widget)? So you'll end up creating then destroying borders. Is this right?

I could always remove the border textures as you suggest (though I would have expected the code to error if it wanted to set border textures and there were none), but it'd be nice to keep them in case I do find a use for them in special circumstances.

Jek

kungfoomasta

25-03-2008 19:43:25

Even if you call setUseBorders(true), borders are only used if textures for them are available. In this way you can switch a button from 1 skin to another, and borders are handled automatically.

You do bring up a really good point, regarding creating borders then deleting them. What is a good way to specify the properties of a widget before its created, or before its built? Maybe there should be a WidgetParameters class, that can be inheritted from, that is required in a widgets constructor.

Jekteir

25-03-2008 19:44:59

I think the logical thing is for widgets to inherit their borders settings either from their parent widget or their parent sheet (which is what I thought was happening with the setUseBorders() function for Sheet).

Jek

kungfoomasta

25-03-2008 19:47:18

That doesn't sound very logical to me..

Are you saying a window with borders should create labels with borders by default? I don't see why this should be the case.

Jekteir

25-03-2008 19:57:47

OK, how about this:

In addition to the local (per instance of class, i.e. object) mUseBorders bool, every class of widget has a static ClassUsesBorders bool. In the .cpp of each class this is initialised to the value of the DefaultSheet's mUseBorders bool value.

Then, whenever a widget is created of that class, check the static value of that class bool for the initial creation or non-creation of borders, and use that for the value of mUseBorders on the local object (and do/don't create the borders at widget creation accordingly).

The programmer can still change the borders on the individual object post-creation whenever he wants, if necessary, without changing the static default for all widgets of that type. Furthermore, if he wants to he can also change the static default for that widget class's border settings, if you provide either a static accessor function to the static var, or make the static var public.

One thing I don't know is whether or not the setting on defaultSheet will be chosen by the programmer before all the initialising of widget static vars takes place (which has to happen in file scope). One thing you could do is choose a default (on/off) which is in the actual code, so all widget classes take their starting bool value from that. Then allow the programmer to make one call with Sheet, e.g. QuickGUI::Sheet::setDefaultUseBorders(false);, and that function goes through all the widget classes and does QuickGUI::WidgetClass::setDefaultUseBorders(boolgiven);. That way a coder can create QuickGUI's root, get a pointer to the default sheet, and then set all widgets to not have borders by default with a single call.

How bout that?

Jek

kungfoomasta

25-03-2008 22:16:41

This could work, but its limited only to borders. What if there are other properties we want to set before creation of the widget? For example, the skin is currently an inheritted property from the parent. But if we wanted to optimize this, we would allow the user to specify the name of the skin in the constructor. Another problem I see is that not all instances should be created with the same properties. The user would have to call the static function to make sure the created widget does or does not have borders on creation.

I'm actually a fan of the WidgetParameters class, since it allows customization on a per widget basis. You can also reuse the same WidgetParameters, if you want to create a lot of similar widgets. You can derive this into ButtonParameters, WindowParameters, JekteirsMagicalWidgetParameters, etc. Its easy to organize, extend, and update. Sounds like a good idea, no? :)

Jekteir

25-03-2008 22:40:04

So how would this work? Would you have one WidgetParameters class, an instance of which could be passed to any widget constructor? I'm assuming something like:

QuickGUI::Window *helpWindow = guiMgr->createWindow("helpWindow",QuickGUI::WidgetParams(0,false,"someskin"),[dimensions here],[etc]);

That would be OK, but it would be a pain to create a different type of widget parameters object for each type of widget you make. It's also a pain to fill in all the details for every widget you create.

It also means that there's no 'default' choice for any of these settings - you have to say explicitly for every widget whether you want it to have borders or not on creation. It's true that 'not all instances should be created with the same properties', but that doesn't mean that it might not be easier to code in the exceptions to the rule (i.e. put borders when you occasionally need them, but have your default as no-borders) rather than type it in every time.

If there are other settings than borders (e.g. default skin name), the set of static bools/data could always be extended with more settings.


OK... I had a good idea. How about a kind of static-y version of WidgetParameters:

i) There is a standard WidgetParameters class with params most widget constructors are interested in, e.g. borders. This can be created and passed as demonstrated above.

ii) We set up another WidgetParameters object, but one whose settings can be altered, and which remain stable after alteration, whether due to being static or global or something. You should be able to change the default for each individual setting, e.g. set hasBorders = false permanently for that object (unless you later decide to change it again).

iii) More specific objects which inherit and extend WidgetParameters can be created for particular widgets, so that your constructor calls can be more detailed if you want.

iv) If you don't create and pass a new WidgetParameters object (or a specific derivation thereof) to a constructor, the default argument is actually the global, static version of WidgetParameters with the default settings that you already chose.

I think this is possible in code, with some careful architecting, and it would allow us to have our cake and eat it too.

Jek

Jekteir

25-03-2008 22:42:22

And to be clear, when I say 'you should be able to set default', I don't mean in QuickGUI's code using = false etc in a constructor. I mean, set a default in the code of the user of QuickGUI, a default which can change during runtime.

kungfoomasta

25-03-2008 23:09:30

Well the Parameters will all have default values to begin with.

Here is an example of a default Button:

...createButton(ButtonParameters());

Or

ButtonParameters b;
...createButton(b);


If you wanted to create a button with skin "xyz" by default:

ButtonParameters xyzButtonParams;
xyzButtonParams.skinSet = "xyz";
...createButton(xyzButtonParams);
...
...createButton(xyzButtonParams);
...createButton(qguiButtonParams);


You only need to tweak the params if you don't want the default values, and if you need to do it often, you can just store the params and reuse them to create those specific buttons.

If anybody has any issues with this approach speak now, I like this approach, its similar to the PhysX method to creating objects. (they use a "description" class and not a "parameters" class to create objects)

Jekteir

25-03-2008 23:16:03

I see, so you could hold your own parameters instance (globally or otherwise as you saw fit), tweak the settings, and then repeat applying it to the objects built. I guess that's almost as tidy as completely default (and easier to code and understand).

I would ask, though, that the maximum possible settings be put in the base class rather than inheriting down. For instance, if there are some classes which don't have borders, but most do, put usesBorders in the base parameters class rather than in each of the derived ones that deal with the different widget classes. The classes that don't use usesBorders won't look at that setting, and we won't have to deal with making different params objects except where necessary for our own purposes (e.g. if we wanted windows borders but no other ones). We can pass a reference to the params object so that a big params object doesn't slow down the function calls.

In fact, as far as I'm concerned we could keep ALL the settings in the base class, and just use the derived classes when we want different settings for different classes. That way, someone who wants the same settings for all widgets can just use the base class for everything.

Jek

kungfoomasta

25-03-2008 23:43:36

In fact, as far as I'm concerned we could keep ALL the settings in the base class, and just use the derived classes when we want different settings for different classes. That way, someone who wants the same settings for all widgets can just use the base class for everything.

Don't you think thats a little bit too lazy for the user? :lol:

I see what you're saying, but one big class doesn't sound very organized. If you want to create a TextBox, you need to use a TextBoxParams (or TextBoxParameters) object. I was thinking of trying to tie the Params object into serialization of widgets, and having one big class would make this harder to accomplish.

I don't think it would be asking too much to have to create parameters and use it to create widgets, would it?

Also, thanks a lot for all your input, I haven't had any users help me brainstorm a big design enhancement in a long time. :)

Jekteir

25-03-2008 23:52:40

But it would be a pain in the ass to handle 10 or 20 different parameters types for all the different widgets I might create. I really think this sort of thing is better handled as much on QuickGUI's side as possible. I mean, now my program's handling a list of QuickGUI objects just to pass to other QuickGUI functions for when I want a GUI item :P

I'm not sure what you mean by 'tie the Params object into serialization of widgets'. Can you elaborate?

How about: for the constructors that use the parameters objects, have them accept a WidgetsParameters (which the caller can optionally pass a derived class to, e.g. TextBoxParameters). Then have the constructor first handle all the params common to all WidgetsParameters objects. Then it can check to see if the passed object is actually e.g. a TextBoxParameters, and if it is, it does any specific settings relating to textboxes; otherwise, it sets them to defaults.

Jek

kungfoomasta

26-03-2008 00:23:28

I mean, now my program's handling a list of QuickGUI objects just to pass to other QuickGUI functions for when I want a GUI item

This is only if you want a widget with values different than the default ones.

Here is a better comparison:

current method:

...createButton();
myButton->setDimensions(...);
myButton->setUseBorders(...);
myButton->setText(...);
etc.


new method:

ButtonDesc b;
b.rect = ...
b.useBorders = ...
b.text = ...
...createButton(b);
etc.


You will still be able to use the old methods in addition to the new, only now it's more efficient, in the situation where you don't want to set the widget to default properties and then change it after creation.

I'm not sure what you mean by 'tie the Params object into serialization of widgets'. Can you elaborate?

Imagine Widgets store a params, or desc object (whatever sounds better, just a term). The params object can be serialized to file, or read in from file, and used to create the widget.

Jekteir

26-03-2008 12:40:15

I mean, now my program's handling a list of QuickGUI objects just to pass to other QuickGUI functions for when I want a GUI item

This is only if you want a widget with values different than the default ones.


I assume by default you mean 'hardcoded into QuickGUI's C++ default variable values'. This type of default seems very unfriendly to me. For example, I think that the default timing for the delete/backspace/arrowmovement/cursorflashing period is too quick, but it's hardcoded in instead of easily changeable, so I'd have to modify my copy of your source (quite possibly every time I update from SVN) to change it...

And, if (as I believe) you're saying that non-hardcoded 'defaults' can be set up by reusing params objects of a particular class type as a 'default' for that type -- that's fine, but it is what I was saying -- keeping track of a big tall list of params objects to act as your defaults, instead of having QuickGUI keep track of default settings for widgets creation.

Imagine Widgets store a params, or desc object (whatever sounds better, just a term). The params object can be serialized to file, or read in from file, and used to create the widget.

If you want to serialise a params to file, and that will create the widget, that means having the widget's name in the params file for serialisation. Remember that if params are to act as defaults for more than one widget we still want to set the widget name in the contructor, not in params. Otherwise we will have have to make at least one change to a params object every time we create a new widget, resulting in 2 calls, not one.

Jek

kungfoomasta

26-03-2008 19:24:20

I assume by default you mean 'hardcoded into QuickGUI's C++ default variable values'. This type of default seems very unfriendly to me.

This is how it is done even now. All widgets have a default size, for example.

I think that the default timing for the delete/backspace/arrowmovement/cursorflashing period is too quick

You can easily submit a patch, or bring forward the issue and I can add it in, when I have time.

keeping track of a big tall list of params objects to act as your defaults, instead of having QuickGUI keep track of default settings for widgets creation.

Again, not all labels will be the same. When you create a params object for widgets it will be populated with acceptable default values. If you want to customize the widget you can do so in the params object, or after its created. For all purposes you could simply use "LabelParams()" everywhere and nothing will have changed. Its impossible to label it as a big burden, its 10-20 extra characters you have to type for each constructor.

You are able to set the name of a widget after its created, so there is no issue here.

[edit]
I will look into making each widget class have a static params object, so you can access and use that if you want.
[/edit]

Jekteir

26-03-2008 19:33:21

(at the bottom of this post I note that I just saw your edit-- so read on but it sounds like you've agreed now anyway!)

I assume by default you mean 'hardcoded into QuickGUI's C++ default variable values'. This type of default seems very unfriendly to me.

This is how it is done even now. All widgets have a default size, for example.


I know, and it sucks that (for instance!) borders are set to 'on' by default, hard-coded. That's how this whole discussion came up!

I think that the default timing for the delete/backspace/arrowmovement/cursorflashing period is too quick

You can easily submit a patch, or bring forward the issue and I can add it in, when I have time.


My point isn't to complain that the hardcoded times should be changed in QuickGUI's source -- it's that the user should have the ability to change the defaults programmatically for their use of QuickGUI.

When you create a params object for widgets it will be populated with acceptable default values. If you want to customize the widget you can do so in the params object, or after its created.

That's great, but what I'm saying is that different users will consider different default values 'acceptable', so it would be nice to be able to change the default values rather than taking the defaults into your own copy of the object and changing them there, and then maintaining your own 'default' params copies for use in your program. If there's a way to change what QuickGUI considers a good default value for a particular setting in a particular params object (e.g. set the default LabelParams object's 'useBorders' setting to no) -- so that you don't have to take a copy and modify it and hold on to it, but rather can grab a new copy (with the defaults you selected) yourself whenever you want, that'd be ideal.

OK, gonna stop now, cause I just saw your edit.

kungfoomasta

26-03-2008 19:53:05

Yah, I agree that allowing users to set the defaults would be a good idea, so I'll look into doing that. Setting the borders off by default would be a total of 3 lines of code :P

LabelParams p;
p.borders = false;
...createLabel(p);


On the actual implementation side of things:

I started using this approach last night, but I have named it to desc, instead of params. Also, params are not limited to widgets, everything you create will have a desc object for it. GUIManagerDesc, MouseCursorDesc, etc.

I have one question regarding usability with the Desc objects. Which way seems to have better design?

1) GUIManager(const GUIManagerDesc& d);

OR

2) GUIManager(const GUIManagerDesc& d1, const MouseCursorDesc& d2);

I initially wrote out constructor 2, but then I switched to 1. What this means is that the GUIManagerDesc includes a MouseCursorDesc object in it. Whats your opinion on this?

sample workflow:

GUIManagerDesc d;
d.mouseCursorDesc = ...;


I'm also going heavy on exceptions, this new version should give more feedback to the user if something is wrong.

Jekteir

26-03-2008 20:22:23

I'm not sure about this 'desc for the GUIManager' thing. I understand that it would be helpful for serialising, but GUIManager isn't actually a _widget_. You're using a tool intended to describe a certain type of thing to describe something quite different. This becomes obvious when you consider that other types of descs shouldn't need to include more descs (AFAIK).

Maybe GUIManagers should be handled in some other way. I'm also not quite sure why we need more than one GUIManager when we can have more than one sheet. What's the difference?

Jek

Jekteir

26-03-2008 20:38:44

Yah, I agree that allowing users to set the defaults would be a good idea, so I'll look into doing that. Setting the borders off by default would be a total of 3 lines of code :)


LabelParams p;
p.borders = false;
...createLabel(p);



Yeah, but if p.borders isn't static, then that 'default' is only good for LabelParams p. E.g.:


LabelParams p;
p.borders = false;
createLabel(p);

...

LabelParams q; // q will still have borders == true if that's set in LabelParams class


If, on the other hand, 'borders' is described as 'static' in class LabelParams, you can't access it as a member of an instance, i.e. p.statBorders is invalid code. You can only access it as a single class-wide value that doesn't belong in any instance, LabelParams::statBorders. HOWEVER, what you could do is, in the constructor of LabelParams, go:

LabelParams::LabelParams() : mBorders(LabelParams::statBorders) {}
, and so on for other defaults. That way, the user can change the default for all LabelParams with LabelParams::statBorders = true;, and when they do LabelParams b; it will have the default they selected on it.

Jek

kungfoomasta

26-03-2008 20:39:14

Well it started with being used for widgets, but I couldn't find any reason why it should stop there. Anything that can be created and customized can be created with a desc structure of some sort. Its more of a uniform design approach used all across QuickGUI. All constructors will only have 1 parameter. I think after you get used to it you'll find it to be a good design. In order to create an object, you need an object description. As long as there are some basic comments for each member within the desc object, it will be fairly straightforward for users.

You need a GUIManager for each viewport. There were problems with multiple viewports on the same render window, but GUIManagers on separate render windows work just fine.