Project Idea for a "good GUI lib for Windows"
axilmar

Is anyone interested in participating in an open source project for a GUI library? I am motivated by the lack of a simple GUI library, as it is mentioned in this thread. I've seen this request for a simple gui library before.

The desired features are:

1) native look and feel; it will use native Win32 widgets underneath and Qt or GTK on Linux.

2) cross-platform at source-code level.

3) simple; the APIs would be uniform and as less complicated as possible.

4) uses established C++ principles, i.e. the STL, boost::shared_ptr, boost::bind, boost::functional, virtual methods etc.; no message maps or macros

5) it will only be a GUI library; there would be no networking, threads, database, etc stuff.

6) uses unicode through out

First version will have a few widgets: label, image, button, check box, radio button, scroll bar, text box, combo box, list box, window, menu, menu item, file dialogs.

Future versions may have more complicated functionality, i.e. Multiple Document Interface, spin box, tree view, table view, drawing canvas, date and time picker, tabs, font and palette dialogs, printer and font support, open gl, drag and drop etc.

These things are difficult and time-consuming to code, but once a basic setup is there, I think it is enjoyable to code components.

So, is anyone interested?

Stagera

wxWidgets?

nonnus29
SmartGuy said:

wxWidgets?

Reading comprehension needs some work I think...

bamccaig

Sounds like fun... :)

Arthur Kalliokoski

I've been thinking about dredging up my old brute-force assembler code for 486 which works just fine... I need it for my game. Never did have a drop-down list though, I'd want that for the available screen modes and saved games.

Neil Walker

other than c++, it ticks all the boxes. You didn't mention it had to be a thick client ;)

http://gwt.google.com/samples/KitchenSink/KitchenSink.html

Andrei Ellman

I've made a C GUI library that I use for my projects. It started off as a buttoned-menu manager but I decided to turn it into a full blown GUI. It's based on Allegro and uses no other APIs. But it currently lacks several important widgets (most notably text-entry widgets) and could do with being tidied up.

AE.

Kitty Cat
Quote:

5) it will only be a GUI library; there would be no networking, threads, database, etc stuff.

Wouldn't something like threads be a great benefit to a GUI lib? Especially where multiple windows are concerned? You'd end up having to wrap the native thread lib (Windows threads or pthreads) into a coherent interface anyway, so what's the difference if it's exposed or not? The app may even have to use it to gaurantee safety, so it would likely have to be exposed..

Plus there's issues between different STL implementations.. some are quite broken, or the spec is just so ill-defined in some places you can't rely on its behavior, meaning you'd have to rewrite at least some of the functionality. Want unicode support? Got a few functions to write, then..

Then you should look into what people would be using a GUI lib for, and provide features so everyone doesn't have to reinvent the wheel all the time.. and since such features are optional, why not include them?

axilmar
Quote:

Wouldn't something like threads be a great benefit to a GUI lib?

I think the decision to use threads or not is better left to the programmer. The point of my proposal is to only deal with GUI, nothing else.

Quote:

You'd end up having to wrap the native thread lib (Windows threads or pthreads) into a coherent interface anyway, so what's the difference if it's exposed or not?

It's not required for a GUI library to use threads. The basic execution flow of a GUI is: a) read events, b) dispatch events. If the user wants to use threads, that's fine.

Quote:

Plus there's issues between different STL implementations.. some are quite broken

Your comment would be valid 10 years ago, but nowadays it's not. All major compilers have a very nice STL implementation.

Quote:

Want unicode support? Got a few functions to write, then..

wstring is quite good. Win32 also has plenty of functions for unicode support.

Quote:

Then you should look into what people would be using a GUI lib for, and provide features so everyone doesn't have to reinvent the wheel all the time..

Because not everyone needs X feature or Y feature. Someone may want to use pthreads, someone else win32 threads, etc. Why force anything on the programmers?

SiegeLord

This sounds like an awesome idea. I've always lamented that wxWidgets does not have a Qt backend. Having a unified library to make GUI for both GNOME and KDE would be awesome. I am not a fan of using boost... I'd prefer the 3rd point to be "simple and small" which would rule out using boost...

Edgar Reynaldo

I've been gearing up to start on my own GUI and since you had mentioned earlier that you were working on one I thought about asking you about collaboration, however I can see that our overall desires for design would not work together at this time. Having a native look is not that important to me at this time, my GUI's idea of cross-platform is using Allegro while allowing customization by the user to the extent I am able to program in. I would like to achieve your 3rd goal of simplicity as well but I'll have to see how that goes. Regarding 4), boost seems like bloat to me, especially if it is just to manage memory. This can easily be taken care of with proper ownership. I agree with 5) but 6) seems like a feature I wouldn't have time for in the first version.

In any case, good luck and good coding.

axilmar
Quote:

I am not a fan of using boost... I'd prefer the 3rd point to be "simple and small" which would rule out using boost...

Why recreate shared ptrs and signals/slots when boost has them ready for us? I had a problem with boost too 'cause I am no fan of 'build it yourself', but since I discovered boostPro, I changed my mind. The aforementioned free download offers precompiled binaries for Visual Studio.

But it's not that hard to download and compile boost yourself. I did it on several occasions. You just download bjam, invoke bjam in the appropriate directory, wait a little and ...hey presto, you've got the binaries.

Recreating shared ptrs, weak ptrs, boost::bind and boost::signal is a major package of work itself.

Quote:

I've been gearing up to start on my own GUI and since you had mentioned earlier that you were working on one I thought about asking you about collaboration, however I can see that our overall desires for design would not work together at this time.

Sorry, I was talking about an application-oriented library. Creating a good gui library for Allegro was a dream of mine a few years back though. Do you have a proposal? I might be interested.

Edgar Reynaldo
Quote:

Creating a good gui library for Allegro was a dream of mine a few years back though. Do you have a proposal? I might be interested.

Well, I'd like to keep it pure Allegro if possible to be useable as an add on package.
I plan on using C++ for inheritance, polymorphism, and possibly templates.
I'd like gui objects to be able to interact with user classes if possible, although I'm still working on figuring out a good way to do that. Perhaps user derived gui objects is the way to go although boost's signals and slots look interesting at a first glance.
I'd like gui objects to be able to interact with each other in at least a minimal way when chained behaviour is requested.

Really though, I'm still in the brainstorming phase. I started putting together some of the basic layout ideas but give me a few days to have a more concrete proposal of any kind.

axilmar

Edgar,

Are you talking about a game-oriented GUI, an application-oriented GUI, or both?

Matthew Leverton

Are you polling for interest because you want help designing it? Coding it? To know if it's worth your time?

You will be best off to just code the base and a few widgets yourself. Then build some sample applications and release it all. That's the only way you'll both get it working and get some help.

As soon as you introduce other people into the "brainstorming," you'll never come to a satisfactory design as everybody will want their own way because they are all programmers that could do it themselves if they had the ambition. But if you get something working, people will care less about the implementation details and more about getting more widgets done.

Hopefully that is your plan, because on the Internet it is unfortunately the only way things work.

Edgar Reynaldo
Quote:

Are you talking about a game-oriented GUI, an application-oriented GUI, or both?

Well, both really. I'm not quite sure what your ideas of the differences between them are though. If you could tell me that I should be able to give you a better answer. I don't seem to have a differentiated notion of one from the other. One of the main things I'd like to accomplish is a smooth interface between the gui and the function it runs in so that information is easily passed back from the gui to let the function alter settings, update objects, and so on. I'd basically like the gui to be able to interact with other objects without making those objects part of the gui or its components. One thing that's proving difficult for me to imagine is the design scheme of modern program guis. Is there some kind of super global object that knows everything and how to manipulate it or what? Maybe I need to check out some open source projects for some design ideas.

Quote:

You will be best off to just code the base and a few widgets yourself. Then build some sample applications and release it all. That's the only way you'll both get it working and get some help.

That's probably how I'll do it. I wouldn't mind some advice on flexible object interface techniques but that may be better left to ask for in a different thread.

axilmar
Matthew Leverton said:

Are you polling for interest because you want help designing it? Coding it? To know if it's worth your time?

a) I want help designing it.
b) I want to be motivated through the sharing of common goals.

Edgar Reynaldo said:

I'm not quite sure what your ideas of the differences between them are though.

The update method for a game GUI is different from the update method for an application GUI. For applications, the traditional method of putting paint events in a queue is adequate for most GUI cases. For games, it's not acceptable: the update method has to be mixed with the game graphics update method.

Edgar Reynaldo said:

One of the main things I'd like to accomplish is a smooth interface between the gui and the function it runs in so that information is easily passed back from the gui to let the function alter settings, update objects, and so on.

Hey, me too. I am fed up having to code a lot of lines of code just to do the most trivial tasks, i.e. change a variable etc.

What I've done in my latest project (using Qt 3.0.5) is to write connection objects between data models and widgets. Qt 3 does not support the Model-View-Controller paradigm, so I had to do this. It works exceptionally well.

Say, for example, that I want to edit an int variable. All I have to do is:

int var;
new LineEditConnection<int>(var, new QLineEdit(form));

The connection object connects the QLineEdit's signals with its own slots, knows how to convert a String to an int, and sets automatically the variable 'var' to the appropriate value.

I think this model of connecting things together with external classes is better than the Model-View-Controller paradigm, because it's much more flexible. With the MVC model, you are limited to what the author of the library thought is required. With this solution, you can connect anything to anything, provided that the necessary events/signals/callbacks/etc are provided.

Edgar Reynaldo said:

Is there some kind of super global object that knows everything and how to manipulate it or what?

Certainly not. All GUIs provide events; you have to create a good way of catching those events and connecting them to the appropriate handlers. The handlers themselves should contain all the necessary information to change the data as needed.

Edgar Reynaldo
Quote:

For applications, the traditional method of putting paint events in a queue is adequate for most GUI cases. For games, it's not acceptable: the update method has to be mixed with the game graphics update method.

