Allegro.cc - Online Community

Allegro.cc Forums » The Depot » Official algui thread.

This thread is locked; no one can reply to it. rss feed Print
Official algui thread.
Matthew Leverton
Supreme Loser
January 1999
avatar

So all of your users

In the beginning, there is only one user. You have to please that one first. ::)

Edgar Reynaldo
Member #8,592
May 2007
avatar

Sorry if I'm double posting, but the server had problems with my entire post. It didn't say it was too long, it just didn't post it right. So here's the rest of my post (I'll try my best to keep it brief from now on, but I wanted to respond to everything axilmar said).

In the beginning, there is only one user. You have to please that one first.

Yeah, I guess you're right.

axilmar said:

Are you telling me that a Button base class should not send a click event, even if I subclassed it to change the way it is drawn?

I think you should re-examine your design ideas.

No, I'm not telling you that a Button base class shouldn't send an event. In the case that you want to override the Draw method only, the base class still sends the event. If a derived class wants to intercept a base class message all it has to do is redefine QueueUserMessage to check for the base class message. My design ideas are just fine, thanks.

axilmar said:

With callbacks, the procedure can be embedded into the widget's logic. With your design, the steps 1-2 happen inside the widget, the step 3 happens from the event queue, and step 4 does not happen unless the user handles the event.

Like I said before, if you really need a callback use one. But if you don't, don't. Also note that step 4 wouldn't happen if your user never set a callback either. What that proves, I don't know.

axilmar said:

The variables touched inside the callback would be globals.

So again, your system encourages either arbitrary structs or forced use of global variables. My system allows the user to use whatever suits them.

axilmar said:

I'd like my library to be consistent. If there was something that the event queue offered me that I cannot do with callbacks, then I'd consider that solution.

This works both ways - If callbacks offered me something I can't do with events then I'd use them.

axilmar said:

As I've shown you, callbacks cover more use cases than events. Callbacks are a more generic solution.

And as I've shown you, callbacks are rarely necessary. Widget messages are a more flexible solution (from the users point of view).

axilmar said:

And then, there is the issue of turning a design of events into object-oriented code. This is where the event system completely breaks down. MFC message maps anyone?

I don't know what you're talking about. There's no reason a user can't use object oriented code with widget messages. How is it that the event system breaks down?

Trezker said:

Of course you don't put all the event handling in main. You make controllers that handle specific tasks.

axilmar said:

@Trezker: I like your idea, but I guess Edgar Reynaldo will disagree with you, I am sure :).

axilmar, how perceptive of you! What's the point of using controllers when you could just use local code? It just leads back to arbitrary structs or global variables. There's no logical reason to add a layer of abstraction to something simple like messages.

I LN
Member #12,580
February 2011

Hello everyone!
Is there a complete working example of algui and Allegro 5? (only basic features)
I'm trying to learn Allegro (and since I'm at it, I figured the latest version would be better) and need a GUI.
Alternatively, if someone could indicate a simple GUI (buttons and editboxes are all I need now), It would do me very good.

Regards,
Laurentiu

PS: Using VS 2008, if it matters.
PPS: The link from http://www.allegro.cc/resource/CodeGallery/TextInput seems dead, if anyone cares

axilmar
Member #1,204
April 2001

@Edgar Reynaldo: The argument is global vs local variables, right? well, let me enumerate advantages and disadvantages of each approach:

Advantages of events posted in event queue.

  • access to the local context.

Disadvantages of events posted in event queue:

  • manual management of event id constants.

  • huge switch or if-then-else statements.

  • not good for injecting code into algorithms.

  • makes debugging asynchronous and hence more difficult than it ought to be.

  • heavy fragmentation of memory.

  • difficult to extend via C++ (see message maps vs virtual functions debates).

Disadvantages of callbacks:

  • no access to the local context.

Advantages of callbacks:

  • cleaner code by using smaller functions.

  • suitable for code injection in predefined algorithms.

  • synchronous debugging.

  • does not require memory allocations/deallocations.

  • easy to extend via C++.

  • no need to manage constants manually.

Hmmm...sorry, I can't see how events are better.

Trezker
Member #1,739
December 2001
avatar

