|
Official algui thread. |
Thomas Fjellstrom
Member #476
June 2000
|
AMCERASOLI said: And I don't understand why when Allegro 5.0 wasn't ready, there was already a 5.1 version... Odd minor releases are always WIP or development releases. All 5.1, 5.3, 5.5, 5.7, and 5.9 releases will be development, much like 4.9 was. 5.1 is the "in progress" devel branch for 5.2, 5.3 for 5.4, etc. Its basically the older style linux kernel version release cycle. -- |
axilmar
Member #1,204
April 2001
|
AMCERASOLI said: hmm.. I supposed it.. but why are you using Allegro 5.1? 5.0 has just been released. The makefile was kindly donated by Bamcaig (thanks man!). I suppose he has Allegro 5.1? |
Evert
Member #794
November 2000
|
axilmar said: They are counter productive. It's simply bad code to have switch functions with many hundreds of lines of code. This is debatable. It's certainly easier to read code that is nice and compact, but that goes for any block of code, not just switch statements. Quote: I am fan of callbacks (signals and slots actually). In my library, users will be able to write callbacks which connect the appropriate widget actions to anything they want, including other widget actions, of course.
That's fine as a choice. But it's not how I would design an Allegro 5 GUI. In a sense, callbacks and handlers are how Allegro 4 did many things. Allegro 5 has an event system instead. Part of the point of having an Allegro 5 GUI, to me, would be to use the event system. Not that you can't put an event system on top of a callback system (just inject an event from the callback), but it does feel a bit clunky. |
Thomas Fjellstrom
Member #476
June 2000
|
Evert said: Part of the point of having an Allegro 5 GUI, to me, would be to use the event system. Qt Has an underlying event system, which its Signals and Slots sit on top of. Often signals and slots are sent directly via a regular method call (at some point), but you can also queue them up, which are then sent via the event queue. For a pure GUI app, signals and slots make the most sense IMO. But it can take some extra work to get a game to fit into it, if you don't just handle the main game stuff along side the gui. -- |
axilmar
Member #1,204
April 2001
|
Evert said: Allegro 5 has an event system instead. Part of the point of having an Allegro 5 GUI, to me, would be to use the event system. Do you really want to litter your code like this? 1switch (event.type) {
2 case WIDGET1_EVENT1:
3 ...
4 case WIDGET1_EVENT2:
5 ...
6 case WIDGET1_EVENT3:
7 ...
8 case WIDGET2_EVENT1:
9 ...
10 case WIDGET2_EVENT2:
11 ...
12 case WIDGET2_EVENT3:
13 ...
14 ...
15}
This kind of code becomes unmaintainable very quickly, especially if event types must be managed manually. Quote: there was quite a bit of discussion on whether callbacks lead to bad programming style. I suppose you mean the comments about callbacks leading to many global variables and many type conversions whereas messages in the queue don't need all that. It's true that with the message queue solution, you don't need global data or type conversions. However, there is a solution: an API that creates an array of void* pointers that is passed to the callback can be extremely useful. For example: void** algui_callback_data(void *data, ...); In the place the widget callback is set: algui_set_button_click(button1_click, algui_callback_data(data1, data2, NULL)); And then in the callback: void button1_click(void **callback_data) { void *data1 = callback_data[0]; void *data2 = callback_data[1]; } However, the above does not solve the problem of type conversions. In case of using custom events, the user event type generation can be automated: when a widget is created, it allocates a new event id by calling a specific function. The event type is stored internally in the widget. For example: ALGUI_BUTTON *algui_create_button() { ALGUI_BUTTON *btn; ... btn->click_event_type = algui_next_event_type(); } ALGUI_BUTTON *btn1 = algui_create_button(); ALGUI_BUTTON *btn2 = algui_create_button(); But then, in the message loop, one would have to do the following: if (event.type == algui_get_button_click_event_type(btn1)) { } else if (event.type == algui_get_button_click_event_type(btn2)) { } This is slower than callbacks, and perhaps leading to giant if-then-else statements. Anyway...I can't tell which is worse, at this point. Let me think about it. Additional comments are welcomed. What do you think about the automated event type generation? |
Evert
Member #794
November 2000
|
axilmar said: Do you really want to litter your code like this?
That rather depends. For some widgets, I may not be interested and simply discard their events. I would probably wrap the entire thing up into a function as well. Perhaps the best way to do it is to be able to register, or not, a widget as an event source. Quote: I suppose you mean the comments about callbacks leading to many global variables and many type conversions whereas messages in the queue don't need all that. It's true that with the message queue solution, you don't need global data or type conversions. However, there is a solution: an API that creates an array of void* pointers that is passed to the callback can be extremely useful.
I would go so far as to say that you need something like this in a callback situation. Type conversions aren't such a big deal, in a sense they're simply a consequence of using C rather than C++. |
axilmar
Member #1,204
April 2001
|
Evert, What do you think about the automated event type generation? EDIT: I guess widget events better be Allegro events, i.e. placed in a queue. But widget messages should not be Allegro events. The algui message system is about object orientation, not about events. I really like the automated event type generation: it allows the programmer to do all the work from the main loop, but it also solves the problem of event type management. |
AMCerasoli
Member #11,955
May 2010
|
Sorry for interrupt your deep conversation about programming... But I want to know, I compiled an Allegro 5.1 Windows binaries, it seems that $ make gcc -shared -Wl,-soname,libalgui.so -o lib/libalgui.so.1 obj/algui_list.o obj/al gui_rect.o obj/algui_tree.o obj/algui_widget.o `pkg-config --libs allegro-5.1 al legro_font-5.1 allegro_image-5.1 allegro_primitives-5.1 allegro_ttf-5.1` Package allegro-5.1 was not found in the pkg-config search path. Perhaps you should add the directory containing `allegro-5.1.pc' to the PKG_CONFIG_PATH environment variable etc... etc... etc... What are those "allegro-5.1.pc" files? EDIT: Binaries doesn't work, when I run my program using them, It ask me for freetype6.dll. I did something wrong when compiling allegro... FreeType should be static or something.
|
SiegeLord
Member #7,827
October 2006
|
Those are files used for pkg-config program. I don't think they are made anywhere else except in Linux. Just ditch the Makefile and compile it using gcc. "For in much wisdom is much grief: and he that increases knowledge increases sorrow."-Ecclesiastes 1:18 |
Peter Wang
Member #23
April 2000
|
axilmar said: I am using ASCII strings for widget ids. Does anyone think that ASCII strings are not enough for such a job? I agree, but.. Quote: for me, handling ASCII strings for widget ids has the following advantages: None of the advantages you listed have anything to do with ASCII or not, except the last. Even that is dubious. If your important characters are all within the ASCII range -- in practice, yes -- a UTF-8 string looks just like an 8-bit extended ASCII string. Non-ASCII characters are just passed through. Anyway, this is verging on off-topic.
|
Edgar Reynaldo
Major Reynaldo
May 2007
|
axilmar said: It's not as trivial as you mention. You have to manage notification ids per widget: for every new command or option you put in the program, you have to allocate a new notification id. And this has to be done manually by the programmer. This is something that I have experience on due to using MFC, and let me tell you that it is very difficult and time consuming. Actually it is rather trivial to do. I'll use my GUI as an example since I use this system quite easily and it adds very little overhead and only requires minimal effort on the part of the user. It starts with the WidgetMessage class : 1/* Returns the next available message id value to prevent message collision.
2 Use TOPIC_* naming with
3
4 extern const unsigned int TOPIC_WHATEVER;
5 in the header and
6
7 const unsigned int TOPIC_WHATEVER = NextFreeTopicId();
8 in the source module.
9*/
10unsigned int NextFreeTopicId();
11
12
13
14class WidgetBase;
15
16class WidgetMsg {
17 typedef unsigned int UINT;
18
19private :
20 WidgetBase* from; // Address of the widget returning this message.
21 UINT topic;// Whatever this is in regards to, like it being about a dialog. Each widget class is
22 // assigned a value for this field from the NextFreeTopicId function, as well as
23 // there being a USER topic for all generic notifications to the user.
24 int msgs; // Bitfield for setting message flags, or single message id. Up to the widget class.
25
26public :
27
28 WidgetMsg();
29 WidgetMsg(WidgetBase* widget_address , UINT widget_topic , int messages);
30 WidgetMsg(const WidgetMsg& wmsg);
31
32 ~WidgetMsg() {}
33
34 inline WidgetBase* const From() const {return from;}
35 inline const UINT Topic() const {return topic;}
36 inline const int Messages() const {return msgs;}
37
38 inline bool IsAbout(const WidgetBase* widget , UINT widget_topic) const {
39 return ((from == widget) && (topic == widget_topic));
40 }
41
42 inline bool IsMessageTopic(UINT widget_topic , int message) const {
43 return ((topic == widget_topic) && (msgs == message));
44 }
45
46 inline WidgetMsg& operator=(const WidgetMsg& wmsg) {
47 from = wmsg.from;
48 topic = wmsg.topic;
49 msgs = wmsg.msgs;
50 return (*this);
51 }
52
53 inline bool operator==(const WidgetMsg& wmsg) const {
54 return ((from == wmsg.from) && (topic == wmsg.topic) && (msgs == wmsg.msgs));
55 }
56
57 friend std::ostream& operator<<(std::ostream& os , const WidgetMsg& wmsg);
58
59};
1
2#define NONE 0
3
4
5
6unsigned int NextFreeTopicId() {
7 static unsigned int id = NONE;
8 id += 1;
9 return id;
10}
Each widget has its own TOPIC and an enum describing the messages it may send : In Widget.hpp extern const unsigned int TOPIC_BUTTON_WIDGET; enum BUTTON_MSGS { BUTTON_CLICKED = 0, // Sent whenever a spring button is pushed down. BUTTON_HELD = 1, // Sent whenever a spring button is held down. BUTTON_RELEASED = 2, // Sent whenever a spring button is released. BUTTON_TOGGLED = 3 // Sent whenever a toggle button is toggled, poll for the current state. }; In Widget.cpp const unsigned int TOPIC_BUTTON_WIDGET = NextFreeTopicId(); When something important happens, the widget queues a WidgetMessage. QueueUserMessage is a virtual function that each widget may or may not redefine. The base class implementation of QueueUserMessage sends the message to it's parent if it has one, or if it is a top level widget then it adds the message to a list that can be retrieved by the user. In Widget.cpp, using a very basic button as an example : WidgetMsg Button::CheckInputs(int msx , int msy) { UINT ret = DIALOG_OKAY; if (area.Contains(msx,msy) && input_mouse_press(LMB)) { QueueUserMessage(WidgetMsg(this , TOPIC_BUTTON_WIDGET , BUTTON_CLICKED)); ret |= DIALOG_INPUT_USED; } return WidgetMsg(this , TOPIC_DIALOG , ret); } And finally, how the user uses the message : In main.cpp : 1Button btn1;
2WidgetHandler gui;
3gui << &btn1;// Adds button 1 to the gui
4
5
6const WidgetMsg btn1click(&btn1 , TOPIC_BUTTON_WIDGET , BUTTON_CLICKED);
7
8// in logic loop
9 WidgetMsg wmsg;
10 while (gui.HasMessages()) {
11 wmsg = gui.TakeNextMessage();
12
13 if (wmsg == btn1click) {
14 do_anything_we_want_here();
15 }
16
17 }
All in all, it's very simple and all the user has to do is compare a WidgetMsg to see if a specific action occurred. If you're not interested in a specific message, don't check for it. Sure if you have a lot of messages then the comparisons can get long, but there's no reason you have to use a switch statement that includes every case of action represented by a WidgetMsg. Contrast this to using callbacks and/or signals and slots. To do this, the user has to create a struct, bind the addresses of affected data to that struct, set the address of that struct(s) for their callbacks, set the callback to their handler function, and then write the code for their callback/slot. If you decide to change what you're doing inside that callback, then you need to modify the struct to reflect the new data, modify your callback to reflect the new struct, and modify the callback to reflect the new procedure. This is all a whole lot more work than it is worth when you could have just compared a single message and then written local code to accomplish what you want to do. My junior high algebra teacher taught me an important principle, KISS. 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
|
I actually took a look at my source code again, and I see I also wrote this function: void algui_observe(void *component, int event_type, bool (*proc)(void *, void *)); used like: bool button_onclick(void *self, void *event) { ALGUI_BUTTON *button = (ALGUI_BUTTON *) self; ALGUI_CLICK_EVENT *e = (ALGUI_CLICK_EVENT *) event; // ... return true; } algui_observe(button, ALGUI_CLICK_EVENT_ID, button_onclick); The return code determines whether or not the event bubbles up to the parent. For example, you could observe the mouse click event on the root node of the GUI to process every click. This step happens after the widget implementation has a chance to process events internally. That is, there's also an onclick entry in the button's vtable. It gets called when the button is clicked, which allows it to do something related to its implementation or behavior. It can then choose to emit the event to the user, which can handle it by the above code. I do also support processing events via an ALLEGRO_EVENT_QUEUE, but after reviewing my code and remembering past thoughts, I tend to agree with axilmar that it's not very useful (particularly as the project gets large). |
axilmar
Member #1,204
April 2001
|
@Peter Wang: these advantages have nothing to do with ascii indeed. They have to do with the default string type C has. Even so, I think that skin files should be kept as simple as possible, following the KISS principle. @Edgar Reynaldo: if this was c++, I would definitely use signals and slots, because in c++ you have the ability to bind variables to functions. Your system is good, but signals and slots is alot more simpler. However, in C, things are different.Since C lacks any kind of templates or lambdas, handling events from the main event queue makes sense, because it allows access to the program context. But there are cases where the event queue cannot be used. For example, when a text is inserted in a textbox, the usual approach is to invoke a user callback that validates the insertion. I cannot see how this will work with the event queue. This thing with messages/callbacks/events needs more thought. |
Edgar Reynaldo
Major Reynaldo
May 2007
|
axilmar said: if this was c++, I would definitely use signals and slots, because in c++ you have the ability to bind variables to functions. Just because you can do something doesn't mean you should. Edgar said: Contrast this to using callbacks and/or signals and slots. To do this, the user has to create a struct, bind the addresses of affected data to that struct, set the address of that struct(s) for their callbacks, set the callback to their handler function, and then write the code for their callback/slot. If you decide to change what you're doing inside that callback, then you need to modify the struct to reflect the new data, modify your callback to reflect the new struct, and modify the callback to reflect the new procedure. Like I said, using callbacks/signals and slots is more work than it's worth. Why spend the time binding data into a completely different scope just to alter it? Why? axilmar said: Your system is good, but signals and slots is alot more simpler. Did you even read my post? What's simpler than this? if (wmsg == WidgetMsg(&button1 , TOPIC_BUTTON , BUTTON_CLICKED)) { do_anything_here(); } Give me an example of how using signals and slots or callbacks is simpler than what I just showed you. axilmar said: However, in C, things are different.Since C lacks any kind of templates or lambdas, handling events from the main event queue makes sense, because it allows access to the program context. At least we agree on this. axilmar said: But there are cases where the event queue cannot be used. For example, when a text is inserted in a textbox, the usual approach is to invoke a user callback that validates the insertion. I cannot see how this will work with the event queue. This is one of the few times that an immediate response is necessary and should bypass a message system. However, the widget could be designed to validate the data itself as well. 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: What's simpler than this? This is a lot simpler (in c++): button.click += slot(myFunction, myData1, myData2); EDIT: Added the following things in the library:
I modified the test skin to load resources for the display widget. The example contains many changes:
Here is an image: {"name":"YDchK.png","src":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/5\/7\/5725c68df582c50869db47eccef7ecd8.png","w":652,"h":518,"tn":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/5\/7\/5725c68df582c50869db47eccef7ecd8"} The background is loaded from the skin. |
AMCerasoli
Member #11,955
May 2010
|
Cheese!! I want it! I want it! I can't manage to install the library! Can I compile the library without using pkg-config? I downloaded the GTK+ binaries so now I have pkg-config but still don't working... All-in-one bundles
SiegeLord said: Those are files used for pkg-config program. I don't think they are made anywhere else except in Linux. Just ditch the Makefile and compile it using gcc. I don't know how makefile works
|
Thomas Fjellstrom
Member #476
June 2000
|
AMCERASOLI said: I downloaded the GTK+ binaries so now I have pkg-config but still don't working... Why would you need or want GTK? I'm confused. Quote: I don't know how makefile works You don't need to know how make works. -- |
SiegeLord
Member #7,827
October 2006
|
By ditching the Makefile I meant deleting it from your system with extreme prejudice. "For in much wisdom is much grief: and he that increases knowledge increases sorrow."-Ecclesiastes 1:18 |
AMCerasoli
Member #11,955
May 2010
|
Well first I saw this: Package allegro-5.1 was not found in the pkg-config search path. Perhaps you should add the directory containing `allegro-5.1.pc' to the PKG_CONFIG_PATH environment variable Then I saw this axilmar MAKEFILE said: LIBS = `pkg-config --libs allegro-5.1 allegro_font-5.1 allegro_image-5.1 allegro_primitives-5.1 allegro_ttf-5.1` etc... ${OBJDIR}/_main.o: _main.c ${OBJDIR}/algui_list.o: ${SRCDIR}/algui_list.c ${OBJDIR}/algui_rect.o: ${SRCDIR}/algui_rect.c ${OBJDIR}/algui_tree.o: ${SRCDIR}/algui_tree.c ${OBJDIR}/algui_widget.o: ${SRCDIR}/algui_widget.c Then went to Google an read this: Quote: pkg-config is computer software that provides a unified interface for querying installed libraries for the purpose of compiling software from its source code. pkg-config was originally designed for Linux but is now also available for the various BSDs, Microsoft Windows, Mac OS X, and Solaris. And thought... "Oh I need pkg-config", still reading and found out that came with GTK+ A library to create GUI, And thought "yhea make sense" and I download it... And now I'm here... Is something wrong in my brain algorithm?
|
Thomas Fjellstrom
Member #476
June 2000
|
AMCERASOLI said: Is something wrong in my brain algorithm? Yes.. Instead of trying to fetch GTK to get pkg-config, you could have just grabbed pkg-config, or edited out the pkg-config bits and replaced then with the right -lblah strings. Or manually compiled the code at the command line. -- |
Edgar Reynaldo
Major Reynaldo
May 2007
|
axilmar said: This is a lot simpler (in c++): button.click += slot(myFunction, myData1, myData2);
You forgot all the code that goes with it : 1class myData1 {
2public :
3 int* int1;
4 double* double1;
5 myData1(int* int_ptr , double* double_ptr) : int1(int_ptr) , double1(double_ptr) {}
6}
7
8class myData2 {
9public :
10 bool* bool1;
11 Object* object1;
12 myData2(bool* bool_ptr , Object* object_ptr) : bool1(bool_ptr) , object1(object_ptr) {}
13}
14
15void MySpecialFunction(myData1* md1 , myData2* md2);
16
17// in main
18int special_int;
19double special_double;
20bool special_bool;
21Object special_object;
22
23myData1 md1(&special_int , &special_double);
24myData2 md2(&special_bool , &special_object);
25
26button.click += slot(MySpecialFunction , &md1 , &md2);
27
28// after main
29void MySpecialFunction(myData1* md1 , myData2* md2) {
30 if (*(md2->bool1)) {
31 md2->object1->DoStuff(*(md1->int1) , *(md1->double1));
32 }
33}
Contrast that with this : // in main int special_int; double special_double; bool special_bool; Object special_object; // in logic loop if (wmsg == WidgetMsg(&button1 , TOPIC_BUTTON , BUTTON_CLICKED)) { if (special_bool) { special_object.DoStuff(special_int , special_double); } } So by using a message system, you saved yourself the trouble of writing two classes, initializing two objects, writing a special function just to deal with that data, and setting up a slot. If you want to modify the behaviour of your button, it's much easier to modify the code inside an if (wmsg ==) clause rather than modifying your classes, modifying your class instances, modifying your special function and resetting the slot to reflect the new function and data. The whole point of writing a function is to either modularize code, or to make reusable code. Functions that use special classes holding data specific to your program will almost never be reusable, so the only reason left is to modularize code. The problem is, code inside an if clause is just as modular as code inside a function, so there's really no point in making up a function, classes, extra object instances just to refer to data, and signals and slots to connect local data with your function. So you tell me which is simpler. Like I said before, why bind local data to another scope to deal with it there when you could have just dealt with it locally to begin with? 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, why did you go to all that trouble? you could have simply written this: button->click += slot(bind(my_function, special_int, special_double, special_bool, ref(special_object)));
Edgar Reynaldo said: why bind local data to another scope to deal with it there when you could have just dealt with it locally to begin with? Signals and slots are a lot more simpler and better than handling events in a queue:
|
Edgar Reynaldo
Major Reynaldo
May 2007
|
axilmar said: Edgar, why did you go to all that trouble? you could have simply written this: Pretend what I wrote was in C. How else would you have done it then? axilmar said:
button->click += slot(bind(my_function, special_int, special_double, special_bool, ref(special_object)));
And how are you going to recreate that same functionality in your GUI library using C? axilmar said: Lots of stuff about signals and slots... But you're using C. How will you recreate signals and slots in C? 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
|
SiegeLord said: Comments about the build system:
As was pointed out, the Makefile was contributed by me. If you don't like the Makefile there then fix it or replace it with something better. That's the best that I know how to do right now, but it's better than plain bash scripts or manually typing commands every time. As for the Allegro 5.1 usage, I honestly don't know anything about it... All I know is that at some point somebody (probably in IRC) pointed out 5.1 (or maybe I just saw the branch in Subversion) and I started using it. I have no objections to updating the Makefile to use 5.0 (I'd do it now, but I'm in Windows still ripping CDs). AMCERASOLI said: I'm getting this: *snip* I'm using MSYS The Makefile was written in Linux (Fedora 13). It's not very robust and not platform-independent. The good news is that it's open source so go ahead and fix it and contribute your fixes. Or if you don't care about a build system and just want to build it, compile it manually; on the command-line or using your favorite IDE. The pkg-config system is used in Linux to fill in the link libraries in a pretty way. Allegro 4 did something similar with allegro-config, but again, I think that was Linux only. Have you considered switching to Linux? -- 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
|
Edgar, C is a different language than C++. We were talking about C++. Whatever applies to C++ does not apply to C and vice versa. For now, I am thinking about a simple system: callbacks. Each widget will have one or more callbacks for its 'events'. People that like events can always install callbacks that post events to the event queue. I prefer callbacks because there is a more universal solution than events. I like my library to be consistent. EDIT: Version 0.0.0.8 is available. I changed the internals of the skin and resource manager APIs to use UTF-8. Now everything uses UTF-8. |
|
|