I think that I would like a more direct method, that all necessary logical updates are performed and then the view is updated all at once and not asynchronously with a paint event queue. I may have misunderstood you though.

Quote:

What I've done in my latest project (using Qt 3.0.5) is to write connection objects between data models and widgets. Qt 3 does not support the Model-View-Controller paradigm, so I had to do this. It works exceptionally well.

It looks quite handy. I'd like to have widgets like this built in to my gui.
Something like :

//
class IntEditor : public TextEntryWidget {/* Magic */};// All widgets derived from GuiBase

GuiManager* Gui = new GuiManager;

int number = 0;
GuiBase* int_editor = new IntEditor(&number);
Gui->AddComponent(int_editor);
//

What I haven't fully imagined is a good way to handle the dependencies on that integer. Does it set a mode or merely a value and what needs to be updated because of it? Should this be accomplished through building callbacks into the widgets, if so is there anyway to avoid forcing the user to use specific call back types. Should the gui return a list of events that the user has to process into what needs to be updated, if so, what's a nice way to do that and stay organized?

Quote:

Certainly not. All GUIs provide events; you have to create a good way of catching those events and connecting them to the appropriate handlers. The handlers themselves should contain all the necessary information to change the data as needed.

It would be nice if I could build in a way for my gui objects to be able to call the handlers themselves, although I don't know a nice way to handle diverse function types and the appropriate way to pass in the correct arguments as well. I'll have to look over some of Boost's signal/slot source code and see if I can find some inspiration. One thing I have to learn about is function objects.

axilmar
Quote:

I think that I would like a more direct method, that all necessary logical updates are performed and then the view is updated all at once and not asynchronously with a paint event queue.

That's the best way to handle updates in a GUI. It also covers the case of a game GUI.

Quote:

What I haven't fully imagined is a good way to handle the dependencies on that integer.

Ah, you are correct. My code example above shows how the widget changes the integer, but not how the integer changes the widget.

I intentionally omitted the part where the integer is not really an integer, but a Property<int> instance. The property has callbacks for when modified, and the connection object modifies the widget if the property's value is changed.

Quote:

Does it set a mode or merely a value and what needs to be updated because of it? Should this be accomplished through building callbacks into the widgets, if so is there anyway to avoid forcing the user to use specific call back types. Should the gui return a list of events that the user has to process into what needs to be updated, if so, what's a nice way to do that and stay organized?

You only need a few primitives (model objects with 'change' callbacks, connection objects that connect the model objects with the widgets). You don't need callback types or anything else you mention.

Quote:

It would be nice if I could build in a way for my gui objects to be able to call the handlers themselves, although I don't know a nice way to handle diverse function types and the appropriate way to pass in the correct arguments as well.

You can do that sort of thing with boost::bind: you can bind any values to a function. It's very sophisticated, and a great deal of work to do on your own.

Edgar Reynaldo
Quote:

Ah, you are correct. My code example above shows how the widget changes the integer, but not how the integer changes the widget.

I was thinking more along the lines of that the integer is a setting somewhere else independent of the gui and what would be an efficient and practical way to notify the user that the setting had changed. Should it be a message returned by the gui that gets processed by the user or should the widget know what method(s) to call to take care of updating whatever is dependent on the value of that setting? I realize you were talking about the integer in an abstracted way, I just wanted to keep the question down to effective gui and widget behaviour and the methods of achieving it.

Quote:

You only need a few primitives (model objects with 'change' callbacks, connection objects that connect the model objects with the widgets). You don't need callback types or anything else you mention.

If there are model objects with change callbacks and connection objects connecting the model objects with the widgets, why not just build the callbacks into the widgets and make the user register one? What I mean is that the user could register a function pointer with the widget to call when the data changes. Were you talking about using the boost library to accomplish this? I haven't decided whether I want to include it but I'd prefer for the gui to only be dependent on allegro.

Something I'd like to avoid is gui code that forces the user to use lots of global variables. This pollutes the namespace and leads to scattered code which I'd like to stay away from. This is why I want to build more functionality into the widgets themselves which will hopefully help keep the scope localized to the function the gui runs in.

axilmar
Quote:

I was thinking more along the lines of that the integer is a setting somewhere else independent of the gui and what would be an efficient and practical way to notify the user that the setting had changed. Should it be a message returned by the gui that gets processed by the user or should the widget know what method(s) to call to take care of updating whatever is dependent on the value of that setting? I realize you were talking about the integer in an abstracted way, I just wanted to keep the question down to effective gui and widget behaviour and the methods of achieving it.

Talking with actual code is always easier:

1//our variable
2Model<int> data;
3 
4int main() {
5 //create a gui
6 Window *wnd = new Window;
7 
8 //edits the data
9 TextBox *tb = new TextBox(wnd);
10
11 //connect the data with the widget
12 data.modified.connect(boost::bind(&TextBox::set, tb));
13 
14 //connect the widget with the data
15 tb->textChanged.connect(boost::bind(&Model<int>::set, &data));
16 
17 //bla bla do events
18}

In the above code:

  1. when the variable 'data' is modified, the widget will change accordingly.

  2. when the widget's content is modified, the data will change accordingly.

So now you have a perfect connection between the UI and the data, specified externally.

Quote:

If there are model objects with change callbacks and connection objects connecting the model objects with the widgets, why not just build the callbacks into the widgets and make the user register one? What I mean is that the user could register a function pointer with the widget to call when the data changes.

Because keeping the widgets independent from the model:

  1. simplifies the development of the widget.

  2. sometimes you don't want widgets to be connected to models.

Quote:

Were you talking about using the boost library to accomplish this? I haven't decided whether I want to include it but I'd prefer for the gui to only be dependent on allegro.

Why re-invent the wheel? boost is there, it works out of the box.

Furthermore, I'd like to use:

  1. shared pointers everywhere. No more memory management, even for widgets.

  2. named arguments; it would be cool to be able to construct a widget with one line of code (e.g.: new Button(text="foo", click=bla bla, enabled=false))

Quote:

Something I'd like to avoid is gui code that forces the user to use lots of global variables. This pollutes the namespace and leads to scattered code which I'd like to stay away from. This is why I want to build more functionality into the widgets themselves which will hopefully help keep the scope localized to the function the gui runs in.

From my experience, a GUI application is composed of 3 things:

  1. the data model; the data of the app must be independent from the GUI, so as that multiple views of the data can be instantiated.

  2. the UI; all the UI forms, windows, etc.

  3. the controllers; the actual code that binds the other two together.

Good separation of concerns is the #1 quality of any good program.

Edgar Reynaldo
Quote:

In the above code:
1. when the variable 'data' is modified, the widget will change accordingly.
2. when the widget's content is modified, the data will change accordingly.

This is helpful certainly, but it is not the entire picture - neither TextBox::set nor Model<int>::set will generally know much about the display logic of the system involved. TextBox::set may know to pass back NEED_REDRAW to a gui manager that could have called it, but it's unlikely that Model<int>::set will know whether the user needs to update any graphics display related to the setting. Does boost::bind allow the return value of a function to be passed somewhere as well?

It seems to me a message has to be passed back along the proper channels to notify the user by means of the gui interface :

//
// Messages for the gui to pass back to the user to allow for dependency updates and for the gui to process
const int WIDGET_SETTING_CHANGED = 1;// for the gui to tell the user that one of the widgets changed a data setting
const int CHANGED_WIDGET_SETTING = 2;// for telling the gui that one of it's widgets settings has been changed outside of the gui
const int Other_Notifications...
class GuiUserMsg {
  public :
    WidgetId widgetid;
    int usermsg;
};
std::vector<GuiUserMsg*> GuiManager::GuiUpdate();

void GuiManager::UserMessages(std::vector<GuiUserMsg*>*);
//

Quote:

Because keeping the widgets independent from the model:
1. simplifies the development of the widget.
2. sometimes you don't want widgets to be connected to models.

I agree that they should both be autonomous, but that doesn't mean that the widgets can't have generic call backs available if the user desires it. I think that I'd rather handle this with a message system though, having the widget notify the gui of a setting change through a return, which the gui relays on to the user who is allowed to make the necessary dependency updates. I think this allows for the most flexibility by the user.

Quote:

Why re-invent the wheel? boost is there, it works out of the box.

I'll download it tomorrow - it will take me 3 hours to do it though as the source is 22MB in the smallest compression package, a .7z file. However, it seems like adding boost to the gui I'm working on may reduce the achievement of one of my main goals - simplicity for the end user with few if any dependencies. The part about boost generally being wholly contained in headers is interesting. I agree that Boost could be quite useful for the gui version you're looking to create though as it seems more like it is destined to be a professional developer package.

In any case, I've monopolized enough of your time and it looks you want to go a different direction for an Allegro Gui than I do. Thank you for your advice and answering my numerous questions. ;) Once I get the whole layout completed, I'll post a new thread for it. Thanks again axilmar.

axilmar
Quote:

It seems to me a message has to be passed back along the proper channels to notify the user by means of the gui interface

I don't understand why you have the notification mechanism. Can you explain why you think it is more useful than signals and slots?

The data model should never tell anyone to redraw anything. That's the job of the connected views.

Quote:

I agree that they should both be autonomous, but that doesn't mean that the widgets can't have generic call backs available if the user desires it. I think that I'd rather handle this with a message system though, having the widget notify the gui of a setting change through a return, which the gui relays on to the user who is allowed to make the necessary dependency updates. I think this allows for the most flexibility by the user.

So, are you saying that, instead of connecting the widget to the model, the widget should be connected to something else, and this something else should actually modify the model?

Well, that's possible, as well, with signals and slots. In my example, the connection between the text box widget and the model is done externally: the text box does not know anything about the model, and vice versa. So, instead of connecting the model directly to the widget, you can connect something else to the widget.

I see no need for a messaging queue in either case.

Quote:

However, it seems like adding boost to the gui I'm working on may reduce the achievement of one of my main goals - simplicity for the end user with few if any dependencies.

Actually, adding boost to your project will simplify it: shared ptrs, bind, etc simply the development greatly.