Once I'm done with saving and loading layouts. Declaring all the widgets in your entire program can be done with one line of code. Or at least just a few...

Something along these lines. Note that I'm not using a single global anywhere.

//Setup
Layout layout("myinterface.layout");
My_controller controller(layout);
//Main loop
layout.Get_root().Handle_event(allegro_event);
controller.Update();
layout.Get_root().Render();

Matthew Leverton
Supreme Loser
January 1999
avatar

What do the layout files look like?

axilmar
Member #1,204
April 2001

Here is a compromise between the event design and the callback design:

  • a widget can have one more callbacks.

  • a callback has an event id and a callback procedure.

  • the event id is automatically generated or manually set.

  • if the callback is invoked and the callback procedure is not null, then the callback procedure is invoked, otherwise an event is posted to the event queue.

So, you event queue lovers can do this:

#SelectExpand
1int main() { 2 BUTTON *btn; 3 4 btn = create_button(); 5 6 //optionally set id 7 set_button_click_event_type(btn, MY_BUTTON_ID); 8 9 while (loop) { 10 al_wait_for_event(&event); 11 if (event.type == get_button_click_event_type_id(btn)) { 12 //bla bla 13 } 14 //or 15 if (event.type == MY_BUTTON_ID) { 16 //bla bla 17 } 18 } 19}

And we callback lovers can do this:

#SelectExpand
1BUTTON *btn; 2 3void btn_click() { 4 //bla bla bla 5} 6 7int main() { 8 9 btn = create_button(); 10 11 //optionally set id 12 set_button_click_proc(btn, btn_click); 13 14 while (loop) { 15 al_wait_for_event(&event); 16 } 17}

What do you people think?

Trezker
Member #1,739
December 2001
avatar

The layout files are xml. Users wont edit layouts manually, I'm making an editor.

Edgar Reynaldo
Member #8,592
May 2007
avatar

axilmar said:

Advantages of events posted in event queue.

  • access to the local context.

Disadvantages of events posted in event queue:

  • manual management of event id constants.

  • huge switch or if-then-else statements.

  • not good for injecting code into algorithms.

  • makes debugging asynchronous and hence more difficult than it ought to be.

  • heavy fragmentation of memory.

  • difficult to extend via C++ (see message maps vs virtual functions debates).

Disadvantages of callbacks:

  • no access to the local context.

Advantages of callbacks:

  • cleaner code by using smaller functions.

  • suitable for code injection in predefined algorithms.

  • synchronous debugging.

  • does not require memory allocations/deallocations.

  • easy to extend via C++.

  • no need to manage constants manually.

Being a little more realistic :

Advantages of events posted in event queue.

  • access to the local context.

  • can deal with all events in a single place, limiting the amount of searching necessary to find relevant code

Disadvantages of events posted in event queue:

  • manual management of event id constants.
    There's nothing to manage about an event id. All you do is declare it :

    if (wmsg == WidgetMsg(&button , TOPIC_BUTTON , BUTTON_CLICKED)) {do_stuff();}
    

  • huge switch or if-then-else statements.
    This is not a disadvantage if you like all of your event handling in a single place.

  • not good for injecting code into algorithms.
    ??? You can use whatever code you want inside an if clause.

  • makes debugging asynchronous and hence more difficult than it ought to be.
    Debugging with events is not any more difficult if you know how to debug in the first place. Set a breakpoint inside the message check, or inside QueueUserMessage. Easy as pie.

  • heavy fragmentation of memory.
    I don't believe that allocating and deallocating memory on the stack fragments memory. Prove me wrong if you like.

  • difficult to extend via C++ (see message maps vs virtual functions debates).
    You can do whatever you like inside an if clause. Provide some proof for this one.

Disadvantages of callbacks:

  • no access to the local context.
    Which forces use of either :

    • global variables

    • pointless structs just to bind data to another context

Advantages of callbacks:

  • cleaner code by using smaller functions.
    If clauses are just as modular as functions are, without scattering code all across your source file(s).

  • suitable for code injection in predefined algorithms.
    Define code injection, and tell me why you think local event code is unsuitable for it.

  • synchronous debugging.
    Event code debugging is just as linear as call back debugging. There's still a defined order to your code, and it's just as easy to follow.

  • does not require memory allocations/deallocations.

  • easy to extend via C++.
    You can extend event code just as easily as you can callback code.

  • no need to manage constants manually.
    What are you talking about? There's nothing to manage. All you do is compare an object to a constant object.

axilmar said:

Hmmm...sorry, I can't see how events are better.

Look closer. Because I can't see how callbacks are better.

Trezker said:

//Setup
Layout layout("myinterface.layout");
My_controller controller(layout);

Sounds ambitious. How do you expect your controller to be able to set callbacks / respond to events without using a predefined set of names for your widgets in your layout file? Ie... how will you hook code up to widget actions?

axilmar said:

What do you people think?

I think it's better than locking your users into callbacks only.

Matthew Leverton
Supreme Loser
January 1999
avatar

Sounds ambitious. How do you expect your controller to be able to set callbacks / respond to events without using a predefined set of names for your widgets in your layout file? Ie... how will you hook code up to widget actions?

I also use XML. Currently it's only for single elements:

ALGUI_BUTTON *b = algui_xml("<button id='close' text='Close Me' width='100' height='50' />");

Obviously you could also use the API to set it all, but the above ends up being far less verbose and easier to remember than a bunch of C calls.

Anyway, I also have this:

algui_find_by_id(void *parent, const char *id);

which could be used to find the individual elements after loading an XML layout (when I add full support).

Trezker is probably doing something similar.

Edgar Reynaldo
Member #8,592
May 2007
avatar

I use a similar mechanism in my GUI, except that const char* id is just string wname and you use FindWidgetByName(string name).

What I was getting at is that there is no way to hook widgets up to actions without using a predefined set of names in your controller, thus rendering a single controller only useful for a single set of named widgets.

Can anyone come up with a way to hook widgets up to actions at runtime without using a predefined set of names and functions?

tobing
Member #5,213
November 2004
avatar

ALGUI_BUTTON *b = algui_xml("<button id='close' text='Close Me' width='100' height='50' />");

Can you specify the text in such a way, that the text is actually taken from another file? To make localization simple. I'm using extra files that only contain the strings, so adding a language is a matter of translating strings in one file and be done. No need to recompile and sources...

Matthew Leverton
Supreme Loser
January 1999
avatar

Can anyone come up with a way to hook widgets up to actions at runtime without using a predefined set of names and functions?

You mean like embedding a scripting language into the layout? (Similar to JavaScript inside HTML.) If so, that's definitely overkill for what my goals for a GUI would be.

tobing said:

Can you specify the text in such a way, that the text is actually taken from another file?

I've not given any thought to localization yet. But something like this would let you do whatever you wanted:

algui_set_text_localization(my_proc);

ALLEGRO_USTR *my_proc(const char *text)
{
  return al_ustr_new(text);
}

// in the internal algui "set title" function
window->title = algui_localization_proc(title);

then you could do whatever you wanted for localization.

tobing
Member #5,213
November 2004
avatar

Maybe... that would let you replace one string by another. Unfortunately localization is a bit more difficult, for example it may be that in one language you need different strings where in another you need the same. Or singular versus plural, which works differently in different languages, or many other problems you run into.

It depends on how much text you have, of course, and how nice you want your output to be. For me, I'm always thinking about localization in the two languages I know best, which is german and english, and I try to write it so that both work right from the start. That's not perfect, but a good start.

Then I use a numbering scheme, associating numbers to strings. The number-to-text mapping is read from a language specific file, and everything else, be it code or script (or XML in your case) would refer to the numbers used. Works really fine for me.

Matthew Leverton
Supreme Loser
January 1999
avatar

Then you would do this:

algui_xml("<button text='1' />");

and your localization function would do a number to text mapping. It's up to you.

Text that requires context should be avoided. E.g.:

[ ] Choice One
[ ] Choice Two
[ ] Choice Three

| PROCESS ITEM(S) |

There the text on the button depends on the number of items selected. A better approach is to just use the text "PROCESS." Hopefully that eliminates the need for complex rules.

But in situations where it cannot be avoided, I don't see why the GUI should solve the problem for you. In that case, you'll need to pass the context along to your own localization function to get the proper string.

tobing
Member #5,213
November 2004
avatar

The GUI is not to solve the problem, I just put the string together somehow and pass it to the UI to be displayed. So in easy cases I might just take the string from the string manager (which maps IDs to strings), and in more complex cases, I might construct the string from parts taken from the string manager and then pass the result to the UI. Depends.

One of the games we are making at home is the RechenMonster (see depot) game, which trains doing computations in hour head. Aimed for kids at school, like 8.12 years maybe, but also fun for adults who want to do some training. The goal is to make this fun (not easy), and one of the points is that you need lots of text. In most cases it's quite easy though, so no problem.

There is one particular mini game inside, which is about reading the clock and times, and here different languages really do work quite differently. In this case, localization goes even beyond replacing text - you need different code (to check if an answer is correct).

But OK, this is in most cases not a problem for the games people make here, I would guess. Your suggestion to map "1" to a language dependent string will definitely do the trick in almost all cases.

axilmar
Member #1,204
April 2001

@Edgar Reynaldo:

1) you have to mange the constants TOPIC BUTTON, BUTTON CLICK, etc. And if you want to extend the library, you have to define new constants. If you then want to change the code, you have to maintain the constants.

2) even if you like all your event code in one place, it soon becomes unmanagable. Good coding practices require breaking long functions into smaller ones.

