|
Official algui thread. |
Matthew Leverton
Supreme Loser
January 1999
|
Edgar Reynaldo said: So all of your users In the beginning, there is only one user. You have to please that one first. |
Edgar Reynaldo
Major Reynaldo
May 2007
|
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). Matthew Leverton 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. My Website! | EAGLE GUI Library Demos | My Deviant Art Gallery | Spiraloid Preview | A4 FontMaker | Skyline! (Missile Defense) Eagle and Allegro 5 binaries | Older Allegro 4 and 5 binaries | Allegro 5 compile guide |
I LN
Member #12,580
February 2011
|
Hello everyone! Regards, PS: Using VS 2008, if it matters. |
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.
Disadvantages of events posted in event queue:
Disadvantages of callbacks:
Advantages of callbacks:
Hmmm...sorry, I can't see how events are better. |
Trezker
Member #1,739
December 2001
|
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
|
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:
So, you event queue lovers can do this: 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: 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
|
The layout files are xml. Users wont edit layouts manually, I'm making an editor. |
Edgar Reynaldo
Major Reynaldo
May 2007
|
axilmar said: Advantages of events posted in event queue.
Disadvantages of events posted in event queue:
Disadvantages of callbacks:
Advantages of callbacks:
Being a little more realistic : Advantages of events posted in event queue.
Disadvantages of events posted in event queue:
Disadvantages of callbacks:
Advantages of callbacks:
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. My Website! | EAGLE GUI Library Demos | My Deviant Art Gallery | Spiraloid Preview | A4 FontMaker | Skyline! (Missile Defense) Eagle and Allegro 5 binaries | Older Allegro 4 and 5 binaries | Allegro 5 compile guide |
Matthew Leverton
Supreme Loser
January 1999
|
Edgar Reynaldo said: 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
Major Reynaldo
May 2007
|
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? My Website! | EAGLE GUI Library Demos | My Deviant Art Gallery | Spiraloid Preview | A4 FontMaker | Skyline! (Missile Defense) Eagle and Allegro 5 binaries | Older Allegro 4 and 5 binaries | Allegro 5 compile guide |
tobing
Member #5,213
November 2004
|
Matthew Leverton said:
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
|
Edgar Reynaldo said: 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
|
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
|
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
|
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
|
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 Or store the widget 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
Major Reynaldo
May 2007
|
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. My Website! | EAGLE GUI Library Demos | My Deviant Art Gallery | Spiraloid Preview | A4 FontMaker | Skyline! (Missile Defense) Eagle and Allegro 5 binaries | Older Allegro 4 and 5 binaries | Allegro 5 compile guide |
bamccaig
Member #7,536
July 2006
|
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. -- acc.js | al4anim - Allegro 4 Animation library | Allegro 5 VS/NuGet Guide | Allegro.cc Mockup | Allegro.cc <code> Tag | Allegro 4 Timer Example (w/ Semaphores) | Allegro 5 "Winpkg" (MSVC readme) | Bambot | Blog | C++ STL Container Flowchart | Castopulence Software | Check Return Values | Derail? | Is This A Discussion? Flow Chart | Filesystem Hierarchy Standard | Clean Code Talks - Global State and Singletons | How To Use Header Files | GNU/Linux (Debian, Fedora, Gentoo) | rot (rot13, rot47, rotN) | Streaming |
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
|
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
|
I was just being pedantic. -- acc.js | al4anim - Allegro 4 Animation library | Allegro 5 VS/NuGet Guide | Allegro.cc Mockup | Allegro.cc <code> Tag | Allegro 4 Timer Example (w/ Semaphores) | Allegro 5 "Winpkg" (MSVC readme) | Bambot | Blog | C++ STL Container Flowchart | Castopulence Software | Check Return Values | Derail? | Is This A Discussion? Flow Chart | Filesystem Hierarchy Standard | Clean Code Talks - Global State and Singletons | How To Use Header Files | GNU/Linux (Debian, Fedora, Gentoo) | rot (rot13, rot47, rotN) | Streaming |
Edgar Reynaldo
Major Reynaldo
May 2007
|
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? My Website! | EAGLE GUI Library Demos | My Deviant Art Gallery | Spiraloid Preview | A4 FontMaker | Skyline! (Missile Defense) Eagle and Allegro 5 binaries | Older Allegro 4 and 5 binaries | Allegro 5 compile guide |
axilmar
Member #1,204
April 2001
|
Edgar Reynaldo said: 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:
Buttons:
Bars:
Editors:
Views:
Layout managers:
Standard dialogs:
Menus:
What do you think about these? do you think there is something important left out? |
|
|