Quote:

In any case, I've monopolized enough of your time and it looks you want to go a different direction for an Allegro Gui than I do. Thank you for your advice and answering my numerous questions. ;) Once I get the whole layout completed, I'll post a new thread for it. Thanks again axilmar.

Thank you very much for the discussion.

Edgar Reynaldo
Quote:

I don't understand why you have the notification mechanism. Can you explain why you think it is more useful than signals and slots?

I see it like this - the function running gui->GuiUpdate() (asking for user input) is basically asking to know what the user has changed setting and selection wise so it makes sense to me to have GuiUpdate() return notifications as to what the user input resulted in. Basically, I think it just needs to return which widget it was that changed a setting. From the widget id, the local function knows what setting was changed and what it should do about it. With only signals and slots between the widget and the data it modifies, the controller (local function) is cut out of the loop and it seems it would have to check whether the data changed before it could make an appropriate decision as to what else needs to be done based on the relevance and context of the data modified by the widget. I think another limitation of signals and slots is that the scope they are run from might not necessarily have all the context needed to make the appropriate calls, although I haven't programmed with them before and I don't really know this.

Quote:

So, are you saying that, instead of connecting the widget to the model, the widget should be connected to something else, and this something else should actually modify the model?

Yes, my idea is that the widget relays information to it's controller, the Gui class, which relays information back to it's controller the local function which then makes the relevant decisions what to do with the information.

In a static gui in which all of the data modified remains unused until the gui is closed, I can see that GuiUpdate() might not need to return any information and the user is still free to ignore the function return if they wish to do so. Notifications should allow for both a statically run gui where the settings are checked after the gui closes and one where it is run interactively, effectively becoming an extension of the function. At least I hope so. ;D

Just got Boost downloaded, I'll check it out tomorrow. ;)

axilmar
Quote:

I see it like this - the function running gui->GuiUpdate() (asking for user input) is basically asking to know what the user has changed setting and selection wise so it makes sense to me to have GuiUpdate() return notifications as to what the user input resulted in. Basically, I think it just needs to return which widget it was that changed a setting. From the widget id, the local function knows what setting was changed and what it should do about it. With only signals and slots between the widget and the data it modifies, the controller (local function) is cut out of the loop and it seems it would have to check whether the data changed before it could make an appropriate decision as to what else needs to be done based on the relevance and context of the data modified by the widget.

In case you have signals and slots, you don't need the local function. It's work can be done inside the slots.

Quote:

I think another limitation of signals and slots is that the scope they are run from might not necessarily have all the context needed to make the appropriate calls, although I haven't programmed with them before and I don't really know this.

That's why you use boost::bind for: to bind the required scope to the slots.

Quote:

Yes, my idea is that the widget relays information to it's controller, the Gui class, which relays information back to it's controller the local function which then makes the relevant decisions what to do with the information.

One problem with your mechanism is that you need a lot of event classes to represent all possible data changes, whereas with signals and slots you don't need those event classes, as the data are directly passed to the slots.

Quote:

In a static gui in which all of the data modified remains unused until the gui is closed, I can see that GuiUpdate() might not need to return any information and the user is still free to ignore the function return if they wish to do so. Notifications should allow for both a statically run gui where the settings are checked after the gui closes and one where it is run interactively, effectively becoming an extension of the function. At least I hope so.

Nothing prevents you from not connecting a GUI to a model in the case of signals and slots. Suppose you have a modal dialog: you don't connect any widget to a model. The widgets change their contents, and when the user presses OK, then the data are transferred to the models.

There is one benefit from using a queue of events: the changes are there for anyone to see and process in their own pace.

There is also a drawback: the queue of events might contain multiple successive changes of the same widget, whereas only the last change is relevant.

The event queue can be simulated almost automatically by the signals and slots mechanism, if you connect widget signals to slots that put data in a queue.

Edgar Reynaldo
Quote:

One problem with your mechanism is that you need a lot of event classes to represent all possible data changes, whereas with signals and slots you don't need those event classes, as the data are directly passed to the slots.

I shouldn't need any more than one event class, containing a message and a widget id. From the widget id, the user checks the required information if the widget is not modifying data directly and carries out any dependent calls. If this were accomplished using signals and slots I think the amount of required code would be generally equal. Instead of a line of code connecting a widget with a model and vice versa, you have a line of code that manually calls the respective functions that are necessary.

I also probably won't need to pass a queue of events back to the user but more likely just one event per update - each gui update can only process one input action by the user (button press, mouse click/drag) so I should only need to pass one event back. The user can also ignore most of the events if they don't wish the gui to run interactively but only modally. Edit Actually, nevermind. I probably will need to pass a queue back to the user in case of buffered commands piling up between gui updates. So disregard that bit.

Quote:

The event queue can be simulated almost automatically by the signals and slots mechanism, if you connect widget signals to slots that put data in a queue.

I think this could work, but my inexperience with boost makes this slightly difficult to fully imagine. I think both methods should work out to roughly the same amount of code.

axilmar

Edgar,

Your argument is that 'I don't need that much of information coming from widgets'. My experience tells me otherwise: you need every bit of information possible. You can't just have a widget id and a message id in a queue.

Take for example, a tree view: the user can do lots of actions on it:

a) pressing ENTER or DELETE on an item (so you need the item information),

b) popping up a context menu on an item (you need the item information as well as the mouse position),

c) selecting multiple items and deleting them (you then need a collection of items).

I just don't see how only a widget id and a message id can be useful.

Edgar Reynaldo
Quote:

Your argument is that 'I don't need that much of information coming from widgets'. My experience tells me otherwise: you need every bit of information possible. You can't just have a widget id and a message id in a queue.

It's not a message id, but a message that will be packed as a bit field, most of which can be ignored by the user as they really only need the notification that a setting was changed - button widget was clicked , menu widget clicked, widget closed / purpose fulfilled, I think it should only be a few simple things. From the message, the user code that handles the messages returned decides what to do with it - if a menu selection was selected, the user decides what to do with the selection - close the gui / open another one.

Proposed solutions for your tree view example :

a) pressing ENTER is a task for a list selection widget, it would pass back it's id and a message ITEM_SELECTED or ITEM_ACTIVATED depending on what the widget is for. The user would handle the message by checking the widget's currently selected item or item range and then take the appropriate action(s) for whatever that information pertains to. Pressing DELETE could return ITEM_REMOVED.

b) If items have context menus , their owner widgets should know how to handle a menu and a selection from it. What the user would receive is a widget message containing the widget id of the context menu and a message like in the response for a user pressing ENTER as above.

c) Answered by a) mostly. This would be handled by an editable list selection widget. Whether it deletes items from a list directly or from a working copy of the list is up to the design of the widget. All it really has to do is tell the user that it's selection and/or list changed. Once the user knows this it can call one of the widget's methods to determine the contents of the list and deal with it appropriately.

I don't think that I'll need more than 32 different messages just to tell the user that the widget's settings changed and the user should check with the widget, but I may be somewhat short sighted about all this. I don't know really.

axilmar
Quote:

It's not a message id, but a message that will be packed as a bit field,

So your system will only have 32 possible options (assuming 32 bits)? what will you do if a possible extension in the future requires more options?

I think using bitfields is not a very good solution. Not only it's easy to screw the values up (because you need to do bitwise operations to get the appropriate values), but it's not very extensible.

Quote:

button widget was clicked , menu widget clicked, widget closed / purpose fulfilled, I think it should only be a few simple things.

So, with your system, how would you find which button was clicked? or which menu was clicked? are you going to search the tree for the widget that contains the 'clicked' flag?

You see, along the message, you also need the widget that initiated the action.

Quote:

if a menu selection was selected, the user decides what to do with the selection - close the gui / open another one

How would you handle the case that the selected menu item was not a command item? perhaps your item was a check box, or a radio button. You will need to test if the menu item widget is a menu item check box or a menu item radio button.

You can tell me that the programmer will know what each widget is, by using the widget's pointer as an id. But that's just bad programming practice. You turn an object-oriented design into a procedural one, using ifs.

What if the list of widgets is big? then you will have a big mess of 'if widget is this, do that' clauses. Not good.

Don't tell me that you will also use integer ids for widget identification. That's just a nightmare waiting to happen (anyone with Win32/MFC GUI experience can validate this).

Quote:

pressing ENTER is a task for a list selection widget, it would pass back it's id and a message ITEM_SELECTED or ITEM_ACTIVATED depending on what the widget is for. The user would handle the message by checking the widget's currently selected item or item range and then take the appropriate action(s) for whatever that information pertains to. Pressing DELETE could return ITEM_REMOVED.

So the user of your library will have to retrieve, from the widget, the list of affected items, in order to work with them. That's an extra step compared to when using callbacks, because in callbacks, the widget can directly pass the collection of affected items.

Quote:

If items have context menus , their owner widgets should know how to handle a menu and a selection from it. What the user would receive is a widget message containing the widget id of the context menu and a message like in the response for a user pressing ENTER as above.

Widget Ids are an evil thing.

Quote:

I don't think that I'll need more than 32 different messages just to tell the user that the widget's settings changed and the user should check with the widget, but I may be somewhat short sighted about all this. I don't know really.

Well, I can't tell you if you are short sighted or not, but I can certainly tell you the big problems I had with similar designs in my projects.

The widget id thinky is especially dangerous. Having to enumerate ids is a source of many problems.

With your design, all the data that are normally passed in callbacks will be stored into the widgets, making widgets taking up more memory than they need.

For example, let's say that the user pasted a piece of text in a text box. With your design, you will keep the pasted piece of text in the text box, along with the current cursor position, then return an action TEXTBOX_TEXT_INSERTED, and then the user would have to use an API to manipulate the text in order to do any validation.

The code would most probably be like this:

GUI_MSG msg;
do_dialog(dlg1, &msg);
if ((msg.action & TEXTBOX_TEXT_INSERTED) == TEXTBOX_TEXT_INSERTED) {
    TEXT_BOX *tb = (TEXT_BOX_WIDGET *)(msg.widget);
    //bla bla manipulate the inserted text
}