3) if i put a breakpoint inside QueueUserMessage, then the execution will be interrupted for all messages. It makes debugging more difficult.

4) i was not talking about local variables, but about messages put in the queues: the is allocation/deallocation all the time.

5) with events, you must bind c++ methods to events via a message map, which is awkward. With callbacks, it's much easier.

6)if clauses are not as modular as functions, because functions can have parameters.

7) code injection is an algorithm with hooks. you cannot insert hooks with events. You cannot specialize an algorithm with events.

For all the above, events are inferior to callbacks. Howeve, i am willing to offer a solution which covers both camps. What do you think of that?

Trezker
Member #1,739
December 2001
avatar

All the widgets in my layout has a unique name. So the controller can easily find the widgets it needs to work with.

Depending on how you want to work you could always look up the name
layout.Get_name(event.source) == "button_x"

Or store the widget
My_controller::My_controller(Layout &layout)
:button_x(layout.Get_widget("button_x"))
{}

Or you could set up callbacks in whatever way you wish. I'm pondering adding a callback feature somewhere. Perhaps in the event_queue, so it would call callbacks instead of storing events in a queue... Though if it could be used in two ways like that it wouldn't be a queue anymore.

Perhaps an Event_handler base class, with Event_queue and Event_relay subclasses or something like that. Then the widgets would only know that they're giving their events to a handler without caring about how they reach the user.

Edgar Reynaldo
Member #8,592
May 2007
avatar

axilmar said:

you have to mange the constants TOPIC BUTTON, BUTTON CLICK, etc. And if you want to extend the library, you have to define new constants. If you then want to change the code, you have to maintain the constants.

I still don't know what you mean by 'manage' the event id's. They're just a set of predefined constants at the top of each widget header. Adding new TOPIC_*'s is just adding a simple external constant, initialized by NextFreeTopicId(). The constants and enums defined by the library will most likely never change (not to say that more can't be added easily without breaking existing code).

axilmar said:

even if you like all your event code in one place, it soon becomes unmanagable.

I still haven't found this to be the case. All it takes to find an event is to do a Find in your editor for the address of the widget you want to know about.

axilmar said:

i was not talking about local variables, but about messages put in the queues: the is allocation/deallocation all the time.

Well, this is true, but it's very rare that you get more than one message per update. I would guess that the same memory slot is just reused, but I can't say for sure. I really don't think it amounts to much in the end though.

axilmar said:

with events, you must bind c++ methods to events via a message map, which is awkward. With callbacks, it's much easier.

I still don't know what a message map is. Can you give a small code example of this? Also, why can't you just call your C++ method from the if clause that handles the message?

axilmar said:

code injection is an algorithm with hooks. you cannot insert hooks with events. You cannot specialize an algorithm with events.

I don't understand this very well. Can you give me a code example? Why can't you just call your algorithm locally after specifying the hook's callback? Any code you can call with a callback can be called from an if clause.

axilmar said:

However, I am willing to offer a solution which covers both camps. What do you think of that?

Fine by me.

bamccaig
Member #7,536
July 2006
avatar

Trezker said:

The layout files are xml. Users wont edit layouts manually, I'm making an editor.

That, good sir, will depend on just how efficient your editor is. ;) It might be faster for some basic layout stuff, but if it's faster to change an attribute by opening the file in Vim then you're damn right I will. ;)