Incidentally, by coding the above I discovered that I need different TEXT_INSERTED messages for different widget classes, in order to identify the different APIs that can handle the text insertion. All widget toolkits that I know of have a simple one-line edit box and a multi-line edit box, possibly supporting rich text or html. So, in case you have two or more classes that support a specific action, you will also need to encode the class type into the message. So probably you will need more than 32 bits.

The same goes with buttons: you need a BUTTON_CLICKED, CHECK_BOX_CLICKED, RADIO_BUTTON_CLICKED, MENU_BUTTON_CLICKED, MENU_CHECK_BOX_CLICKED, MENU_RADIO_BUTTON_CLICKED in order to host different button type events.

If you don't do that, and you have a single ITEM_CLICKED id, then you will have to be very careful on casts. Example:

1GUI_MSG msg;
2do_dialog(dlg1, &msg);
3if ((msg.action & ITEM_CLICKED) == ITEM_CLICKED) {
4 if (msg.widget_id == BUTTON1) {
5 button1_action();
6 }
7 else if (msg.widget_id == CHECKBOX1) {
8 CHECK_BOX *cb = (CHECK_BOX *)msg.widget;
9 BOOL checked = cb->checked();
10 checkbox1_action(checked);
11 }
12 else if (msg.widget_id == CHECKBOX2) {
13 CHECK_BOX *cb = (CHECK_BOX *)msg.widget;
14 BOOL checked = cb->checked();
15 checkbox2_action(checked);
16 }
17 else if (msg.widget_id == RADIOBUTTON1) {
18 RADIO_BUTTON *rb = (RADIO_BUTTON *)msg.widget;
19 BOOL active = rb->active();
20 int option = rb->option();
21 checkbox2_action(active, option);
22 }
23 else if (msg.widget_id == TEXTBOX1) {
24 TEXT_BOX *tb = (TEXT_BOX *)msg.widget;
25 clear_text(tb);
26 }
27}

I think that the above is extremely bad programming style:

1) callbacks are declared away from their widgets.
2) each time there is an event, a whole bunch of ifs must be executed.
3) widget ids are evil.

Isn't it simpler to do the following?

Button *button1 = new Button(parent, button1_action);
CheckBox1 *checkBox1 = new CheckBox(parent, checkbox1_action);
CheckBox2 *checkBox2 = new CheckBox(parent, checkbox2_action);
TextBox *textBox1 = new TextBox(parent, textbox1_action);

EDIT:

If you only handle one event a time, why do you need a bitfield? you don't.

Edgar Reynaldo
Quote:

I think using bitfields is not a very good solution. Not only it's easy to screw the values up (because you need to do bitwise operations to get the appropriate values), but it's not very extensible.

Bitwise AND'ing with a flag isn't so hard, but perhaps #defined int flags would be better as it allows for switch statements. I've added a second unsigned int to the WidgetMessage for the action taken. The actions can be #defined by each widget's header separately this way.

Quote:

So, with your system, how would you find which button was clicked? or which menu was clicked? are you going to search the tree for the widget that contains the 'clicked' flag?

By the WidgetId - it will have an unsigned int field that is set when being added to a WindowWidget (basically a widget controller). The WindowWidget module will have a private unsigned int representing the next available id number. It is incremented each time a WindowWidget class object adds a widget to control. A WidgetId will be returned by the WindowWidget::AddWidget function. The user stores this and uses it for comparison. The WidgetId class has a method to compare WidgetId's and the WidgetMsg class has a method to check for a certain action by a certain widget. Example follows :

1//
2class WidgetMsg {
3 public :
4 WidgetId widgetid;
5 unsigned int msg;// values #defined by widget base class
6 unsigned int action;// values #defined by individual derived widget classes
7
8 inline bool EqualTo(unsigned int checkmsg , const WidgetId& checkid , unsigned int checkaction) {
9 return ((msg == checkmsg) && (widgetid.EqualTo(checkid)) && (action == checkaction));
10 }
11 private :
12};
13 
14/// In the users main processing function, duties could be relegated to a class as well
15 
16MusicPlayer music_player;
17bool song_playing = false;
18// Step 1 setup - create the widgets that will be used in the gui
19 
20Window* sound_controls = new Window(x,y,w,h);// Controller class for widget, is also derived from widget base to allow nested windows
21ClickButton* play_button = new ClickButton(x,y,w,h,"Play");// location relative to the area of the window that will control it
22ClickButton* stop_button = new ClickButton(x,y,w,h,"Stop");
23ListBox* song_listbox = new ListBox(x,y,w,h,&SongList);
24 
25const WidgetId play_id = sound_controls->AddWidget(play_button , "Play Button");
26const WidgetId stop_id = sound_controls->AddWidget(stop_button , "Stop Button");
27const WidgetId songlist_id = sound_controls->AddWidget(song_listbox , "Songs List");
28const WidgetId sound_controls_id = sound_controls->GetId();
29 
30WidgetMsgGroup* messages;
31WidgetMsg info;
32 
33// In the users main logic processing loop
34if (sound_controls->Open()) {
35 messages = sound_controls->HandleInput();
36 
37 if (messages) {
38 while(messages->IsMessageAvailable()) {
39 info = messages->takemessage();
40 /// Fully drawn out example but this is unnecessary - look at simpler example below using WidgetMessage::EqualTo function below
41 switch(info.msg) {
42 case ACTION_TAKEN :
43 if (info.widgetid.EqualTo(play_id) {
44 switch(info.action) {
45 case BUTTON_CLICKED :
46 if (song_playing) {
47 music_player.stop_music();
48 }
49 song_playing = music_player.play_music(song_listbox->GetSelection());
50 }
51 }
52 break;
53 }
54 /// Simplified example using WidgetMessage::EqualTo
55 if (info.EqualTo(ACTION_TAKEN , stop_id , BUTTON_CLICKED)) {
56 if (song_playing) {music_player.stop_music();}
57 }
58 if (info.EqualTo(ACTION_TAKEN , songlist_id , ITEM_SELECTED)) {
59 // this check is unnecessary and could be omitted as only play and stop
60 // really do anything
61 }
62 }
63 }
64}
65 
66//

Quote:

How would you handle the case that the selected menu item was not a command item? perhaps your item was a check box, or a radio button. You will need to test if the menu item widget is a menu item check box or a menu item radio button.

This works out okay because the user already has handles to each widget created, one to the object through the pointer returned by new, and one returned by WindowWidget::AddWidget as a WidgetId object. So the only testing needed is against the WidgetId contained in the widget message and if it checks out then the user uses their pointer handle to fetch information from the widget if necessary. After that they call any relevant code that is needed to handle dependencies.

Quote:

What if the list of widgets is big? then you will have a big mess of 'if widget is this, do that' clauses. Not good.

A large list of widgets is just that, a large list. It will take extra code to deal with whether callbacks are registered or called manually. Only the messages the user actually wants have to be checked, the rest can be ignored. Modal dialogs should only need a few comparisons per message with the information being read off when the dialog closes if it wasn't cancelled.

Quote:

Don't tell me that you will also use integer ids for widget identification. That's just a nightmare waiting to happen (anyone with Win32/MFC GUI experience can validate this).

I don't see how a privately incremented unsigned int being returned as part of an id can go wrong. The WidgetId also stores a name for the widget which is only compared if the WidgetId::id field is equivalent. Do you have an example of how it could fail?

Quote:

So the user of your library will have to retrieve, from the widget, the list of affected items, in order to work with them. That's an extra step compared to when using callbacks, because in callbacks, the widget can directly pass the collection of affected items.

Not necessarily. It depends on the individual widget design. Does it work with pointers to data or does it work with copies? If it's copies then yes it would have to be retrieved when it was necessary to work with it. If you build callbacks into widgets, you generally have to custom design the widget to work with your specific data and callback types. In my opinion it seems easier to let the widget be a generic worker and leave the specifics of what to do with its data up to the user.

Quote:

With your design, all the data that are normally passed in callbacks will be stored into the widgets, making widgets taking up more memory than they need.

Yes, but consider that widgets generally only work with built in data types and you can build direct editing options into the widgets with pointers. I doubt that large directly editing sets of widgets would take up more than a few kilobytes and a entire megabyte of memory seems silly.

Quote:

For example, let's say that the user pasted a piece of text in a text box. With your design, you will keep the pasted piece of text in the text box, along with the current cursor position, then return an action TEXTBOX_TEXT_INSERTED, and then the user would have to use an API to manipulate the text in order to do any validation.

If validation is necessary, it could be handled by the text editor widget through a user set function pointer.

Quote:

So, in case you have two or more classes that support a specific action, you will also need to encode the class type into the message. So probably you will need more than 32 bits.

I've already ditched the bit field and went with numbered #defines for WidgetMsg::msg and WidgetMsg::action. The actions will be defined by the individual widgets.

Quote:

Isn't it simpler to do the following?

Button *button1 = new Button(parent, button1_action);
CheckBox1 *checkBox1 = new CheckBox(parent, checkbox1_action);
CheckBox2 *checkBox2 = new CheckBox(parent, checkbox2_action);
TextBox *textBox1 = new TextBox(parent, textbox1_action);

That looks nice and simple but once you have to repeatedly customize the widgets for different callback types to include scope dependent information I don't think it will be worth it. Simple call back types are okay to include like setter functions but even those can have dependencies that the user may not want to handle in the callback function and if they did handle them there then more global variables start collecting and I'm not terribly fond of using more globals than I have to.

I think my main design philosophy right now is that determining the relevance of the data modified by the widgets should be left to the user and not to the widget. If some of that can be built into the widgets generically, that can be okay but it shouldn't be limited to what the widget is capable of doing which brings about the need for user notifications.

axilmar
Quote:

By the WidgetId - it will have an unsigned int field that is set when being added to a WindowWidget (basically a widget controller). The WindowWidget module will have a private unsigned int representing the next available id number. It is incremented each time a WindowWidget class object adds a widget to control. A WidgetId will be returned by the WindowWidget::AddWidget function. The user stores this and uses it for comparison.

The WidgetId is not really necessary: you could have used the actual widget address.

Quote:

Example follows

Here is your example with "proper" GUI coding:

MusicPlayer music_player;

Window* sound_controls = new Window(x,y,w,h);
ClickButton* play_button  = new ClickButton(sound_controls, x,y,w,h,"Play", boost::bind(&MusicPlayer::play, &music_player, boost::bind(&SongList::get_selection, &songList));
ClickButton* stop_button  = new ClickButton(sound_controls, x,y,w,h,"Stop", boost::bind(&MusicPlayer::stop, &music_player));
ListBox*     song_listbox = new ListBox(sound_controls, x,y,w,h, &songList);

See how much smaller and easier is to build a gui? no ids, no loops, etc.

Quote:

This works out okay because the user already has handles to each widget created, one to the object through the pointer returned by new, and one returned by WindowWidget::AddWidget as a WidgetId object. So the only testing needed is against the WidgetId contained in the widget message and if it checks out then the user uses their pointer handle to fetch information from the widget if necessary.

You suppose that the widget variables are visible by the message loop. But that's not just the case every time. For example, if you have a complex dialog created by a function, you don't have access to the widget variables.

Quote:

A large list of widgets is just that, a large list. It will take extra code to deal with whether callbacks are registered or called manually. Only the messages the user actually wants have to be checked, the rest can be ignored. Modal dialogs should only need a few comparisons per message with the information being read off when the dialog closes if it wasn't cancelled.

But you can avoid the big mess by using signals and slots.

Quote:

I don't see how a privately incremented unsigned int being returned as part of an id can go wrong. The WidgetId also stores a name for the widget which is only compared if the WidgetId::id field is equivalent. Do you have an example of how it could fail?

Ok, a privately incremented id counter can not go wrong that easily, but you still have to take care of naming those ids, and then using the right ids in the right places.

I don't see why should I have ids, when I can have slots attached directly to the widgets.

Quote:

Not necessarily. It depends on the individual widget design. Does it work with pointers to data or does it work with copies? If it's copies then yes it would have to be retrieved when it was necessary to work with it.

No, you have to retrieve those pointers as well, from the widget.

Quote:

If you build callbacks into widgets, you generally have to custom design the widget to work with your specific data and callback types. In my opinion it seems easier to let the widget be a generic worker and leave the specifics of what to do with its data up to the user.

Your solution is not easier: the callback data in my design are member variables in your design. They still exist.

Quote:

Yes, but consider that widgets generally only work with built in data types and you can build direct editing options into the widgets with pointers. I doubt that large directly editing sets of widgets would take up more than a few kilobytes and a entire megabyte of memory seems silly.

Sure, memory is plenty these days, but you don't know when you want more memory. For example, if I load DTED2 maps in memory, I suddenly need all the memory I want. In this case, widgets not taking up memory counts. And it's also a cache issue that affects performance.

Quote:

If validation is necessary, it could be handled by the text editor widget through a user set function pointer.

Aha. You are going to use a callback. So why not unify the two mechanisms (callbacks and messages), into one? and with boost, you can bind anything you desire into those callbacks, making the application really easy to code.

Quote:

That looks nice and simple but once you have to repeatedly customize the widgets for different callback types to include scope dependent information I don't think it will be worth it.

But different widget instances will do different jobs anyway, so you can't avoid scope-dependent information even with messages.

With boost::bind, you can attach whatever data you want in a function, so I don't see there a problem of scope.

Quote:

Simple call back types are okay to include like setter functions but even those can have dependencies that the user may not want to handle in the callback function and if they did handle them there then more global variables start collecting and I'm not terribly fond of using more globals than I have to.

That's what boost::bind is for: for avoiding global variables.

The problem with C++ is that it lacks lambda functions. We wouldn't have this conversation if C++ supported them. But since we don't have them, binding is the next best thing, even endorsed by the language creators themselves.

Quote:

I think my main design philosophy right now is that determining the relevance of the data modified by the widgets should be left to the user and not to the widget.

You can achieve that with signals & slots as well. A widget using signals does not modify the data, it only outputs a signal that something may be modified. The programmer can then attach any slot to modify whatever he desires.

Quote:

but it shouldn't be limited to what the widget is capable of doing which brings about the need for user notifications.

In either case (signals and slots, or notifications), a widget is limited by what it is designed to do: what signals it emits or what notifications it creates. So I don't see that as an argument as well.

Edgar Reynaldo
Quote:

The WidgetId is not really necessary: you could have used the actual widget address.

Yeah, I think you're right.

Quote:

See how much smaller and easier is to build a gui? no ids, no loops, etc.

To be honest, the example I put up was a very simple gui with very simple behaviour where each action taken by the user of the gui could be easily translated to a single function call. What happens when there are more than one or two lines of code that the user wants to execute on that condition, and some aren't desired to be function calls? What does the boost::bind(,...) function return? Is it a boost::function<T?> object? Can you really cram everything the user could want to do into a single argument and a single object type? Would you really want to do that for more complicated behaviours?

I can't get over the nagging feeling that it just won't be that simple.

Quote:

You suppose that the widget variables are visible by the message loop. But that's not just the case every time. For example, if you have a complex dialog created by a function, you don't have access to the widget variables.

Wouldn't this example be suitable for moving into a specialized gui class object to contain the gui and manage it?

Quote:

But you can avoid the big mess by using signals and slots.

Perhaps the length of the code would be reduced but it's complexity and minimum entry level of coding skill would increase. I've got the boost source , build and jam downloaded (23MB download and 208MB unpacked) and I've been looking at the template declarations for boost::bind.
This excerpt is from lines 1236 - 1261 of boost_1_35_0\boost\bind.hpp :

1 
2// bind
3 
4#ifndef BOOST_BIND
5#define BOOST_BIND bind
6#endif
7 
8// generic function objects
9 
10template<class R, class F>
11 _bi::bind_t<R, F, _bi::list0>
12 BOOST_BIND(F f)
13{
14 typedef _bi::list0 list_type;
15 return _bi::bind_t<R, F, list_type> (f, list_type());
16}
17 
18template<class R, class F, class A1>
19 _bi::bind_t<R, F, typename _bi::list_av_1<A1>::type>
20 BOOST_BIND(F f, A1 a1)
21{
22 typedef typename _bi::list_av_1<A1>::type list_type;
23 return _bi::bind_t<R, F, list_type> (f, list_type(a1));
24}
25 
26template<class R, class F, class A1, class A2>
27// ...

This is way over my head. Let me try anyway - The first template is a function definition for a function named bind (macro substituted for BOOST_BIND) that takes an object of type F and returns an object of type _bi::bind_t<R , F , _bi::list0> created with the (F,_bi::list0) constructor and takes template parameters of <class R,class F>. Blind stab at attempting to declare a variable to hold the return value :

//
double dbl = 3.14;
_bi::bind_t<int,double,bi::list0> UTO = bind<int,double>(dbl);
//

But what does UTO do? (Undiscernable Template Object)
I don't have the faintest idea what a _bi::bind_t even vaguely represents and I don't know how long it would take me to learn that either. Template errors returned by compilers for STL functions are hard enough to read, imagine getting back an error for one of the boost::bind templates or trying to debug the things.

Also, how would you make a generic argument for a widget constructor that took a boost::bind object?

Quote:

I don't see why should I have ids, when I can have slots attached directly to the widgets.

Isn't the type of the slots allowed in the signal limited to the type of the signal? What if you have multiple function types to call or local user code to execute?

Quote:

Your solution is not easier: the callback data in my design are member variables in your design. They still exist.

I only plan to use callbacks when it is logical to do so. The only way I've included member data function pointers so far is to provide some drawing behaviours for the widgets and to allow the user to customize them with their own drawing functions. This way some of the drawing details like the focus state / active state behaviours can be set for all widgets through their window or for individual widgets. It should be noted that I haven't made any widgets yet, only working on their base and their handler so far.

Quote:

Sure, memory is plenty these days, but you don't know when you want more memory. For example, if I load DTED2 maps in memory, I suddenly need all the memory I want. In this case, widgets not taking up memory counts. And it's also a cache issue that affects performance.

I don't know what DTED2 maps are but I'll take your word for it. The main memory problems for widgets would probably be from images and large text fields. Also, not all the data always needs to be stored within the widgets if they're working with pointers or references to the data they're modifying.

Quote:

Aha. You are going to use a callback. So why not unify the two mechanisms (callbacks and messages), into one? and with boost, you can bind anything you desire into those callbacks, making the application really easy to code.

If a callback is logical to have, then sure. It doesn't make sense to me to assume that the user of the gui will wish to code new functions to connect to a widget action when they could have just coded it locally. Hopefully this should keep the widgets flexible for the majority of users.

Boost::bind is not exactly an amateur or even intermediate level programming interface. I've been looking for a tutorial for boost::bind and these are the best two pages I've found :
http://www.codeproject.com/KB/library/BoostBindFunction.aspx
http://www.boost.org/doc/libs/1_35_0/libs/bind/bind.html
I found two gamedev.net threads as well, but they've gone even deeper into the boost library without offering much of an explanation of how bind works. I didn't find anything on bind in the doc directory of the source distribution and neither of the first two links have given me a clear understanding of how to use it. If I'm having this much trouble trying to figure things out, it's likely many novice programmers will decide not to bother with it, as well as intermediate users looking for a simple plug and play library. </EndRant?>

I'm not out to discourage you away from using Boost in your gui, I just don't see how it's right for me with the end goal of creating an entry level easy to use gui with no dependencies and a gentle learning curve.

Quote:

In either case (signals and slots, or notifications), a widget is limited by what it is designed to do: what signals it emits or what notifications it creates. So I don't see that as an argument as well.

A widget may be limited by what it is designed to do, but that doesn't mean it's design should limit what the user is able to do with it's events. I just don't see how signals/slots and/or bind can provide a one size fits all approach.

Quote:

You can achieve that with signals & slots as well. A widget using signals does not modify the data, it only outputs a signal that something may be modified. The programmer can then attach any slot to modify whatever he desires.

Can they attach multiple slot types to a single signal?

Quote:

The problem with C++ is that it lacks lambda functions. We wouldn't have this conversation if C++ supported them. But since we don't have them, binding is the next best thing, even endorsed by the language creators themselves.

I don't really understand what lambda functions are. In any case, this post is massive enough as it is. </EndChapter> :)

axilmar
Quote:

What happens when there are more than one or two lines of code that the user wants to execute on that condition, and some aren't desired to be function calls?

You get to write a new function that does the job, binding it with all the context it requires.

Quote:

What does the boost::bind(,...) function return? Is it a boost::function<T?> object?

No, but you don't have to care about that. Boost::function can be initialized with the result of boost::bind.

Quote:

Can you really cram everything the user could want to do into a single argument and a single object type? Would you really want to do that for more complicated behaviours?

The argument T of the template boost::function is not an argument type, it's a function signature. Some examples:

boost::function<void ()> f0;
boost::function<void (int)> f1;
boost::function<int (int, int)> f2;

Quote:

Wouldn't this example be suitable for moving into a specialized gui class object to contain the gui and manage it?

You could do it like that, but it is really not necessary if you used signals and slots: at the point you create the gui, you install all the necessary callbacks.

Quote:

Perhaps the length of the code would be reduced but it's complexity and minimum entry level of coding skill would increase.

I respectfully disagree. Using boost::bind and boost::function is very easy, and these constructs do not require any coding skills greater than your solution requires.

Quote:

This is way over my head. Let me try anyway

It's over my head as well. But I don't need to read the boost source code! I happily use boost::bind and boost::function, without needing to understand the internals.

Quote:

Template errors returned by compilers for STL functions are hard enough to read, imagine getting back an error for one of the boost::bind templates or trying to debug the things.

Indeed, the error messages produced by the thing are unreadable. But here is a trick: if there is a template error, it's in your code, not in boost. And therefore, you have to check how you use boost::bind and boost::function. In all the time I've used boost::bind and boost::function, all the errors I had were due to using wrong arguments in boost::bind. I learned to ignore the error shown by the compiler and go back to the line of code that created the error, inspect the call to boost::bind and find the error.

You gain so much by using boost::bind, that the problem of reading the errors produced by it are insignificant, at worst.

Quote:

Also, how would you make a generic argument for a widget constructor that took a boost::bind object?

What do you mean? you can use _1, _2, _3, ... to put arguments in the way you want, the order you want or perform the operations you want.

Quote:

Isn't the type of the slots allowed in the signal limited to the type of the signal? What if you have multiple function types to call or local user code to execute?

Indeed, the type of slots is limited by the type of signal. But you can bind functions to arguments using boost::bind, and therefore use functions with signals that has another signature from the functions.

Quote:

What if you have multiple function types to call or local user code to execute?

You put that in a separate function, that accepts the slot parameters, as well as any custom parameters, then you bind the custom parameters with the arguments you want, and voila: your code is called as a result of the signal being emitted.

Quote:

I don't know what DTED2 maps are but I'll take your word for it.

Digital Terrain Elevation Data. There are huge files, in the order of 100s of megabytes, that I have to make a display module for, for the current project I work with :-). Just an example that memory can always be scarce.

Quote:

If a callback is logical to have, then sure.

But you'll have two different mechanisms. Personally, I hate inconsistency.

Quote:

It doesn't make sense to me to assume that the user of the gui will wish to code new functions to connect to a widget action when they could have just coded it locally. Hopefully this should keep the widgets flexible for the majority of users.

But 'coded it locally' is the same as 'code new functions'. The code that goes inside the local block will be exactly the same as the code that goes inside the function. The user does not gain anything by your method. Your method has even the extra overhead of coding the message loop, whereas with functions you only need to write a new function.

Quote:

I found two gamedev.net threads as well, but they've gone even deeper into the boost library without offering much of an explanation of how bind works. I didn't find anything on bind in the doc directory of the source distribution and neither of the first two links have given me a clear understanding of how to use it. If I'm having this much trouble trying to figure things out, it's likely many novice programmers will decide not to bother with it, as well as intermediate users looking for a simple plug and play library.

Boost::bind is very easy to use, and perhaps that's where your confusion lies. The boost lib documentation explains it all, I don't see what more would you want. I learned boost::bind from that page!

Quote:

I'm not out to discourage you away from using Boost in your gui, I just don't see how it's right for me with the end goal of creating an entry level easy to use gui with no dependencies and a gentle learning curve.

I don't think your solution is easier than mine. I think your solution is more complex and more difficult to master than using boost.

It's easier to explain to a novice that when you see a boost::function<SIGNATURE>, you call boost::bind(FUNCTION_WITH_SIGNATURE) or boost::bind(METHOD_WITH_SIGNATURE, OBJECT) that does your job, than to explain the message loop, what the actions are, the widget ids, how to locate data, etc.

By using boost, you also have the advantage of using boost::shared_ptr, for memory management. Now that's a lot easier to explain to a novice, than how to appropriately use delete.

Quote:

A widget may be limited by what it is designed to do, but that doesn't mean it's design should limit what the user is able to do with it's events. I just don't see how signals/slots and/or bind can provide a one size fits all approach.

Exactly. Boost::bind allows you to bind any context to a function, and thus it does not limit you to do anything.

Quote:

Can they attach multiple slot types to a single signal?

Yes, through boost::bind.

Quote:

I don't really understand what lambda functions are.

They are anonymous functions that use variables of the scope they are defined into. C++ lacks those, and so you and I are having this conversation.

Quote:

In any case, this post is massive enough as it is.

It's very interesting to discuss about design of software. And it's also very important.

You have a point about 'no dependencies beyond allegro' though. Some parts of the boost library should have been parts of the STL a long time ago.

SiegeLord

I found MFC to be easy to use and it had no need for weird template tricks. As I understand wxWidgets and Qt and GTK++ have the same general principle as MFC. Why reinvent the square wheel that in addition to being confusing also takes additional 23 megabytes to download to compile?

I think you both would benefit by doing an audit of existing GUI libraries to see what works and what doesn't, and then see what effect your changes in methodology and API have on the ease of use of your GUI library compared to the existing ones.

That being said, I think lambda functions are generally quite nice, I found them quite fun to use in hoc for example (even though you have to jump through a hundred hoops to use them in that language) so if you can isolate that part of boost and not force the people who want to compile it to download the entirety of boost's source, then good for you.

Nevertheless, my philosophy is that distinct objects should only communicate between each other using messages, and nothing else.

Let me take two situations for example where I think lambda would be good and where it wouldn't be.

If ButtonA is pressed then ButtonA becomes invisible


This I feel should be done via a lambda function, since the activity does not span multiple objects.

If ButtonA is pressed then ButtonB becomes invisible


This on the other hand, should be done via a message loop, since the activity spans multiple objects.

axilmar
Quote:

I found MFC to be easy to use

I find MFC horrible: a) double creation of widgets (1st create the object, then call CreateWindow), b) different ways of deleting widgets (is it DestroyWindow or deleting the object?), c) win32 is not hidden at all, d) message maps are appalling.