axilmar
Member #1,204
April 2001

My lib has the function algui_set_translation to set the text, which is loaded from a config file. Each widget receives the relevant message and reads the appropriate string from the config file.

@Edgar:

1)@when you make new classes and when you remove classes, when you make new event constants and delete old ones, or modify existing ones, that's what i mean about managing event ids...not at run time, but at compile time.

2) the long functions are truly difficult to maintain. Imagine having, let's say, 200 widgets...if each event takes 10 lines of code, the main loop would be 2000 lines of code! for just one loop!

3) if your widgets produce events like "value changed" when the mouse is moved, for example a slider, then hundreds, maybe thousands of message structs will be allocate each second.

4) i am posting this from my mobile phone, so it's kind of difficult to give you a message map example. You can search in google and you will find many. A message map is a way to bind messages to functions.

Calling a method of an object for each possible combination of message and method manually is impractical.

5) having to manually invoke an algorithm's continuation from the event loop is impractical.

Trezker
Member #1,739
December 2001
avatar

bamccaig: If you only want to change single attributes then I think it would be hard to make an editor that beats a text editor in usability. So in that case it's good I'm using xml. You can just open the file, search for the widgets name and change the attribute.

bamccaig
Member #7,536
July 2006
avatar

Edgar Reynaldo
Member #8,592
May 2007
avatar

axilmar said:

@when you make new classes and when you remove classes, when you make new event constants and delete old ones, or modify existing ones, that's what i mean about managing event ids...not at run time, but at compile time.

Well, I haven't gone to too much trouble while doing this. New widget with important actions? Write one constant declaration and definition, and write one enum with the possible actions. It's really not too much to ask a widget writer to do. It's about the same amount of work it takes to add a function pointer to your class and a method to set it. The TOPIC constant never changes, and the enumerated messages almost never change, so it doesn't require much upkeep either.

axilmar said:

the long functions are truly difficult to maintain. Imagine having, let's say, 200 widgets...if each event takes 10 lines of code, the main loop would be 2000 lines of code! for just one loop!

But you would prefer to have 200 different functions instead. In my case all it takes to find the relevant code is a Find for a widget address. You would have to Find the widget declaration and then the callback function you set for it. It's not all that different I'd say.

axilmar said:

if your widgets produce events like "value changed" when the mouse is moved, for example a slider, then hundreds, maybe thousands of message structs will be allocate each second.

No, most likely only 60 per second (or whatever your update rate is) at most, since CheckInputs almost always only produces one widget message per call. And that's only one push/pop per update, which I would guess uses the same memory location for each push. If I used a vector for the message queue then it would definitely use the same memory if I used reserve(). I really don't think this is a problem, but if you can prove otherwise then I might change it.

axilmar said:

i am posting this from my mobile phone, so it's kind of difficult to give you a message map example. You can search in google and you will find many. A message map is a way to bind messages to functions.

There's no need to use a message map though, if you want to call a function when you receive a specific event, you just do it. If the users want to complicate things by using a message map, then that's their choice to do so, but they really don't have to, nor should they.

axilmar said:

Calling a method of an object for each possible combination of message and method manually is impractical.

Not just impractical, but silly as well. If you really want to , you could use a std::map<WidgetMessage , void (*) ()>. Or whatever form your callbacks take. This really doesn't make sense though, because you can just call your specific function from the if (wmsg == ...) statement.

axilmar said:

5) having to manually invoke an algorithm's continuation from the event loop is impractical.

I don't understand this. Why would you continue an algorithm from an event detection anyway? And how would you integrate a callback for a widget into an algorithm instead? Shouldn't it be the other way around? That after detecting an event or inside a callback that you then call the algorithm?