Quote:

As I understand wxWidgets and Qt and GTK++ have the same general principle as MFC. Why reinvent the square wheel that in addition to being confusing also takes additional 23 megabytes to download to compile?

I think you both would benefit by doing an audit of existing GUI libraries to see what works and what doesn't, and then see what effect your changes in methodology and API have on the ease of use of your GUI library compared to the existing ones.

Good idea. I have already checked those out, so let's see (and I've put a couple of others that I know of):

  • wxWidgets: message maps. Same as MFC. Appalling, just like in MFC.

  • Qt: signals and slots, but applied through the MOC. Major disadvantage: you can't connect any function you want, it must be a slot compiled with MOC.

  • GTK: signals and slots, identified through strings. Uses valists. Dangerous.

  • Motif/Lesstif: function pointers with user data pointers. Untyped interface for callbacks, dangerous.

  • Swing: listener subclasses; work like lambdas, but enclosed variables must be final.

Quote:

Let me take two situations for example where I think lambda would be good and where it wouldn't be.

I think both examples can be handled extremely well by lambdas. Is there a particular reason why lambdas, for you, can't have references to more than one object?

Actually, Edgar's idea about returning an action code to the main loop is not such a bad idea after all! I am saying this because I am extremely bothered with dependencies, now that I think of it. It's one thing to build an executable for a client, and another thing to make a library. The client will never require boost, but forcing boost upon library users is not a very good thing.

You have to keep the event data inside the widgets, but for a simple solution with no dependencies, it's very good! The only other solution is callbacks with user data pointers, but that's inferior to Edgar's solution.

A slice of event handling should return:

  • the pointer to the widget that created the event.

  • an event id.

Perhaps this mechanism should be extended to handle mouse and keyboard events as well, unifying the system events with the user events, allowing the user to write mouse and keyboard event handlers without requiring to subclass widgets. For example:

1int main() {
2 WIDGET *root;
3 BUTTON *btn1, *btn2;
4 while (loop) {
5 MSG msg = do_events(root_widget);
6 if (msg.widget == root) {
7 }
8 else if (msg.widget == btn1) {
9 if (msg.id == BUTTON_CLICK) {
10 printf("button clicked\n");
11 }
12 else if (msg.id == LEFT_BUTTON_DOWN) {
13 beep();
14 btn1->do_message(msg);
15 }
16 else {
17 btn1->do_message(msg);
18 }
19 }
20 }
21}

EDIT:

What about when there are events that span multiple widgets? Perhaps event data should not be kept in widgets, but in the event themselves.

SiegeLord
Quote:

message maps are appalling

::shrug:: I found them reasonably pleasant to use in practice. As for wxWidgets I thought it was also signals and slots, and the message map macros were provided for the MFC fans as an option.

Quote:

Is there a particular reason why lambdas, for you, can't have references to more than one object?

The reason is purely philosophical. With a lambda one widget may affect the other instantaneously, perhaps when the other widget cannot respond to that action yet. With events the processing of the event can occur at an arbitrary time after the issue of the event. Also, in terms of parallelization, events generally are better.

In practice, I guess I don't care. Personally, here's the API I'd prefer myself the most, however it is realized.

1GuiHandler* handler = new GUI();
2CButton* button = handler->CreateButton();
3button->OnPress = ButtonFunc;//Or whatever other mechanism you use here
4CEditBox* box = handler->CreateEditBox();
5box->OnChanged = EditBoxChange;
6box->OnUpdate = EditBoxUpdate;
7 
8 
9//later in the main loop
10handler->Update()
11handler->Draw()
12 
13//in the global namespace, or perhaps in a class
14void ButtonFunc(GuiHandler* handler, CButton* widget)
15{
16 vector<ID_Type> id_array;
17 handler->GetIDByName("*top", &id_array);
18 handler->SendMessageTo(GUI_HIDE, id_array);
19}
20 
21void EditBoxChange(GuiHandler* handler, CEditBox* widget)
22{
23 global_var = widget->value;
24}
25 
26void EditBoxUpdate(GuiHandler* handler, CEditBox* widget)
27{
28 widget->value = global_var;
29}

axilmar
Quote:

::shrug:: I found them reasonably pleasant to use in practice.

Once upon a time, I had a message handler with a signature different than the one I was supposed to have. In release mode, the stack was trashed, but in debug mode, the app worked fine. It took me a month to find out the problem. In this month, our clients got the debug version, instead of the release version.

Quote:

With a lambda one widget may affect the other instantaneously, perhaps when the other widget cannot respond to that action yet.

So you don't want lambdas because of the 0.00000001% of the cases that they might not cause a problem? what about the rest of 9.99999999% that lambdas is the right thing to do?

Quote:

In practice, I guess I don't care. Personally, here's the API I'd prefer myself the most, however it is realized.

It's callbacks both ways (changes coming from widget and to widget). But it's better to wrap things up into classes that automate the process (see the example with the model classes I posted above).

I like Edgar's idea, but I forgot that changes do not come only from widgets, they also come from data, as well. So, it's signals and slots for me :-).

Edgar Reynaldo

Lots to respond to!

Quote:

No, but you don't have to care about that. Boost::function can be initialized with the result of boost::bind.
The argument T of the template boost::function is not an argument type, it's a function signature. Some examples:
What do you mean? you can use _1, _2, _3, ... to put arguments in the way you want, the order you want or perform the operations you want.

So which boost::function function signatures will you build into the constructors of your widgets? This is what I meant when I asked how you were going to form the argument types for your widget constructors, based on your example of :

//
ClickButton* play_button  = new ClickButton(sound_controls, x,y,w,h,"Play", boost::bind(&MusicPlayer::play, &music_player, boost::bind(&SongList::get_selection, &songList));
//

Quote:

Indeed, the type of slots is limited by the type of signal. But you can bind functions to arguments using boost::bind, and therefore use functions with signals that has another signature from the functions.

You put that in a separate function, that accepts the slot parameters, as well as any custom parameters, then you bind the custom parameters with the arguments you want, and voila: your code is called as a result of the signal being emitted.

You get to write a new function that does the job, binding it with all the context it requires.

This is one of the kinds of things I am hoping to avoid by leaving the major widget events to be processed to the user so they don't have to bind everything into a new function to fit the widget design. The message loop processing overhead in my design seems to equate to refactoring the users code in your design, at least for non-simplistic event handling.

Quote:

It's over my head as well. But I don't need to read the boost source code! I happily use boost::bind and boost::function, without needing to understand the internals.

Yes, but the problem is that I don't understand their interface, not from reading their manual page that I linked to earlier, and not from going to their interface in the header (which in this case is the implementation as well due to being templates). The problem is that I don't understand the basic prototypes of boost::bind - take these two examples from the manual page :

bind(g, _1, 9, _1)(x);                 // g(x, 9, x)

I understand that x is to be provided as the first and third argument to the function g when it is called, but not when it is called. Is g called when x changes then? How could it know when to call g without polling x?

Here's an example of boost::bind's confusing syntax :

bind(f, ref(i), _1);

What on earth does the _1 refer to? There are no arguments provided like x was in the first example.

If I'm going to understand boost::bind, I need to know the basic prototypes of it's use which I haven't found anywhere. Something like this would suffice :

//
template <class FuncType>
boost::function<FuncType> boost::bind(FuncType , arg1 , arg2 , ...) (bound_arg1 , bound_arg2 , ...)
//

It looks like the first argument list is constructor arguments but what is the second list? Is it a secondary function call that is called after the constructor executes? I just don't get it. :(

Quote:

Quote:

If a callback is logical to have, then sure.

But you'll have two different mechanisms. Personally, I hate inconsistency.

I don't mean including a call back for when the widget's data changes, I mean call backs for customizing widget behaviours.

SiegeLord said:

I found MFC to be easy to use and it had no need for weird template tricks. As I understand wxWidgets and Qt and GTK++ have the same general principle as MFC. Why reinvent the square wheel that in addition to being confusing also takes additional 23 megabytes to download to compile?

I think you both would benefit by doing an audit of existing GUI libraries to see what works and what doesn't, and then see what effect your changes in methodology and API have on the ease of use of your GUI library compared to the existing ones.

Except for wxWidgets, aren't those all platform specific? Also, wxWidgets is for a cross-platform look, whereas I just want cross-platform functionality with the same look. I may poke around to look at a few things, but I don't really want to go through the trouble of learning a whole new library before I write my own.

Quote:

Actually, Edgar's idea about returning an action code to the main loop is not such a bad idea after all! I am saying this because I am extremely bothered with dependencies, now that I think of it.

I hope you're not just saying this to make me feel good. ;)

Quote:

Perhaps this mechanism should be extended to handle mouse and keyboard events as well, unifying the system events with the user events, allowing the user to write mouse and keyboard event handlers without requiring to subclass widgets.

I wrote some keyboard and mouse event trackers for my programs since I was tired of tracking each key state separately and using rest(150) to prevent duplicate actions. I'm planning to use them in the gui for widget input. They need to be polled, but they operate passively and don't interfere with use of allegro's key array or key buffer or mouse data. Here's a zip file of key_handler and mouse_handler source and header files. I haven't tested the mouse handler code yet but the key handler seems to be working properly. They both track the state of the key/button as up, down, click or release states. Clicks and releases are stored in a buffer. On second look, I haven't implemented a mouse action buffer yet, only the keyboard action buffer.

           else if (msg.id == LEFT_BUTTON_DOWN) {
               beep();
               btn1->do_message(msg);

In my design, actions like this would be left up to the widget themselves and wouldn't notify the user that the button was being held down over the widget unless it was part of it's design. What's the btn1->do_message(msg) for?

SiegeLord said:

Personally, here's the API I'd prefer myself the most, however it is realized.

GuiHandler* handler = new GUI();
CButton* button = handler->CreateButton();
button->OnPress = ButtonFunc;//Or whatever other mechanism you use here
/// ...
void EditBoxChange(GuiHandler* handler, CEditBox* widget)
{
    global_var = widget->value;
}
//

If you make Create<WidgetType> a member function of the widget handler class, then the widget handler class is going to have to know every kind of widget there is just to use it. Isn't it better to have a widget handler AddWidget function that takes a widget base class pointer and interacts with the widgets through the base class interface? Also, the EditBoxUpdate and EditBoxChange functions don't seem very handy in my opinion. They introduce more globals and a new set of functions for each variable you wish to edit in that way. This is one of the reasons I'm not fond of Allegro's built in gui. It forces globals and extensive function creation and customization instead of autonomous objects. I don't think it makes for nice interfaces is all.

Quote:

I like Edgar's idea, but I forgot that changes do not come only from widgets, they also come from data, as well. So, it's signals and slots for me :-).

You can still build widgets that auto update their data from an external source using a pointer. You could also use a widget class method to update their data. I suppose that's what I'll be doing. ;)

Quote:

Digital Terrain Elevation Data. There are huge files, in the order of 100s of megabytes, that I have to make a display module for, for the current project I work with :-). Just an example that memory can always be scarce.

Sounds interesting, and exciting. I hope it all goes well for you. :)

SiegeLord
Quote:

If you make Create<WidgetType> a member function of the widget handler class, then the widget handler class is going to have to know every kind of widget there is just to use it. Isn't it better to have a widget handler AddWidget function that takes a widget base class pointer and interacts with the widgets through the base class interface?

Sure, I could very well go with:

GuiHandler* handler = new GUI();
CButton* button = new CButton();
handler->AddWidget(button);

That is indeed better. My example wasn't very well thought out.

Quote:

Also, the EditBoxUpdate and EditBoxChange functions don't seem very handy in my opinion. They introduce more globals and a new set of functions for each variable you wish to edit in that way. This is one of the reasons I'm not fond of Allegro's built in gui. It forces globals and extensive function creation and customization instead of autonomous objects. I don't think it makes for nice interfaces is all.

This is opposite for me. I specifically liked AllegroGUI for that reason. I have no phobia of global variables like some people do. ;) I don't like AllegroGUI for one reason only, it does not play well with hardware acceleration in my experience.

Also, I did specify in that comment "or whatever other mechanism you use here". You could switch that to boost::bind as well:

button->OnPress = bind(some_sort_of_std_function, _1, &global_var);//pardon my syntax, never used bind before

And if you really want to avoid the global variables altogether, you could just derive a new class from CButton, and overload its OnPress related functions yourself. In the edit box's case you could make a CStdEditBox whose contructor would look like CStdEditBox(const char* fmt, ...) ala sscanf, and avoid writing the getters and setters yourself. Perhaps, since it is presumably so useful, it would even be included in the vanilla version of the library.

As for the very necessity of the two functions for the Edit box, well I feel that they are very important. A long time ago, when I used MFC, I used this built in distinction in MFC to great success. When a variable changes, it gets passed to the EditBox. When the EditBox changes the variable does not immediately get updated, but it can be validated first.

Hehe, not that I say that I want you to adopt that philosophy! It's just the way I think and code.

axilmar
Edgar Reynaldo said:

So which boost::function function signatures will you build into the constructors of your widgets?

It depends on the widget. A push button widget, in our example, has a simple function for clicking, and therefore the signature of that function would be boost::function<void ()>.

Edgar Reynaldo said:

This is one of the kinds of things I am hoping to avoid by leaving the major widget events to be processed to the user so they don't have to bind everything into a new function to fit the widget design. The message loop processing overhead in my design seems to equate to refactoring the users code in your design, at least for non-simplistic event handling.

You don't have to write new functions for code that invokes another object's method though. The majority of cases is like that. The overhead of writing a message loop is greater than that of writing separate functions. And the code with signals and slots is easier to read, because the bound functions are close to their widgets.

Edgar Reynaldo said:

I understand that x is to be provided as the first and third argument to the function g when it is called, but not when it is called. Is g called when x changes then?

Nope.

Edgar Reynaldo said:

How could it know when to call g without polling x?

When we bind a value to a function, we simply create a new function with less parameters than the original one. The binding here is not interactive, i.e. it does not mean that when one value changes, the whole series of functions is invoked again.

Boost::bind is called 'currying' in functional languages: I have a function "f x y", when I bind y = 1, I have another function f' x => f x 1.

In the example you posted, the expression bind(g, _1, 9, _1) creates a function with one parameter out of function g that takes 3 parameters. When this new function will be invoked, the first parameter will be passed to g's 1st and 3rd parameters respectively.

Edgar Reynaldo said:

Here's an example of boost::bind's confusing syntax :

bind(f, ref(i), _1);

What on earth does the _1 refer to? There are no arguments provided like x was in the first example.

I wish you did not blame Boost because you don't understand binding :-).

The expression bind(f, ref(i), _1) creates a function object with 1 parameter that will invoke f(x, ref(i), 1st parameter) when invoked.

Edgar Reynaldo said:

It looks like the first argument list is constructor arguments but what is the second list? Is it a secondary function call that is called after the constructor executes? I just don't get it. :(

Boost::bind only creates a function object.

You can then invoke the function object as if it was a function.

Edgar Reynaldo said:

I don't mean including a call back for when the widget's data changes, I mean call backs for customizing widget behaviours.

Same thing.

Edgar Reynaldo said:

I hope you're not just saying this to make me feel good.

No, it's a good solution Edgar, it just does not fit my goals. I view the GUI as a 'process' which is used to view and manage an object model. Your solution fits nicely with the 'manage' part, but it does nothing for the 'view' part.

Edgar Reynaldo said:

You can still build widgets that auto update their data from an external source using a pointer.

A pointer alone is not good enough. There needs to be a signal emitted from the external source that widgets will attach themselves to.

Edgar Reynaldo said:

You could also use a widget class method to update their data. I suppose that's what I'll be doing.

Aha. So why not automate the whole thing, using a model class?

Vanneto
Quote:

As soon as you introduce other people into the "brainstorming," you'll never come to a satisfactory design as everybody will want their own way because they are all programmers that could do it themselves if they had the ambition. But if you get something working, people will care less about the implementation details and more about getting more widgets done.

This is just whats happening! Wow! :o

Edgar Reynaldo
Quote:

It depends on the widget. A push button widget, in our example, has a simple function for clicking, and therefore the signature of that function would be boost::function<void ()>.

You must have A5 installed, I think you're reading the gui library user's mind. Who says that the push button widget doesn't need data x and y and to call f(x,y) when the button is pushed? Is this where bind comes in for you by reducing the multi argument function call to a void() function call that grabs/stores x and y to pass to the user function that the widget calls when it's event occurs? Would they have to use boost::bind(f , ref(x) , ref(y)) for the argument then? And which event does it get called for? If a widget has multiple events do you provide an argument in the constructor for each?

Quote:

I wish you did not blame Boost because you don't understand binding :-).

If the Boost documentation provided an example I could understand I wouldn't blame them for not explaining it clearly. ;) Without knowing what kind of object it creates, how could I use it?

Quote:

No, it's a good solution Edgar, it just does not fit my goals. I view the GUI as a 'process' which is used to view and manage an object model. Your solution fits nicely with the 'manage' part, but it does nothing for the 'view' part.

Can't display type widgets just store a pointer to the data and use the stored value? I haven't thought much about the messages the user will be able to send to the widgets, but it's not like I won't have any. Considering I'm not using boost, what would you suggest?

Quote:

A pointer alone is not good enough. There needs to be a signal emitted from the external source that widgets will attach themselves to.

Couldn't this be provided by the user sending a message to the widget or calling a widget's 'refresh stored data copy from source' method?

Quote:

Quote:

You could also use a widget class method to update their data. I suppose that's what I'll be doing.

Aha. So why not automate the whole thing, using a model class?

I'm not quite sure what you mean. A custom data class with a set method that's hooked up to the widget? Why should the user have to adapt their classes to mine just to use my gui? Perhaps that's not what you meant. Model View Controller makes more sense to me as Data Display Logic. Isn't this more the task of the Logic Controller rather than the Data Model?

Quote:

This is just whats happening! Wow! :o

Perhaps superficially that is what it looks like. However, I believe this has been a very beneficial discussion for me. Discussing the potential strengths and weaknesses of our respective designs helps us avoid problems and focus on using better methods. Remember, our designs were never meant to be the same but that doesn't mean they can't benefit from each other.

axilmar
Quote:

Who says that the push button widget doesn't need data x and y and to call f(x,y) when the button is pushed?

That's why bind exists.

Quote:

Is this where bind comes in for you by reducing the multi argument function call to a void() function call that grabs/stores x and y to pass to the user function that the widget calls when it's event occurs?

Yes.

Quote:

Would they have to use boost::bind(f , ref(x) , ref(y)) for the argument then?

It depends on what the action needs to do. If you need the most recent value, and x is a local variable, then you write ref(x). If you need the value at the place of bind, you don't use a ref.

Quote:

Can't display type widgets just store a pointer to the data and use the stored value?

What if the value changes while the widget is displayed? you have to update the widget somehow.

Quote:

I haven't thought much about the messages the user will be able to send to the widgets, but it's not like I won't have any. Considering I'm not using boost, what would you suggest?

Use the same mechanism as for widgets. Values changed may create notifications, and then you can use these notifications to update the gui.

Quote:

Couldn't this be provided by the user sending a message to the widget or calling a widget's 'refresh stored data copy from source' method?

Why do it manually when it can be done automatically?

Quote:

Model View Controller makes more sense to me as Data Display Logic. Isn't this more the task of the Logic Controller rather than the Data Model?

If you have a need for a controller, then yes. But in most cases, the view simply shows what the data contain, without any need for transforming the data into other data. So, you don't need a controller. All you need is a model class that contains a signal that can be bound to a widget.

Quote:

However, I believe this has been a very beneficial discussion for me.

For me, as well. I never thought of Edgar's design.

Quote:

Discussing the potential strengths and weaknesses of our respective designs helps us avoid problems and focus on using better methods. Remember, our designs were never meant to be the same but that doesn't mean they can't benefit from each other.

I couldn't agree more!

Edgar Reynaldo
Quote:

What if the value changes while the widget is displayed? you have to update the widget somehow.

All I should need to do is have a virtual 'RefreshDataView' method in the widget base class. For widgets that need to refresh their data it can be called and for widgets that don't need it they can leave the implementation empty so it does nothing. The WindowWidget widget manager's implementation can be to call each controlled widget's RefreshDataView method. The widgets then re-read the value of the stored data and set a need_redraw flag if necessary.

Quote:

Why do it manually when it can be done automatically?

If you're going to automate it, how will the widget know when the data changes? What will call the widget's DataRefresh method? Will it be your model class? Does this mean that the user will have to use your model class instead of or within their own classes?

Quote:

If you have a need for a controller, then yes. But in most cases, the view simply shows what the data contain, without any need for transforming the data into other data. So, you don't need a controller. All you need is a model class that contains a signal that can be bound to a widget.

It sounds like this is the basis for your system, do you have an example of your model class I could take a look at?

Quote:

For me, as well. I never thought of Edgar's design.

And I had no experience with the abilities of bind or signals and slots, so its given me more options to explore in my own programs.

axilmar
Quote:

All I should need to do is have a virtual 'RefreshDataView' method in the widget base class. For widgets that need to refresh their data it can be called and for widgets that don't need it they can leave the implementation empty so it does nothing. The WindowWidget widget manager's implementation can be to call each controlled widget's RefreshDataView method. The widgets then re-read the value of the stored data and set a need_redraw flag if necessary.

But when are you going to call 'RefreshDataView'? are you going to poll? it's a waste of resources. If you have a huge dialog with 100s of widgets, polling all these values would be extremely wasteful.

If, on the other hand, you are going to manually do it, then doing it automatically with signals saves you from all the trouble (and it's more consistent).

Quote:

If you're going to automate it, how will the widget know when the data changes? What will call the widget's DataRefresh method? Will it be your model class?

The model class will have a signal that can be connected to the widget's RefreshDataView method.

Quote:

Does this mean that the user will have to use your model class instead of or within their own classes?

The purpose of the model class is to provide change signals. It can be a template, customized on the user type: Model<int>, Model<String>, Model<MyOwnType> etc.

Of course, nothing stops the user for making it's data objects change-aware, by adding the appropriate signals.

Quote:

It sounds like this is the basis for your system, do you have an example of your model class I could take a look at?

You can check the attached file. It's a version of an abandoned project from my job. It uses Qt, and provides a bridge between boost and Qt.

Thread #597148. Printed from Allegro.cc