axilmar
Member #1,204
April 2001

so it doesn't require much upkeep either.

But with callbacks you don't need to write even a single constant.

Quote:

But you would prefer to have 200 different functions instead.

Yes, 200 different functions can be organized much better than a continuous function spanning, let's say, 2000 lines of code.

And with such long functions you quickly lose track of variables.

Quote:

No, most likely only 60 per second (or whatever your update rate is) at most, since CheckInputs almost always only produces one widget message per call.

With Allegro 5, as with any other event system, the rate of mouse position change does not depend on the screen refresh rate. So, it really depends on how fast the user moves the mouse and how fast the hardware can read mouse events.

Quote:

And that's only one push/pop per update, which I would guess uses the same memory location for each push. If I used a vector for the message queue then it would definitely use the same memory if I used reserve().

If you use a vector, you have two problems in your hand: a) the reallocation of the vector's internal buffer, b) the copying of elements when you remove an element from the front of the vector.

If you use a list, then you have fragmentation. The same memory slot may or may not be reused, since there can be other allocations at the same time.

Quote:

I really don't think this is a problem, but if you can prove otherwise then I might change it.

It is, in memory-tight systems. For example, the iPhone gives you 48 MB of memory to play with.

Even if it's not a problem in modern PCs, though, I don't see why there has to be an allocation, since it's not required.

Quote:

There's no need to use a message map though, if you want to call a function when you receive a specific event, you just do it. If the users want to complicate things by using a message map, then that's their choice to do so, but they really don't have to, nor should they.

You don't understand the use case. Suppose you want to make a set of objects with virtual functions that correspond to events. Also suppose you want this binding of events to virtual functions to be permanent, so you can reuse the same components as-is.

The only way to do the permanent binding is to use a message map, i.e. a static map of event bindings to virtual functions, like MFC does.

Programming message maps is cumbersome and has a lot of problems.

With your system, though, you can't even do message maps, since events do not have static ids.

Quote:

This really doesn't make sense though, because you can just call your specific function from the if (wmsg == ...) statement.

Remember, you want a permanent reusable binding.

Quote:

I don't understand this. Why would you continue an algorithm from an event detection anyway? And how would you integrate a callback for a widget into an algorithm instead? Shouldn't it be the other way around? That after detecting an event or inside a callback that you then call the algorithm?

Suppose a widget defines a procedure (not a piece of code, but a series of steps), and also suppose this procedure has steps that must be supplemented by the end-user programmer. With events, in order to have these procedure execute from start to finish, the programmer is forced to respond to the events. With callbacks, no such thing is required: the injected code (i.e. the steps supplemented by the end-user programmer) can be bound permanently to components.

EDIT:

For the first version of algui, I am planning to do the following widgets:

Static widgets:

  • label; text label.

  • image; image label.

  • frame; a rectangular area with a background and a border. The background will be a color, a picture, or a gradient (horizontal/vertical), and the border would be a bitmap, or a 3d sunken/raised rectangle.

Buttons:

  • push button; standard button with a click event.

  • check box; standard check box with a tick.

  • radio button; standard radio button with an associated group; only one button can be selected.

Bars:

  • scroll bar; either horizontal or vertical.

  • progress bar; either horizontal or vertical.

  • slider; either horizontal or vertical.

Editors:

  • text box; a single line text editor.

  • combo box; a combination of text box and a list view.

  • spin box; a text box with buttons that allow the change of the value with a device other than the keyboard.

Views:

  • list view; items arranged vertically in a list.

  • tree view; items arranged in a hierarchical view.

  • table view; items arranged in a grid.

Layout managers:

  • horizontal box; widgets laid out horizontally.

  • vertical box; widgets laid out vertically.

  • grid box; widgets laid out in a grid.

  • splitter; child widgets will be resizable via handle bars.

Standard dialogs:

  • message dialog; a dialog with a text message, an icon, and one or more buttons.

  • file dialog; save or load a file or choose a folder.

Menus:

  • menu; horizontal/vertical menu.

  • menu item; item of a menu.

What do you think about these? do you think there is something important left out?



Go to: