Hi there,
Since you three have some experience of modifying the Allegro GUI I was wondering how you would go about designing a new GUI system? One that's more flexible and skinnable..
Thought I'd better get some ideas before I jump in at the deep end!
Just download their GUI's and look at the source code. I haven't tried out all tree of them but I know that MASKinG comes with docs explaining the interface in great detail. Copy & paste!
I was thinking more of listening to their ideas after they have done their libs. Most of us realise the mistakes we've made in design after we've done something!
But, you do have a good point there amarillion..
Try to do write a program using Java SWING. Although it is highly critisized for being very slow, I think it has a very nice, neat structure. For example, the way events are handled or the way data is separated from widgets.
Can you explain more please?
Ok, let's startr simple: You should use an OO approach. I'll use C++, you can of course use C aswell (but using C++ in the examples will make the examples more readable).
The first think you should realize is that every widget (UI Element) should be eable to draw itself, and to deal with any events itself.
This leads to the point, that sombody or something needs to tell the widgets that an event has happened. Let's call this thing the EventManager.
What the EventManger does is quite simple. Every time he is called (Let's assume there is an EventMessagner::update() method, or something like this) it checks where the mouse is. It then tries to find the widget at this position (if any).
Usually, you'll check if teh mouse has moved at all first, then if the mouse is still within the widget it has been in before.
Once the widget the mouse is in is found, you generate an according event (mouseMoved, mouseOut, mouseEnter, mouseDrag, etc).
If the mouse button status has changed, you signal this is well (mouseButtonPressed, mouseButtonReleased).
These methods might create repaint events which are stored in the EventManagers queue. So, after doing it's normal stuff, it will work on this list as well. And calling the update method of the widgets in question.
In order to make painting easy, every widget should only get a sub bitmap of the bitmap everything is drawn on. So, you can always assume that (0,0) is the upper left corner of the widget whichs paint() method is called.
Depending on your intention, you might want to use something similar to a graphics context, to store the bitmap to work on, the current font, foreground and background colors, etc.
Another key point is that your widgets should be containers. This means they should be able to contain other widgets.
So, if you have a textbox, a button and a pop-up menu you should be able to code a combo box simply by combining these elements.
A spinner would be a textfield an two buttons (up/down).
Once you have a simple widget, and the EventManager writing the widget itself is pretty simple.
In theory, your widget class could look like this:
1 | class Widget { |
2 | vector<Widget*> childs; |
3 | Widget* parent; |
4 | |
5 | int state; |
6 | Rect bounds; |
7 | |
8 | protected: |
9 | virtual void handleMouseEvent(MouseEvent e); |
10 | virtual void handleKeyboardEvent(KeyboardEvent e); |
11 | virtual void handleJoystickEvent(JoystickEvent e); |
12 | |
13 | virtual void repaintChilds(GraphicsContext *g); |
14 | virtual void repaintSelf(GraphicsContext *g); |
15 | |
16 | public: |
17 | Widget(); |
18 | virtual ~Widget(); |
19 | virtual void repaint(GraphicsContext *g); |
20 | virtual int getState(); |
21 | virtual void setState(int state); |
22 | |
23 | virtual void setLocation(Point *p); |
24 | virtual void setLocation(int x, int y); |
25 | virtual void setBounds(Rectangle *rect); |
26 | virtual void setBounds(int x, int y, int w, int h); |
27 | virtual void setSize(int w, int h); |
28 | virtual void setSize(Dimension *size); |
29 | |
30 | virtual void setVisible(bool visible); |
31 | virtual bool isVisible(); |
32 | |
33 | virtual int add(Widget* child); |
34 | virtual Widget *getChildAt(int index); |
35 | virtual int getChildCount(); |
36 | virtual Widget *getWidgetAtLocation(int x, int y); |
37 | |
38 | }; |
The getWidgetAtLocation() checks if one of the child widgets is at the given position. If so, it calls the getWidgetAtLocation() of the corresponding child widget. If there's no child at this position, it returns the this pointer.
So, your EventManager class can call the getWidgetAtLocation() method to get the widget under the mouse (Some complex widgets, say a combobox, might not call the child class, but simply always return this).
And yes, this is of course simplified.
Hope it helps anyway...
Well I did write some docs for my lib and went over a few of the subsystems in depth. I can safely say I've made many mistakes while making my GUI, but there is really no right or wrong way, just a matter of preference. For instance at one point I had a texture field for all of my skins so you could have one texture used for multiple things, later out of preference I changed it so that each object has a different texture name, this cut down the number of config lines in the config files, but in some instances caused skins to bloat a bit because two textures may look identical, but require different names. If you really want to know how to make the allegro GUI more flexible download my source and look at some of the things I did, also looking at the docs and the "making procedures" section should explain some of the subsystems I had to create to handle stuff. The most important subsystem I had to create so far was the memory manager, without that I would be extremely limited in how flexible I could make the GUI, with it however there is almost no limit. In fact all of the subsystems I use for my GUI are completely portable to any other GUI and are not tied into NAS GUI at all. Of course you are free to extend any part of my GUI as you wish. I'm looking for someone who is willing to extend it to allow for multiple overlapping dialogs and menus. After that all I have to do is finish off the few remaining procs and it'll be nearly complete.
Here's a simple example of how my GUI works:
1 | //Draws a bitmap onto a dialog box |
2 | int nas_bitmap_proc(int msg, DIALOG *d, int c){ |
3 | BITMAP *wnd; //window bitmap |
4 | BITMAP *b = (BITMAP *)d->dp; //bitmap image |
5 | int x, y; |
6 | //Returns relative coordinates since we are not drawing to the screen, we have to get the relative coordinates of the procedure in relation to the main dialog window |
7 | get_rel_coords(d, &x, &y); |
8 | if (msg == MSG_DRAW){ |
9 | wnd = get_window(); // Get the window bitmap |
10 | // Smart updating, only the bitmap portion of the screen will be updated |
11 | invalidate_rect(d -> x, d -> y, d -> w, d -> h); |
12 | // Draw our bitmap using any one of the four drawing modes |
13 | switch(d -> d1){ |
14 | case NAS_BITMAP_BLIT: |
15 | blit(b, wnd, 0, 0, x, y, d -> w, d -> h); |
16 | break; |
17 | case NAS_BITMAP_MASKED_BLIT: |
18 | masked_blit(b, wnd, 0, 0, x, y, d -> w, d ->h); |
19 | break; |
20 | case NAS_BITMAP_STRETCH_BLIT: |
21 | stretch_blit(b, wnd, 0, 0, b -> w, b -> h, x, y, d -> w, d -> h); |
22 | break; |
23 | case NAS_BITMAP_MASKED_STRETCH_BLIT: |
24 | masked_stretch_blit(b, wnd, 0, 0, b -> w, b -> h, x, y, d -> w, d -> h); |
25 | break; |
26 | } |
27 | } |
28 | return D_O_K; |
29 | } |
Simple no? We don't even have to worry about handling the mouse as hiding it and such because that is handled by the dirty rectangle system.
Here's one using the memory manager:
1 | // Displays a progress bar |
2 | int nas_progress_proc(int msg, DIALOG *d, int c){ |
3 | BITMAP *wnd; |
4 | int *state; |
5 | int ret = D_O_K; |
6 | int x1, y1, col; |
7 | int (*proc)(int d2value); |
8 | int newpos; |
9 | int texty = ((d -> h - progressbar.tpadding - progressbar.epadding) >> 1) - (nasfont_text_height(AL_font, NASFONT_NORMAL) >> 1) + progressbar.tpadding; |
10 | int width = d -> w * (d -> d2/(float)d -> d1); |
11 | int x, y, w, h, cl, cr, ct, cb; |
12 | if (width > d -> w) |
13 | width = d -> w; |
14 | if (width < 0) |
15 | width = 0; |
16 | get_rel_coords(d, &x1, &y1); |
17 | // Retrieve our state variable from memory (returns NULL at first since it doesn't exist) |
18 | state = get_mem(d, MEM_STATES); |
19 | switch(msg){ |
20 | case MSG_START: |
21 | // Two states: |
22 | // State[0] - set to mouse state |
23 | // State[1] - set to last clicked position |
24 | // Create our state memory segment to hold two integers. |
25 | state = (int *)create_mem(d, MEM_STATES, (int *)calloc(MEM_STATES, sizeof(int) * 2), MEM_ALLOCATED); |
26 | break; |
27 | case MSG_DRAW: |
28 | // Get our window again |
29 | wnd = get_window(); |
30 | // Update the DRS system |
31 | invalidate_rect(d -> x, d -> y, d -> w, d -> h); |
32 | // Draw the progress box |
33 | drawskinnedrect(wnd, &progressbar.box[0], x1, y1, x1 + d -> w, y1 + d -> h); |
34 | drawskinnedrect(wnd, &progressbar.box[1], x1, y1, x1 + width, y1 + d -> h); |
35 | if(d -> dp){ |
36 | if(d -> flags & D_DISABLED) |
37 | col = 2; |
38 | else |
39 | col = 0; |
40 | if(wnd -> clip){ |
41 | cl = wnd -> cl; |
42 | cr = wnd -> cr; |
43 | ct = wnd -> ct; |
44 | cb = wnd -> cb; |
45 | } |
46 | else{ |
47 | cl = ct = 0; |
48 | cr = wnd -> w; |
49 | cb = wnd -> h; |
50 | } |
51 | x = x1 + progressbar.lpadding; |
52 | y = y1 + progressbar.tpadding; |
53 | w = MIN(x1 + d -> w - progressbar.rpadding, x1 + width - progressbar.spacing) - 1; |
54 | h = y1 + d -> h - progressbar.epadding - 1; |
55 | set_clip(wnd, x, y, w, h); |
56 | nasfont_textout_shadow(wnd, AL_font, d -> dp, x1 + (d -> w >> 1), y1 + texty, progressbar.textcol[col], NASFONT_CENTERED); |
57 | set_clip(wnd, cl, ct, cr, cb); |
58 | |
59 | if(d -> flags & D_DISABLED) |
60 | col = 2; |
61 | else |
62 | col = 1; |
63 | if(wnd -> clip){ |
64 | cl = wnd -> cl; |
65 | cr = wnd -> cr; |
66 | ct = wnd -> ct; |
67 | cb = wnd -> cb; |
68 | } |
69 | else{ |
70 | cl = ct = 0; |
71 | cr = wnd -> w; |
72 | cb = wnd -> h; |
73 | } |
74 | x = x1 + width - progressbar.spacing; |
75 | y = y1 + progressbar.tpadding; |
76 | w = x1 + d -> w - progressbar.rpadding - 1; |
77 | h = y1 + d -> h - progressbar.epadding - 1; |
78 | set_clip(wnd, x, y, w, h); |
79 | nasfont_textout_shadow(wnd, AL_font, d -> dp, x1 + (d -> w >> 1), y1 + texty, progressbar.textcol[col], NASFONT_CENTERED); |
80 | set_clip(wnd, cl, ct, cr, cb); |
81 | } |
82 | break; |
83 | case MSG_LPRESS: |
84 | if(!(state[0] & BUTTON_FOCUS)) |
85 | state[0] ^= BUTTON_FOCUS; |
86 | break; |
87 | case MSG_LRELEASE: |
88 | if(state[0] & BUTTON_FOCUS) |
89 | state[0] ^= BUTTON_FOCUS; |
90 | break; |
91 | case MSG_IDLE: |
92 | if(!(mouse_b & 1) && (state[0] & BUTTON_FOCUS) && !(mouse_x >= d -> x && mouse_x < d -> x + d -> w && mouse_y >= d -> y && mouse_y < d -> y + d -> h)) |
93 | state[0] ^= BUTTON_FOCUS; |
94 | if(d -> dp2 && (state[0] & BUTTON_FOCUS)){ |
95 | newpos = d -> d1*((mouse_x - d -> x)/(float)d -> w); |
96 | if(state[1] != newpos && newpos >= 0 && newpos <= d -> x + d -> w){ |
97 | proc = d -> dp2; |
98 | ret |= (*proc)(newpos); |
99 | nas_object_message(d, MSG_DRAW, 0); |
100 | state[1] = d -> d1*((mouse_x - d -> x)/(float)d -> w); |
101 | } |
102 | |
103 | } |
104 | if(d -> dp3 && !(d -> flags & D_DISABLED)){ |
105 | proc = d -> dp3; |
106 | ret |= (*proc)(d -> d2); |
107 | } |
108 | break; |
109 | case MSG_END: |
110 | // Destroy the memory segment |
111 | destroy_mem(d, MEM_STATES); |
112 | break; |
113 | } |
114 | return ret; |
115 | } |
This code is entirely non-blocking as well since it uses no while loops. This is because the memory manager allows me to store a little bit of extra information per procedure like the states variable. This allows me to retirive the last mouse clicked position and some other useful information like a status flag telling me that it has been clicked, or the mouse was held down outside of the procedure, etc. Because of this system I can store any type of data and retrieve it per procedure as needed.. pretty nifty right;D
So pretty much the same way as Allegro handles it then?
Is this really how all modern GUI's handle things?
EDIT: Saw Steve's post after this reply. Thanks Steve that looks very useful. I'm still not clear on why you need a memory manager though? Can you explain it to me a bit more? (sorry I'm a newbie on GUI stuff!)
Cheers!
Rich
Qt and Swing both use a signals/slots mechanism, AFAIK - someone explained this in another thread.
I wrote a GUI (remember that ugly horror? ). If I were going to write an Allegro GUI, I'd probably ape the crap out of Win32 since it's all I know. Are any of the Allegro GUI's at a good useable state? I keep forgetting; there's too many
The memory manager is not really needed as you could store teh extra information in any one of the free dp* fields, this however would clutter up the GUI since you'd have to create structures or something to hold multiple types of data in one pointer. This manager allows flexibility without using any dp* fields. In fact I could rewrite the DIALOG structure to not have any dp fields at all and just call helper functions to set callbacks, etc. because those procs could retirieve that information from the memory banks. It just makes the API a lot prettier
It's also a very simple manager, three basic commands, create, get, destroy...
Signal / Slot is also calles Listener and/or Subscriber.
It works like this: You're telling the Widget what events you're interested in, and pass an instance of yourself. Whenever the event occurs, you will be called.
Edit:
So pretty much the same way as Allegro handles it then?
Um no.
Allegro has no concept of containers. There's no z-order as well. Allegro does not abstract the graphics context. And allegro is blocking.
My GUI lacks z-ordering but as soon as I impliment multiple dialogs that will be there. So far I've managed to abstract the graphics context and made non-blocking procedures, not so sure about containers or how I could do that...
[edit]
Richard.. if it makes more sense I need the memory manager to "remember" stuff outside of the procedure. If I made it a local variable then the procedure would "forget" what it was set to each time it returned. This way I can have separate unique variables for each procedure. It also comes in handy for the dialogs because get_window uses the memory manager to get the window bitmap from the main dialog procedure. Sorta like shared memory, all the procedures under that dialog have access to the main dialog's nas_dialog_proc window.
[/edit]
Attaching on to spellcaster's post:
Note the distinction between Events and Signals. Events are from the event manager to the widgets (e.g. "repaint yourself", "the user pushed the left mouse button"). Signals are from the widget to the application (e.g. "hey, I got clicked, time for you to do something useful").
This is the terminology used by GTK. Other toolkits [should] have the same separation, if under different names. Otherwise, you end up with a mess like the Allegro GUI.
I've noticed several people (including myself) are working on their own GUIs instead of using a pre-existing GUI. Is this because they feel more comfortable using a GUI they completely understand rather than an off-the-shelf GUI, or do they simply find Allegro's GUI to be a bit limited, or do they want to have complete control of it and customise it any way they want? I chose to write my own GUI in my own games for a mixture of these three reasons.
AE.
not only is allegro's gui limiting. but its down-right ugly.
Which reminds me, an Allegro theme for my GUI would be neat for those who want some nastalgia. Hmm completely black and white buttons and dialogs... fun fun;D
You should call it GEM though, because that's what Allegro is supposed to look like, afaik.
Well, while working on your own GUI is a lot of work I think it's something that I may have to do at some point. However I really should look at the GUI Libs people have done using Allegro and see if I can adapt them.. (Those that are open source of course).
So that's why I was interested to seeing the different design approaches GUI's use.
EDIT: GEM (or Graphical Enviroment Manager) was part of the GUI system used on the Atari ST. Pretty soon it looked horribly dated, but that black and white style looks quite retro and attractive recently!
I would just like to add that I based my GUI on a library called DeGUI which was basically a C++ wrapper for the Allegro GUI (that was in the times of Allegro 3.12). The author of DeGUI abandoned it and I picked up on it improving it and adding extra functionality (support for skins etc.) While it's still incomplete and a bit on the buggy side, it does support more or less everything spellcaster said up there except for that z sorting thingy which I don't even know what it is. Or if it is what I think it might be then I have that too to some extent.
Anyway, while working on the lib I discovered it's NOT a good idea to make a GUI based on the Allegro GUI code. You will be much better off writing it completely from scratch using spellcaster's code as a starting point. If you decide to make your own GUI I suggest you study a good C++ book very carefully before you start. I was almost a complete newbie in C++ a year ago and that shows in my code. Since then I learned a lot and a while ago I was even tempted to rewrite my lib from scratch too but then I realized it would be too much work for too little benefit and I just quit. I am planning to make major improvements to the lib in the summer though (summer starts in july here)...
Hmmm.. The problem here is that I don't know C++ (I did try to learn). I know C++ would be a better choice for doing a GUI, but for some reason I just don't like it.
I have done some tinkering with the Allegro GUI routines for the interface in Stylepaint. But I find some aspects of it very restricitng.
Oh, I thought that LexGui was based around simply adding skin support to the Allegro GUI. Or have I done Lenny an injustice?
Yeah, LexGUI is just an extension of the default Allegro GUI (as far as I can see all the widgets are "derived" from the default ones and they only "overload" the MSG_DRAW message)...
I've used a lot of Java Swing and only a very brief peppering of Win32 code, but I do know that I love Java Swing. Looking at spellcaster's code that's a really good way to do it, so listen to spellcaster . Signal / slots is awesome, although I've never heard that term except on this fourm. I thought the Java Swing followed MVC (Model-View-Controller). But its event system is subscription based and I love it. I like those concepts so much that I followed them very greatly when I designed my GNE library.
I think GUIs is the the best or very close to the best example case for object-oriented programming and one of the best cases for mutli-threaded programming. GUIs are very suited for a lot of really cool programming concepts that I like .
Lenny, apparantly I've got to listen to you..
Using plain C how would you do a GUI system? Or would you simply modify allegro's? (That question is for everyone else too..)
As I already said, I don't suggest just modifying the Allegro GUI. You'll get results sooner that way but in the long run you'll regret it. If you do want to go down that road then why don't you modify LexGUI instead? Spellcaster obviously stopped working on it a long time ago...
Unfortunately I think you are right Miran. It's just making a GUI from scratch when you've not made one before seems rather daunting..:-/
not only is allegro's gui limiting. but its down-right ugly.
You're free to change the drawing routine to anything you like.
Yeah, LexGUI is just an extension of the default Allegro GUI (as far as I can see all the widgets are "derived" from the default ones and they only "overload" the MSG_DRAW message)...
Yup. I tried to reuse most of the code. But some of the functionality is not changeable easily (without duplicating most of the code, and then changing small parts of it) so this project died ... more or less.
I've used a lot of Java Swing and only a very brief peppering of Win32 code, but I do know that I love Java Swing.
AT my last job, my main job was designing Java widget sets. Some of them where close to Swing, some others were not that close to swing and employing some other (strange) libs like HAVI.
I also did a lot of work with swing.
And yes, I think the swing design is neat.
Using plain C how would you do a GUI system? Or would you simply modify allegro's? (That question is for everyone else too..)
Allegro's main drawback is the missing container hierarchy. It's theone that will you bite sooner or later in the gluteus maximus.
So, I'd start from scratch.
And if I had to do it in C, I'd simulate C++ by giving each struct a VTABLE, and accessing functions only via that table.
But that's not very clever way (IMO), since you#re doing work others have done already for you (the g++ team) and chances are good that they did do a better job
So, I'd suggest that you use C++ instead.
While you can do everything C++ can do with C, doing it with C will normally be more easy
Also, if you know how to pull these kind of stunts in C, you can code C++ already... and if you don't know how to do these stunts, explaining it wil take longer than migrating from C to C++.
You're free to change the drawing routine to anything you like.
if i plan to ever use the allegro gui i most likely will.
And the fun thing is: it's quite easy to do
With a few lines of code, the gui will really look nice (or let's say: way nicer)
In combination with DLG, the gui can cut your delevopment time (so use it for tools, but avoid it for games - or at least use it with care )
It's frustrating when you know your skills can't match your vision for a program. Is a vtable a list of function pointers?
Too much I don't know.. Oh well, probably still going to try anyway!
Learn C++.
Will look good in your CV, and it's always fun to learn something new
Richard casts "BAH!" spell on Spellcaster for +4 damage.
Spellcaster casts "newbie" on Richard for +8 confusion.
Confused Richard says:
"Don't want to learn no yucking C++ "
Richard goes to bed!
Um.. Pauls mailed me some pretty good texts on OO in C, I'm quite sure he can mail them to you as well.
But I still think it would be better to have a look at C++.
Go to [url http://www.bruceeckel.com/] and order/download some books. I especially recommend Thinking in C++ (both volumes)...
But Spellcaster's LexGUI is just an extension of the Allegro GUI, nothing more, mine was based off of LexGUI but took it a step further by adding DRS support, mouse extensions, font extensions, non-blocking procedures, etc. The Allegro GUI way of doing things isn't neccessarily bad, though it has bugs (i.e. Double clicking is horribly implimented and fails in most cases, try holding the mouse button down and quicky pressing again.. that should NOT be a double click )
[edit]
May I ask how it would be possible to allow for container functionality using my GUI, I never thought about containers or knew about them until this thread popped up. I could just call another proc from within a proc like teh allegro GUI does, but you'd have to have special handlign of overlapping portions, etc.
[/edit]
At first, you'd have to implement something like a relative coordinate system.
Then you'll need functions to translate between the systems, and also functions to clip rects (imagine the case when a child is partly outside its parent container).
Then you need to ensure that no widget can draw outside it's visible area. If everything worked in step one and two, this should not be that problematic.
I had a look at the changes needed and decided that it's not worth the effort.
If I ever start something similar again, I would start from scratch.
Yeah I know what you mean, at least dialog boxes are separate containers in my GUI, nothing can draw outside of them because they are separate bitmaps. The relative system I have going though is kind of a hack, the structure takes absolute coordinates, i.e. dialog.x is 100, button1.x is 120, and teh button appears 20 pixels inside of teh dialog box, however when a proc goes to draw itself it must translate it's position relative to the dilaog box position since it's drawing to it's bitmap surface. This surface can be clipped to inside of it's borders as well so that when you resize the sides overlap it's contents. Maybe what we need is a simple base system to base GUI's off of, something that allows for flexibility than the allegro GUI, and to build off of that. Either way though, I've come too far with my GUI to back down... I'll see how far I can take this thing before I can absolutely go no further
Sounds like everyone's pretty much agreed that rather than adapting the existing allegro GUI I should write a new one..
Curses!
You Fiends! Do you know how long that will take me ?! hehe..
depends what you want.
If you really want a full fledged gui, yep, writing a new one would be the way to go.
You should start by listing all widgets / features you want. Depending on this list it migh be possible to use allegro's GUI functions as a framework.
If you take a look at "Plastic Animation Paper" for example, or at some other programs like the old spaint from crack dot com... something like this could be done easily using allegro as backbone.
It's always a question of what you need
Just for teh curious and for Richard, I copied and pasted much of my programs nas_do_dialog and nas_update_player from teh allegro source, but since it's already separated from Allegro it should be easier to customize to your own needs. Which means I need to start rewriting sections of it myself, but at least I have it set up so I can do so.
It's difficult to write a good GUI. Not that the main functionalities are difficult to implement, but there are a lot of things involved:
1) events should be queued. It's frustrating to click buttons but the GUI ignores them because it did something else than polling events at that time.
2) geometry management. A hot subject. If you look at commercial stuff, you will see lots of code for it. But, in a good gui, the programmer shall not position widgets at absolute positions.
3) skinning. Ok, that's fairly easy. But it involves careful planning of what resources each widget needs.
4) the Allegro does not have a paint origin: every coordinate passed in a drawing function is relative to 0, 0. But widgets need to draw into themselves, using their topleft corner as 0, 0.
5) drag-n-drop. Sooner or later, you will need widgets to talk between themselves and negotiate data transfers.
6) clipboard. Nuff-said. What GUI program is useful without a clipboard ?
7) Basic widgets like buttons are very easy to implement. But the most interesting widgets take big amounts of effort to get it right: trees, toolbars, grids, tables, etc. And you need to have toolbars in a modern gui. Have you ever wondered why Allegro carefully avoids more complex widgets ?
8) file associations. File dialogs need to know this as well as the icons for each registered file type.
9) text output. Allegro routines can't draw partial strings, and you need to make some hacking in order to properly display text.
10) a good GUI needs truetype fonts. Allegro does not have true type fonts. Although Alfont is an excellent library, it does not have routines for drawing underlined "&" characters. More hacking here.
11) the event model: how to raise windows when their controls are clicked. Windows knows when a window is toplevel, and it is raised automatically by the system when a child is clicked. But that is not valid in your gui, since you want to make a general purpose gui lib. So you have to come up with a system where events are bubbled up or propagated down...but its one has its own problems.
12) keyboard management: keyboard shortcuts, hot keys, etc. When pressing alt, Allegro gives you a number from 1 to 26 for each English letter. But that does not tell you what the character was, so good luck with implementing hot keys (not that it can't be done, though).
13) there is a long list of complementary classes that you need to code in order to make a gui. Take trees for example: widgets are trees, and tree items within a tree view are also trees. So, you need a template class that implements trees. You also need smart pointers and reference counting for managing resources (skin objects shared by widgets, for example). You also need to make your own string class that manages Allegro unicode strings. And don't forget those template classes for signals with 0, 1, 2 or more parameters.
14) how about an IDE ? it is really boring to write guis by hand. But then your classes would need reflection, i.e. to have the ability to manage widgets without knowing the class: just by using a predefined interface. IDEs are written that way. But good reflection can take ages to write, for each widget class.
15) you would also need a mouse cursor factory, since you can't have one bitmap for all video modes. That means routines for remapping bitmaps from one color depth to another, taking care of the mask pixel values (blit does not do that, does it ?). You should also code a routine that draws bitmaps disabled, i.e. with raised or sunken outline.
16) you need to make a smart architecture about the application's actions, since many elements will call the same action. For example, the save menu command shall be disabled when the document is already saved; but the toolbar save icon should also be disabled in such a case. These two must be linked somehow, otherwise the programmer will have to explicitely disable all related resources.
I've done more than 50 tries to write a good gui. I've written a Window system (Mx-Windows, if you are interested), but after finding how region clipping works, the rest was easy. But a gui toolkit..., that's a completely different thing. I've bitten myself too many times, thinking I've got a great design, then started adding virtual functions all over the place, only to come up with a mess, which I delete afterwards.
And since a GUI can be so large, either be prepared for a long development time, or get your friends to help you.
(Or we could just assemble a team of coders: I could write the basic functionality (from group feedback, of course), the most basic classes, then each one of the group, including me, we would implement one or two widgets. For example, if we had 10 programmers, we could easily have 50 widgets in two weeks time: each programmer would make 5 widgets in two weeks.)
I think a proper fully-fledged GUI is the most difficult thing to write. That's why so few toolkits got it right: Qt, Swing, Photon (maybe there are other good things out there, I don't know all). But the fact is that most gui libs are limited or flawed.
Thanks for that post. I'm going to give your comments added value since you wrote mx-windows.
If you've wrote many GUI attempts, then have you decided which design works best of all? Or is there no such thing?
I can see how a GUI is going to be a lot of work. It's unfortunate as the Allegro GUI just isn't good enough for me. I'm also not sure about adapting any existing GUI library after some of the earlier suggestions in this thread to start from scratch..
Hmmm...
mx-windows just plain rocks. I started writing a themeable widget set onto of (a slightly modified/extended) mx-windows, and since mx-windows can do shaped windows, you get shaped themed windows
really nice if you ask me.
If you've wrote many GUI attempts, then have you decided which design works best of all? Or is there no such thing?
I can't really say which approach works best. When writting a small library like MxWindows, it is quite easy to stay focused. But a full gui library, I don't know...If I knew, I would have made one, wouldn't I ?
I can see how a GUI is going to be a lot of work.
This is solvable: we need cooperation.
It's unfortunate as the Allegro GUI just isn't good enough for me.
And for me, also. I imagine for many others, as well.
So, guys, Richard, Thomas, Steve Terry, Miran, Spellcaster, I have a question for you: why don't we join forces and produce the Allegro's ultimate GUI ? we can have a short period discussing the main issues, then each one of us can take the responsibility for producing a number of widgets. Since widgets are independent of each other, each one of us can take his/her own time and do as good as job as needed. I can make the framework classes for you to get started with!!!
This would be awesome. For tools and stuff. But Could it be usefull as a generic gui for in game stuff? Maybe config and what not.. Yo know? if the framework is good enough this could seriously rock.
also, Hopefully this'll work. But some widgets will take longer than others
I'm up for it though I think something like this has been attempted before without much success.. a lot of discussion but after that it just died. I know we need cooperation and determination with team effort to even get this thing off the ground and that's if everyone else you mentioned agrees with your statement. We need to decide on how the individual components will be divided up and who will take which tasks etc as well as the language we're going to base this on.. I'm for C since I'm adept with it and it's the language allegro is based on. If you go C++ that's fine but it will lose all possiblility of being integrated into allegro if it's good enough doing away with the hideous GUI forever
I see a couple of problems. First one would be the C vs C++ discussion.
If you ask me, we should go for c++.
I doubt the allegro gui will still be part of allegro for v5, anyway.
Even though, I prefer C, C++ probably is a better option. IMO.
If we use C, we have to implement all the OO stuff ourselfs, so why not use the OO mechanism C++ provides already?
Virtual functions and polymorphism is what makes GUI development easy. Without it, you'll run into problems sooner or later.
I think you are all positive to the idea of making a gui for Allegro. That's a good start. Of course, there is going to be a lot of disagreements, but we can solve them the democratic way: by voting. The minority would have to accept the majority's decision. Of course, any thesis should be backed up by the proper arguments, shouldn't it ?
So, let me present you with my opinion on the programming language: I think C++ is the way to go, for the following reasons:
1) Allegro 5 will not have an intergrated gui.
2) A proper gui lib would be twice as large as Allegro itself. I don't think the main library should be that fat.
3) C++ offers object oriented mechanisms, like encapsulation and polymorphism and inheritance. These features will have to be emulated in plain C, which is a tedious task.
So, for me, the proper language is C++.
Do we agree on C++ ?
.. Agreed
Not agreed
My reasoning is as follows.
It is not possible to use a C++ library in a C program, but a C++ API can be wrapped around a C library.
C is easier to optimise and debug, IMO
My lib is in C, and works fine.
OK, that isn't all exactly objective reasoning, but the first point is hard to argue with.
It is not possible to use a C++ library in a C program, but a C++ API can be wrapped around a C library.
Some of Allegro 5's internals are C++, and it will be useable with C. You can link to non-overloaded static C++ functions from C, AFAIK.
aigh ok, can it be agreed upon then to have some parts of the GUI in C++ as long as there is NO overloading for C backwards compatibility if it must be done so less work is needed. I'm cool with that idea, but yes we must not limit the audience by using strict C++ code because many will be turned away. I mean that IS the main reason I'm attempting my lib in pure C and it's come a long ways... yet I still have a long way to go. Maybe if someone helps me work on my GUI subsystem aka nas.c, gets relative systems going and some way to do menus/multiple dialogs it'd be good to go. I have an excellent mouse system (at least I think so), I have the font system down with both points accomplished (the && system with TTF and partial strings... I clip mine but I think you mean the ... thing which I have yet to hack out), the skin system could use some help, but it works. But again it's probably best to start over since rewriting the subsytem will probably mean I'd have to propegate though all my other functions and apply any needed changes.. which definately takes time.
Hmm the next thing we need to decide on is... what the dialog container, are we gong with a structure like the cough allegro GUI or maybe a linked list structure, I'm not sure exactly what windows or Xwindows does... maybe a win_main() type callback? Just suggestions.
Signals and slots is where its at no WndProc, no callbacks, just signals and slots.
It is not possible to use a C++ library in a C program
Another possible way of doing this is to turn the library into a COM object. This is probably overkill and X-G's method may be sufficient, but this is how DirectX works. When I was at Criterion, our game engine (not Renderware, Dive - the one they use internally) was built entirely out of COM objects. We wrote the objects in C++, but they could easily be used by a C program simply because they were COM objects. The downside is that COM only exists natively on Windows (AFAIK), but at Criterion, we managed to create a working multi-platform implementation of COM (at the time I left, we were getting it to work on the Dreamcast). Even with Windows's native COM, it would require quite a bit to get a working COM engine up and running.
Another suggestion for the GUI is to write it in C++, but use a C++ to C translator so the end-user can build a C version of the library if they're using C. I know that such programs exist. In fact, the first ever C++ 'compiler' was really just a C++ to C translator that passed the output to a C compiler.
I'm writing a GUI as part of Chickens which is being written in C. The GUI is the only part of the code where I think writing it in C++ would have been a lot better. Writing the rest of the game in C feels fine to me.
AE.
You know, both C and C++ are compiled down to machine code by the time you link them anyway - the only problem lies in name mangling, and extern "C" { } will take care of that.
One thing I'll have to note, In the game I'll probably die before it gets done, will use the game engine for the gui.. all scripted and such. Don't know how possible it'll be to use a premade lib for the handling.. probably impossible. Or at the very least improbable.
Well, personally I prefer C++, but if I am a minority, I am willing to accept C as the language of choice. From what I see, C++ looks scary to some people. C is no problem for me.
If we use C, I propose that we do a clever system with classes like the following:
1 | struct OBJECT; |
2 | |
3 | //property descriptor |
4 | typedef struct PROPERTY { |
5 | int id; |
6 | const char *name; |
7 | void (*set)(struct OBJECT *object, const void *value); |
8 | void (*get)(struct const OBJECT *object, void *value); |
9 | } PROPERTY; |
10 | |
11 | //object vtable |
12 | typedef struct VTABLE { |
13 | void *(*get_instance)(struct OBJECT *obj, const char *name); |
14 | } VTABLE; |
15 | |
16 | //an object's class |
17 | typedef struct CLASS { |
18 | const char *name; |
19 | int size; |
20 | CLASS *super; |
21 | void (*constructor)(struct OBJECT *obj); |
22 | void (*destructor)(struct OBJECT *obj); |
23 | VTABLE *vtable; |
24 | PROPERTY *properties; |
25 | } CLASS; |
26 | |
27 | //the object struct |
28 | typedef struct OBJECT { |
29 | CLASS *rclass; //run time class |
30 | } OBJECT; |
31 | |
32 | //function that creates an object |
33 | OBJECT *create_object(CLASS *cl, ...); |
34 | |
35 | //function that destroys an object |
36 | void destroy_object(OBJECT *obj); |
37 | |
38 | //function that sets an object's properties |
39 | void set_object_properties(OBJECT *obj, ...); |
40 | |
41 | //function that retrieves an object's properties |
42 | void get_object_properties(OBJECT *obj, ...); |
So, classes can be declared like this:
1 | //widget struct |
2 | typedef struct WIDGET { |
3 | OBJECT object; |
4 | int x, y, w, h; |
5 | } WIDGET; |
6 | |
7 | |
8 | //widget constructor |
9 | void widget_constructor(OBJECT *obj) |
10 | { |
11 | WIDGET *wgt = (WIDGET *)obj; |
12 | wgt->x = 0; |
13 | wgt->y = 0; |
14 | wgt->w = 0; |
15 | wgt->h = 0; |
16 | } |
17 | |
18 | |
19 | //widget destructor |
20 | void widget_destructor(OBJECT *obj) |
21 | { |
22 | //TODO: remove from parent, destroy children |
23 | } |
24 | |
25 | |
26 | //widget vtable |
27 | typedef struct WIDGET_VTABLE { |
28 | VTABLE object_vtable; |
29 | void (*set_geometry)(WIDGET *wgt, int x, int y, int w, int h); |
30 | } WIDGET_VTABLE; |
31 | |
32 | |
33 | //sets the widget's x property |
34 | void widget_set_x(OBJECT *obj, const void *value) |
35 | { |
36 | WIDGET *wgt = (WIDGET *)obj; |
37 | wgt->x = *(const int *)value; |
38 | } |
39 | |
40 | |
41 | //gets the widget's x property |
42 | void widget_get_x(const OBJECT *obj, void *value) |
43 | { |
44 | const WIDGET *wgt = (const WIDGET *)obj; |
45 | *(int *)value = wgt->x; |
46 | } |
47 | |
48 | |
49 | //widget properties |
50 | PROPERTY widget_properties[] = { |
51 | { PROP_X, "x", widget_set_x, widget_get_x }, |
52 | { PROP_Y, "y", widget_set_y, widget_get_y }, |
53 | { PROP_W, "w", widget_set_w, widget_get_w }, |
54 | { PROP_H, "h", widget_set_h, widget_get_h }, |
55 | { 0 } |
56 | }; |
57 | |
58 | |
59 | //widget class |
60 | CLASS widget_class = { |
61 | "Widget", //this class is named "Widget" |
62 | sizeof(WIDGET), //size of struct |
63 | NULL, //base class |
64 | widget_constructor, //constructor |
65 | widget_destructor, //destructor |
66 | widget_vtable, //widget vtable |
67 | widget_properties //properties |
68 | }; |
69 | |
70 | //usage |
71 | OBJECT *wgt1 = create_object(&widget_class, |
72 | PROP_X, 10, |
73 | PROP_Y, 20, |
74 | PROP_W, 100, |
75 | PROP_H, 500, |
76 | NULL); |
In a few words, this is a C interface for doing object-oriented programming, while at the same time allowing COM-style functionality, run-time object identification, single inheritance, and run-time property management for IDEs.
Inheritance works by embedding the super class instance as the first member of the object. Example:
typedef struct BUTTON { WIDGET widget; char *text; } BUTTON;
In the above example, OBJECT *, WIDGET * and BUTTON * all point to the same object.
We could also generalize the signals & slots/callbacks/object events/whatever in the following way:
1 | //a slot |
2 | typedef struct SLOT { |
3 | NODE node; |
4 | OBJECT *object; |
5 | void (*proc)(struct OBJECT *obj); |
6 | } SLOT; |
7 | |
8 | //a signal |
9 | typedef struct SIGNAL { |
10 | LIST slots; |
11 | } SIGNAL; |
12 | |
13 | //emits a signal |
14 | void emit_signal(SIGNAL *signal) |
15 | { |
16 | for(SLOT *slot = (SLOT *)signal->slots.first; slot; slot = (SLOT *)slot->node.next) { |
17 | slot->proc(slot->object); |
18 | } |
19 | } |
20 | |
21 | //connects a slot to a signal |
22 | void connect_slot(SIGNAL *signal, OBJECT *obj, void (*proc)(OBJECT *obj)) |
23 | { |
24 | SLOT *slot = calloc(1, sizeof(SLOT)); |
25 | slot->object = obj; |
26 | slot->proc = proc; |
27 | append_node(&signal->slots, &slot->node); |
28 | } |
29 | |
30 | //a button with a signal |
31 | typedef struct BUTTON { |
32 | WIDGET widget; |
33 | char *text; |
34 | SIGNAL click; |
35 | } BUTTON; |
36 | |
37 | //a dialog |
38 | typedef struct DIALOG { |
39 | WIDGET widget; |
40 | BUTTON *okButton; |
41 | } DIALOG; |
42 | |
43 | //called when the dialog is accepted |
44 | void dialog_accept(OBJECT *obj) |
45 | { |
46 | DIALOG *dlg = (DIALOG *)obj; |
47 | } |
48 | |
49 | //dialog constructor |
50 | void dialog_constructor(OBJECT *obj) |
51 | { |
52 | DIALOG *dlg = (DIALOG *)obj; |
53 | dlg->okButton = (BUTTON *)create_object(&button_class, |
54 | ID_TEXT, "Ok", |
55 | NULL); |
56 | connect_slot(&dlg->okButton->click, obj, dialog_accept); |
57 | } |
By the way, we we also need a generalized linked list module, as it is obvious from the above example.
P.S.: if you like the above style of programming, let me know. I can finish the module today.
Argh, you've just depressed me Axilmar! You're far far beyond me in your programming skills.
I don't think I'll be much use for this project.. Which is disappointing to me.
EDIT: I think it's the way you use C to do C++ things. I just can't seem to get my head around C++ concepts. Even concepts in C like pointers get me confused..
Any chance you can explain what's happening in your code examples?
RP: You've got to be kidding me, you're totally capable of that In the least, you've got an amazing design/artistic sense.
I'm not that sure if choosing the C approach will result in any postive results for us.
If we go for our own OO implementation, I'd try to add some more features, like the possibility to query for interfaces, etc.
Using aggregation to implement inheritance on that level is not a very good idea, as well.
Ok, after all this nay-saying I should try to come up with some positive suggestions as well, I guess
What about using a hashtable to store methods and attributes? We could even have different tables for private / protected / public members if needed.
But this is just an idea, not sure if it would be practical...
BTW, I still say we should stick with C++.
I see no point in doing a C API.
Unfortunately I'm not kidding...
What's aggregation and what's a hash table?
I don't have a take on whether it's C or C++, I'll leave that to the people who know more about all this than me!
Aggregation, in OO, is what's some times call "has a" relation - when a class contains another object. The alternative is Inheritance - "is a".
Richard:
Argh, you've just depressed me Axilmar! You're far far beyond me in your programming skills.
I don't think I'll be much use for this project.. Which is disappointing to me
Please, don't be depressed. Of course each one of us has different skills, according to our experience. If you think you are not at the same level, it's a good chance for you to learn while giving something back to the community. We are all going to learn many things through cooperation.
Any chance you can explain what's happening in your code examples?
It's easy: a CLASS struct has all information that describes a class. This information is used to create OBJECT structs, i.e. structs that inherit from OBJECT. The CLASS struct is filled by you, the programmer with proper information. The rest is done by the framework which has 4 main functions:
1) a function for creating an object : 'create_object'
2) a function for destroying an object : 'destroy_object'
3) a function for retrieving the properties of an object: 'get_object_properties'
4) a function for setting the properties of an object: 'set_object_properties'
Since the framework knows which properties each object has, it searches the class table to find a property (search is done by the property id), then calls either 'set' or 'get' of the found PROPERTY struct.
Spellcaster:
I'm not that sure if choosing the C approach will result in any postive results for us.
If we go for our own OO implementation, I'd try to add some more features, like the possibility to query for interfaces, etc
I don't like the C approach either, but a good GUI needs many hands. So, if it is difficult to find C++ coders in here, maybe we should do it in C. For me, a primary concern is to get it over as quickly as possible, because a good gui for Allegro is long overdue (some years!!!).
The VTABLE struct has a method 'get_instance' that is used to query the object for interfaces.
If anyone cares, I'm a C++ coder. Not that I'm any good.
Ok, here is a zip file that contains the object-oriented C framework I talked earlier and an example.
If we choose C, I suggest we use this or a similar framework, in order to make our lifes easier...
As said, I coded quite few java widgets sets at work. It's not that difficult once you have basic framework.
Starting from java.awt.Container it's normally a 8 week job to create a basic widget set with all widgets needed for your normal digital tv needs.
This includes pushbuttons, checkboxes, radiobuttons, thumbwheels, inputwidgets for num and alphanum, formatted text input, textoutput, icons, scrollbar, scrollpanes, splitter windows, tabbed windows, lists, trees and focus management.
We normally did this in a 4week, 2 persons routine (using your normal 8h workdays).
I don't think writing a widget set / gui lib is that much of a problem if one decides first what he wants.
The problem with GUIs for game development is normally that the "what people want" part is the most problematic point.
IMO, throwing code around right now, is a pretty bad idea.
If we want to start creating a GUI, what we should do first is collecting a list of wanted features / requirements.
Then we should start grouping them, both inheritance and priority wise. Once this is done, we'll be able to get an image of what we need as a framework.
I think most of us (reading this thread) have already a pretty good view of what they want. I also think our views are prety different.
Since I'm used to java widget sets (and prefer this way of coding to win32 style), I tend to go into the direction and recrating this environment.
Others might prefer the win style.
So, i't important that we get a better idea of what we want, before we start thinking about "how we implement it".
Again, I know this is skipping ahead ... but today I wrote (entirely unrelated to this) another entity system I realized in the end was very similar to how GUI systems group their widgets. A .tar.gz is attached - have a look if you want.
No, I'm not trying to hijack the thread.
X-G!! I could swear at you!!
You say you can't do work on your version of Chaos and then you show me some code that I can't even understand!
Grrr...
On-topic I think that people need to decide C/C++ before they do any coding.
That work wasn't for my Chaos clone. If you didn't know, it's listed as Inactive/Abandoned on my page (see sig).
Ok, here is a list of widgets that I would like to have:
1) button
2) check box
3) radio button
4) button frame
5) scroll bar
6) slider
7) separator
8) label
9) image
10) raised panel
11) sunken panel
12) menu bar
13) menu item
14) popup menu
15) window
16) toolbar
17) tool button
18) tree
19) list
20) text box
21) text editor
22) html viewer
23) splitter
24) spin box
25) combo box
26) table
27) common dialogs
28) bitmap button
29) message box
Hmmm...it certainly does not seem as a 2 month job to me, especially if the underlying window and drawing system is not there. On top of it, I would like a programming style as simple as possible: just creating one widget within the other, set a few properties, a few callbacks, and that's it basically.
I am thinking towards applications for Allegro, and not necessarily games.
Bah, now that I think it, it is too much work, especially for the summer...
Okay, I'm going to be critical a bit - see this as positive feedback, because I'm interested in seeing a good GUI lib too, and communication is what makes things better.
10) raised panel
11) sunken panel
Same widget, different options to change drawing style.
20) text box
21) text editor
Same widget - one can do multi-line, the other can't. Might as well be a flag. One can subclass if one wants, say, richtext features.
25) combo box
Aggregation of button/label, but still a widget in its own ...
27) common dialogs
Aggregation of window and buttons - doesn't deserve its own widget, should just be a static function that creates a preset common window.
28) bitmap button
Add bitmap support to the regular button instead.
29) message box
See #27.
Also, I would like to add something like an icon pane (like your desktop, for instance) and a scrollable canvas space.
What's a "button border"?
Labels and buttons share most of the layout code (position of text and image in relation to another and the area they occupy).
Toolbars are just a container with a lot of buttons, but I agree we should have some utilities to allow a quick creation of them.
I see no difference between a menu and a pop-up menu. Your normal menu is just a pop-up menu which is shown at agiven position below your menu bar.
Ok, so let's group this list a little.
1 | Static: |
2 | * labels. Can display both text and/ or images |
3 | |
4 | Buttons: |
5 | * Pushbutton |
6 | * togglebutton |
7 | * checkbox |
8 | * Radiobutton (and group) |
9 | |
10 | Menus |
11 | * Menu Bar |
12 | * Menu |
13 | |
14 | Bounded Range Widgets |
15 | * sliders |
16 | * scrollbars |
17 | * Spinner |
18 | |
19 | Input Widgets |
20 | * textfield |
21 | * formatted input field |
22 | |
23 | Single Selection Widgets |
24 | * Combobox |
25 | * Thumbwheel |
26 | * Color Selector |
27 | |
28 | Multi Selection Widgets |
29 | * List |
30 | * Tree |
31 | * Table |
32 | |
33 | Container |
34 | * Panels |
35 | * Splitter Panes |
36 | * Frames |
37 | * Scrollpanes |
38 | * tabbed panes |
39 | |
40 | Dialogs |
41 | * File IO |
42 | * Messageboxes |
43 | * Inputboxes |
44 | * Color chooser |
I removed the window and html viewer. Reason to remove the window was that a window makes no sense ATM, since it would be equivalent to a Panel at the moment (Since allegro doesn't allow us to spawn new windows).
So, what we really need is a frame (a window with a border and title).
The html viewer is too much to chew, IMO.
If you have the label, the push button is almost done as well. Once the push button is done, the others will follow, since labels and all buttons have just a few minor differences.
Most containers are pretty simple as well, since they are mainly aggregations of existing widgets.
The most complex widget in that list is the table (or grid) followed by the tree.
The other widgets are more or less pretty simply, since they only extend existing widgets, add new functionality by grouping widgets or simply because they have a simple structure....
So, besides the table and tree, the foundation, the event, drawing and layout/resize code is the largest problem.
I feel no distinction should be made between togglebuttons and regular buttons - "togglable" should be a flag. They both share the same behaviour, and I don't feel such a small change warrants a widget of its own.
I'm not sure what you mean with "formatted input field" - if you mean richtext, then yes, it should be its own. If you just mean "format masked", then it should probably be a part of the textbox.
What would the different between a Panel and a Frame be?
And, as mentioned, the dialogs might as well not be widgets of their own ... a file list might very well be though (a list or iconview that automatically populates by reading from filesystem).
First of all, sorry for being late in this discussion. A project like this is a good idea and I would love to work on it. I've been working on my lib for a long long time now and I put a lot of hours in it so it will be kind of hard to just drop it. But then again after reading axilmar's post I realized what a crappy piece of shit it really is so I guess it wouldn't be that hard after all...
About the language: C++ is the way to go. Personally I don't see any reason why we should try to make a pure C library that could be integrated in Allegro or something. And doing a standalone GUI lib in C makes no sense to me...
About the features: Allegro is a game programming library. Making an add-on library for Allegro that is not focused on making games is pointless and we should take that into consideration. But making a GUI for a game is quite different than making a GUI for a tool. First of all the library must be completely skinnable which is not as easy as it may seem at first. Secondly the lib must support things like animated widgets and mouse cursors, transparency, non-rectangular widgets, etc. Also the GUI must work with various different screen update methods. Most GUIs (mine included) use a dirty rectangle system based on memory double buffering while most games will want to use page flipping or triple buffering. Also OpenGL for 3D games must be supported natively...
About the last little argument on widgets: There are two extreme positions one can take in making a widget set. Either you can make just a few very general widgets each with countless flags and properties or you can make a separate widget for every little difference in functionallity. Naturally when using OOP one should lean more to the second aproach although in some cases this could be open to discussion...
One has to be pragmatical though ... having one class for InsetBevelFrame and one for OutsetBevelFrame is ridiculous - it's unnecessary, counter-intuitive, and blocks fast switching between styles if it ever becomes actual.
Umm.. if people do want to make this library then they better agree now to use C or C++ and whether it's going to be a general GUI library or designed for games.
It may be that people won't be able to agree on these issues. Maybe we will have two different GUI libraries?
I don't think a C library would be impossible. Maybe make one first, then port to the other? Starting with C++ would probably be good then ...
I feel no distinction should be made between togglebuttons and regular buttons - "togglable" should be a flag. They both share the same behaviour, and I don't feel such a small change warrants a widget of its own.
That's fine for me. I just thought that using the toggle button as the base for radio buttons and checkboxes might be a good idea.
I'm not sure what you mean with "formatted input field" - if you mean richtext, then yes, it should be its own. If you just mean "format masked", then it should probably be a part of the textbox.
Not sure here. A formated input field allows you to pass an input mask, say "###.###.###.###" for an IP address, and similar things. The non-special chars (the dots in this example" will not be part of entered text, but will be displayed statically within it.
I could agree on that a single-line-text-input (phew) and the formated text input should be the same, but since you also said already that you want multi-line text and single line text to be the same widget...
Also, using different widgets doesn't require you to duplicate code. Good class hierarchy and / or using utility classes allows you to reuse the code.
What would the different between a Panel and a Frame be?
A panel is mainly a rectangular area. It's more or less the most primitive widget there is.
A frame would have all the nice things title bars, resizing controls, etc.
And, as mentioned, the dialogs might as well not be widgets of their own ...
While they might be no widgets, common dialogs are still something we're going to need.
About the features: Allegro is a game programming library. Making an add-on library for Allegro that is not focused on making games is pointless and we should take that into consideration.
I disagree here. Allegro is more than just a game coding lib. It's a multimedia lib. The main reasons there are not many tools using it, is the lack of a gui
First of all the library must be completely skinnable which is not as easy as it may seem at first.
True.
This is another part of the list we should start:
General requirements.
One problem I have with your list is transparency (as long as it refers to more than the magic-pink trans), since it's not well supported by allegro.
Regarding the screen update: I assume it will render to any context you provide, and use dirty rect system internally as well.
So, you could tell all your base widgets (say a list of buttons, on the bottom, a map in the center right and a floating window) to update itself.
They will redraw them self (according to input they got, and a list of dirty rects you may have passed to them) and add the changed areas to a dirty rect list.
You can then simply blit the context, or use the list to blit only the dirty areas.
Again, this is just my view of it, once again pretty close to the way java is doing this stuff.
Also OpenGL for 3D games must be supported natively...
In this case, I'd say you should team-up with Korval, who should have something like this ready already (sorry, couldn't resist)
Regarding the upcoming lang discussion again:
I think we can do the general planning now without thinking too much about the lang.
Once we know what we want, it will be more easy to find the best way to do it
I agree with spellcaster here as well, we should just generalize out what we need and get some simple structured outlines going, after that we'll get a better idea on what language we should do the GUI in. Unfortunately for me though I'm moving in a month and do not know how much free time I may have after that, let alone the amount of free time I have at the moment since I'm busy transfering jobs, traveling, filling out apps, etc. I like the slots and sockets approach, callbacks have issues and cause extra code that could be done with a simple pointer from one widget to another. It's a brilliant concept (I read up on it a bit ), and doesn't look like it'll be that hard to do in either language, though most of what I've read about explicity mentioned OOP as the requirement. This is an excellent discussion and I think we've all learned much from it, now if we can all band together with our differences aside and actually make something that works and looks promising. I know it'll definately take longer than a year to complete this, but it'll take determination and team effort.
Spellcaster, I agree with you. Although my widget list was a bit long, it was just a list of requirements. I disagree with you about the Window though: you need resizable widgets with a caption that can be moved around with the mouse. When I talked about Windows, I meant 'window frames': caption, icon, close button, minimize button, maximize button, etc. If that's what you meant, then we are on the same track.
The following quote is the most important one heard in this discussion:
Allegro is more than just a game coding lib. It's a multimedia lib. The main reasons there are not many tools using it, is the lack of a gui.
If Allegro is to take off, it needs a decent GUI library. Every game development environment is accompanied with a suite of tools that ease game development. Without the tools, a developer has to go through the path of making the tools first. That's a big 'no' for most of us, because what we want is to make games, not tools. We get easily bored, and that's why there are so many abandoned projects. The usual counter-argument is that 'there is no one size-fits-all', and that's true. But, if every developer has to develop his/her own tools, why should he/she create a gui lib first ? at least the gui should be 'standard', in the sense that it is available right from the start.
As for the game gui, I disagree: how many games need a combo box, for example ? most games need a simple menu-driven with input boxes and buttons gui, not a gui toolkit. If one plans to make gui-driven games, like a strategy game, that means that most probably he/she does not need 60 FPS and the game can hapilly live within the space of gui toolkit.
There's another issue, though: Should such a GUI library really use Allegro, or is it better to write tools in GUI systems intended for more general use, such as Qt, GTK+ or wxWindows? Is an Allegro GUI as we know it now just a weak "emulation" of a proper GUI that can never be as good as a "proper" system?
As for game GUI's: Netcrux requires a fairly complex GUI system, including combo boxes and lists (for the configuration menus), and it needs a fairly high FPS - not necessarily at the time one uses the GUI, but still. Also, have a look at games like Neverwinter Nights - fairly sophisticated GUI, high frame rates.
I disagree with you about the Window though: you need resizable widgets with a caption that can be moved around with the mouse.
Um, yep. But a window has neither a border nor a title. At least not in the common GUI terminolgy.
Think win32, motif, xlib: every widget is a "window".
In X, you need a window manager to provide the frame (caption and resizing) for top-level windows, in win32 you can specify that you want to add a frame, and in Swing Windows and Frames are different classes.
If speaking in patterns, the frame "decorates" the window, allowing to resize, move, etc. whatever is in it's content area.
Should such a GUI library really use Allegro, or is it better to write tools in GUI systems intended for more general use, such as Qt, GTK+ or wxWindows?
Should one really code games with allegro, or is it better to use OpenGL / DirectX or SLD / ClanLib?
Um, yep. But a window has neither a border nor a title. At least not in the common GUI terminolgy.
Well, anyway, I meant a frame. The essence is not in nitpicking, is it ? Let's make a gui library, guys!!!
or is it better to write tools in GUI systems intended for more general use, such as Qt, GTK+ or wxWindows?
WxWindows is not very C++ style because it uses message maps to map methods to messages. That's the primary reason I dislike it.
I don't know GTK.
Qt is fine, but on Windows you need a commercial licence to use anything beyond version 2.1. So either the tool must be limited to 2.1 capabilities or someone must have a Qt 3.1 licence.
Furthermore, there is a need for an intergration layer with Allegro. I don't remember by heart, but Qt widgets don't give access to the window handle. Even if they did, the intergration layer would be specific to each window system: different code for win32 ,for X, etc. I am assuming here that the tool would use Allegro somehow to draw stuff within a window, manage bitmaps, etc.
One of the advantages of Allegro is that it is multiplatform: the same code compiles under Windows, Linux, Mac, BeOS and a lot of other platforms. Having a thin gui library which exploits this multiplatform capability would be a great asset. Writing Allegro tools using allegro would be great, since there would be no intergration problems.
not necessarily at the time one uses the GUI, but still. Also, have a look at games like Neverwinter Nights - fairly sophisticated GUI, high frame rates.
Sorry, I haven't played Neverwinter Nights. Would you care to explain what complex GUI interaction it has while the game is running ?
Anyway, the problem of the update method can be easily solved: each widget should have one or more flags that tell how the widget is changed. Is it resized ? is it dirty ? etc. Now, when it is time to update the screen, the widget tree is updated using the above-mentioned flags, and then drawn onto the game buffer bitmap. In applications, widgets are drawn only when they need to directly to the screen. In games, the programmer shall have the capability to install a back buffer, i.e. a bitmap to use while drawing the widgets.
I see spellcaster missed my point. I was just asking if I was the only one who felt a bit uncomfortable at the thought of a GUI running inside a graphical window, which does not support normal interaction with outside windows (think drag and drop) and generally has a completely different look and feel than the rest (and in some cases, reacts much more slowly - Allegro's mouse is, for instance, much slower than the Windows one on my computer)?
Could you care to explain what complex GUI interaction it has while the game is running ?
Lists, text input, drag-n-drop, IIRC there were checkboxes and combo boxes, popup menus.
I'm kinda confused about the slots and sockets approach though, wouldn't you still need callbacks for specific function calls, whereas slots and sockets are excellent for tieing two or more widgets together?
Yes, you would. Signals and slots are just for connecting widget actions togther - such as what should happen when something is clicked, etc. The alternative is overriding a function - things like onPaint() fall into this category, because they're not really something signals and slots are supposed to be used for.
I see spellcaster missed my point.
I'm quite good in point missing
I was just asking if I was the only one who felt a bit uncomfortable at the thought of a GUI running inside a graphical window, which does not support normal interaction with outside windows (think drag and drop)
You can support d&d nicely while running within an allegro window. All you ahve to do is to add some win32 calls.
and generally has a completely different look and feel than the rest (and in some cases, reacts much more slowly - Allegro's mouse is, for instance, much slower than the Windows one on my computer)?
The mouse problem should not be there in windowed modes, and I've reported the problem with the mouse almost one year ago.
Regarding the look: "hip" applications (video/audio apps in particular) seem to use a pretty non standard gui anyway.
And still, Music Maker, Bryce, Poser, Painter, eJay, etc. sell well.
It all depends on the market. And if you're going to use the allegro gui for a project, chances are good that you'll target gamers, or "geeks" of some kind or another.
Regarding Signals and Slots: Um, they are nice, but I must admit that I'd prefer regiserable callbacks and function overloading.
(Um, yes, once again I try to milk my java knowledge )
I just think calling a registering function is pretty simple.
You want to get notified as soon as a window is moved? Register your function at the eventSource, and wait until you get called.
Implementation could be either function pointers in C++, interface pointers or functors in c++.
The main reason I don't like Signal/Slot is that it requires you to do some extra parsing.
I'm not that fond of MessageMaps, because it's pretty hard to alter them dynamically.
Regarding the look: "hip" applications (video/audio apps in particular) seem to use a pretty non standard gui anyway.
Yes, and I'm equally annoyed at them. >:E
Then you should not partake in this discussion here
If you want a native GUI interface, use the native API of your platform. You don't have much choice there.
It all depends, really. I'm all for having a GUI - some games need it - but I'm just wondering whether its feasible to have one to use for tools in these days, when we have more "standardized" GUI systems - such that didn't really exist in the days of the Atari ST and DOS.
I was just asking if I was the only one who felt a bit uncomfortable at the thought of a GUI running inside a graphical window, which does not support normal interaction with outside windows (think drag and drop)
Drag-n-drop from other windows is different for any window system anyway. In this case, we would need custom code for each O/S.
and generally has a completely different look and feel than the rest
Well, on my Windows box alone I have various apps with completely different look and feel:
1) normal Win32 apps from Microsoft which are the original look and feel.
2) Qt based apps that have a slightly different look and feel. I can tell which apps are written with Qt anyway.
3) GTK based apps with an entirely different look and feel; for example DevC++.
4) Java apps with with an entirely different look and feel; for example PVCS, JDeveloper, PowerDesigner (at work).
5) All the media player programs that are skinnable. Way different interface.
So, the unified look and feel is kind of a myth. But this is no problem, as long as an app has menus, scroll bars, list boxes, text boxes and other things that are similar ideas. So, I don't think anyone would complain that our lib would look slightly different.
Don't forget that we can make it skinnable. The MASkinG library has done a pretty good job in skinning.
I agree with Axilmar on the last point. If everyone loved the look of windows then everyone would use it. With skinnable themes you can at least cater for different personal tastes.
I hope people here follow up on their intention to start a GUI library!
It would certainly benefit most Allegro users anyway..
I'm well on my way with my own library though it's limited due to using the same allegro dialog constructs as with most of the other GUI's. This most has excited me into realizing I need to ditch the underlying allegro structures and go with something a little bit different. Miran, Spellcaster, and I have had a great deal of experience with skinning so that won't be a problem there. So far I've managed to mimic the look anyway of MacOS, Windows, XWindows, and BeOS with a single dialog procedure, it's based on what is there and what is not there in the skin. For example BeOS has header text background (the thing behind the text that conforms to it's width), and no minimize icon. In the skin config file you tell it to draw the close icon to the left of the text, and the maximize icon to the right of the text, not the dialog header. Because the minimize icon doesn't exist (loaded as NULL) then a double click to the header will minimize the dialog. It requires one heck of a lot of code, but it's doable. In fact a lot of stuff for the headers is completely optional:
Main header bitmap (goes behind text, fits entire doalog top) Required
Text background bitmap (goes behind text, conforms to text) Optional
Minimize Icon: Optional
Minimize Disabled Icon: (these are for when the dialog is not selected (greyed out)) Optional
Maximize Icon: Required
Maximize Disabled: Optional
Exit Icon: Required
Exit Disabled: Optional
Rollup Icon: Optional (as well as disabled icon)
UnRollup Icon: Optional (as well as disabled icon)
Sticky Icon: Optional (ditto)
UnSticky Icon: Optional (ditto)
Help Icon: Optional
Help Disabled Icon: Optional
Header Panel: Optional (used to go in front of text, single bitmap)
Application Icon: Optional
That's 20 separate elements to draw just a skinned header, but rarely will every element exist in one datafile, and you have to keep in mind that most of these elements can be left, right, or center aligned. I also have plans for a mask element as well to poke through the header and show the background.
To do something like winamp with our GUI we could also have a procedure that acts like the dialog but is one single bitmap, dragging can be done anywhere another widget doesn't exist, and buttons, etc, can be replaced my icons instead giving the look and feel of a winamp-type application.
I think Axilmar gave a pretty decent framework example go off of, but I really haven't had the time to look over it in detail. But I think we have enough to get started with. As well any of my subprocedures i.e. mouse and text can be ported to this new library easily... very easily. I use nothing GUI specific in them and are in fact separate libs already. The text routines need some help though, I'm just not satisfied with how I handle them yet...
Here's what we've agreed on so far:
Skins
Slots and Sockets
C/C++ (I don't think it matters anymore as long as it works under both API's)
I would like to see different update methods as Miran pointed out but it's gonna be tricky to pull off. We may either have a set_gui_update_method or a way to pass a backbuffer and screenbuffer into our routines, maybe a combination of both...
I would hate to see my GUI die as well which is why I would hate to give up working on it even if my efforts are kind of vain, but I'll also give my best effots towards this lib as well if it gets off the ground. Again lots of discussion which is good, but little in teh ways of everyone getting together on this and making a working framework or teaming off to work on separate components. I think that's something we should do anyway, break off and work on base components (i.e. mouse, text, main routine runner, and anything else that's needed) once we get a decent idea on where to go with this GUI.
Hey Steve, I already have my Widget class ready that provides the following:
1) buffered and non-buffered drawing
2) focus management
3) geometry management
4) opaque and non-opaque widgets
5) misc widget properties like enabled, visible, etc.
6) accelerator table support
7) Widget trees (of course).
It's quite a clean interface. I have already made all the big decisions about the design, but I am always open to suggestions.
Tomorrow I am gonna do the Event class which reads mouse, keyboard and timer events from an internal queue.
It's C++ of course. Are you interested ?
Can we get to see this code? I'd be very interested in having a look at it.
I'm all for a new GUI, by the way - and I much prefer C++ over anything else for it.
I'd love to give some support for this lib, my C++ skills are a bit rusty though, but I understand class concepts, etc. Maybe after this week I can begin to do some work, I think I'll have most of my moving issues sorted out by then
1)Drawing straight to screen, or buffered? not sure here what you mean. Fast GUI's use a DRS system, which is a form of buffered drawing.
2)Ok, gotcha there
3)Not sure what geometry management is.. like non-rectangular widgets?
4)I think that's up to the skin for opaqueness
5)Like the allegro GUI you need some flags like D_SELECTED, D_HIDDEN, etc.
6)What does this do?
7)Trees are good, I'm assuming you mean child widgets where the child widget is relative to the parent widget at location 0 0. I pondered this as well, like nested widgets.
But yeah, I'm interested and would look over it.
6) Keyboard shortcuts.
Ok, here is the source code (attached to the message). The suppied zip also has html help starting from the 'index.html' file in the docs directory, as well as a Test class derived from Widget for testing purposes.
Steve, here are your answers:
1) widgets use the 'screen' pointer to draw themselves, but in bufferred mode the screen pointer points to the buffer.
2) Geometry management: Each parent widget is responsible for maintaining the layout of its children, or resize itself according to its children. My Widget class has lots of methods to support this mechanism and to minimize redrawing for avoiding flickering.
4)It is not enough for the skin to define such a property. The widget redraw mechanism must support redrawing of overlapped widgets before drawing a non-opaque widget. Non-opaqueness is also useful in labels, check boxes, radio buttons and other non-rectangular widgets that use their parent as background.
6) Each widget can register itself to be called when the relevant accelerator key is called.
7) Of course. Child widgets have a x, y relative to the parent. This parent-relative x, y is translated to the screen-relative x, y once widgets are on the screen. All other operations are done using the widget's screen coordinates, though. For example, the Test class draws the widget by using its x1(), y1(), x2(), y2() coordinate methods.
I can't compile the source though, or is it even ready to compile, I get an error that list cannot be found on #include <list>
I'm compiling with this:
D:\unzipped\algui[1]>g++ -c -I./include src/widget.cpp
and a bunch of syntax errors follow.
Anyway algui sounds appropriate for a name... but any other suggestions or maybe a codename ... that'd be cool.
Um... ok, if we start to throw code around before we even have started to design this thing, then I'm out.
If you take a look around here, and count the number of unfinished/abandoned gui libs you'll see why starting another one without a good design first is not going to work.
Esp. the base classes need more thought. Reason for this is simple: Once you have the base classes coded, and add some classes on top, it's hard to change the design again ("since we already have working classes").
spellcaster, have you also realized that the previous "projects" for new guis or other larg projects never got off the ground because noone actually started? I tihnk this is a good thing, show some code, and debate, at least then theres "something".. instead of a bunch of hot air.
I can't compile the source though
Its the STL's 'list'. It compiles ok under MSVC++...
Um... ok, if we start to throw code around before we even have started to design this thing, then I'm out
I will not claim to be "the" authority when it comes to guis, but I have given a lot of thought into the subject. I think that my Widget class is a very good start, and with a very good design.
Of course, I posted the code out as a basis for discussion, rather than as the definitive start. Since time is money though, I am gonna continue building on it. I am open to suggestions, and I would be very happy if I have some.
That's my point. So it won't be a team project, but we'll all start doing our own thing. Again.
Regarding your design, my major problem is that BITMAPs are not hidden. Another point is the way common gfx operation are perfromed directly.
But, as you said, time is money, and you have spend a lot of time thinking on this matter already.
I'll be glad to use your GUI lib as soon as it's finished.
Don't get me wrong folks, but exactly this is why group projects die very youg here all the time. A single developer might be able to jump start a project. He might even be able to finish it, even if he doesn't have a design (besides the idea in his head), but for a team project, this just doesn't work.
Like I said before, we all have spent quite a few time thinking about this topic. We all have worked with guis and developed our own enhancements, add-on or own systems.
But if we just say "here's my code. take a look, discuss it, but I'll do my stuff anyway" we'll end up with one gui lib per person.
If this is what you want, ok
Spell: Why don't you post some detailed ideas here so other people can see how you would approach this library?
I think people are rushing ahead too, but in fairness to axilmar, he has been most productive so far.
Why doesn't everyone discuss how they would design the GUI? Then we can have a discussion and (hopefully) reach an agreed design...
my major problem is that BITMAPs are not hidden.Another point is the way common gfx operation are perfromed directly.
Sorry, but I don't understand what you mean. If you mean that drawing should be abstracted, like Windows drawing on a device context, my opinion is that it should not, for the following reasons:
1) All allegro drawing must be encapsulated within a class. Too much work.
2) Too many indirection layers. Drawing a simple line will call the virtual method of the class, which in turn will add the widget's screen position to the line's coordinates, then use Allegro to draw, which will redirect the call to the BITMAP's vtable, which in turn will use DirectX or GDI or xlib to draw...
I don't think Allegro should be hidden, because there is a lot of functionality that needs to be wrapped up and this is quite tricky since it is not easy to tell what one might need upfront. I am not for a generic GUI toolkit, since it is quite difficult to achieve.
As I stated earlier, I am open to suggestions. (But I don't see any). Therefore, I am gonna proceed on my own, and whoever wants to jump in, is welcomed.
I've opened a sourceforge project. When I finish the event handling, I'll notify you of the link.
As soon as you start blitting directly to a bitmap, you'll end in the same dead-end as allegro's direct screen blitting.
Assume you have a GraphicContext class. Assume this class has a blit method. With a minor chang in that blit method, you can add a dirty rectangle system to all applications using the gui.
What about text output? If you textout your strings directly to a bitmap, you're stuck with the text rendering system you're using. If you decide to use Allegro Fonts, this means no true type support without changing all widgets.
1) All allegro drawing must be encapsulated within a class. Too much work.
Doubt that. You need blit, textout, rect, rectfill and line.
If somebody needs more, he can just add them to the GC and all apps can use them.
2) Too many indirection layers.
Exactly one more than before.
I don't think Allegro should be hidden,
There's no need to hide allegro completly. But the whole idea is to give you a flexible way to access GUIs.
The system you're proposing is as limited as the current allegro gui. It just adds some fluffy classes arond basically the same functionality and adds containers.
As I stated earlier, I am open to suggestions.
Um , no you're not. YOu have started implementing already, this means that you have already a fixed plan in your mind. That's not a bad thing, but it makes planning pretty pointless, since you don't want to design a GUI, you want to implement one.
(But I don't see any)
In that case, you should actually try to read what's written here
Therefore, I am gonna proceed on my own, and whoever wants to jump in, is welcomed.
Hehee.. that's exactly what I said a couple of days ago. Who needs team projects anyway, heh.
I wish you good luck with your GUI.
Spellcaster, was it you who emailed me about my gui? Sory, I've just been having a rough time..
Spellcaster, I will be straight with you: I don't like this nitpicking of my words. We are here to do a job, not to play "who's the smartest". Anyway, for defending myself, I will also nitpick your talking:
Assume you have a GraphicContext class. Assume this class has a blit method. With a minor chang in that blit method, you can add a dirty rectangle system to all applications using the gui.
There is no point in adding a dirty rectangle system in a GUI library. For the following reasons:
1) applications only need double-buffering, they don't need a dirty-rectangle system or any other system whatsoever. My Widget class uses double-buffering to blit to the screen only the portion that is updated. For example, if a small button is redrawn, the part of the screen that will be affected is only the part that is updated by the button. Overlapped and foreground widgets will be drawn to the buffer too, but clipped accordingly.
2) The dirty rectangle system is needed only for games that their screen does not change so often. This is valid only for a small amount of games, since most games will update the whole screen every frame. And the dirty rectangle system only fits with buffering and not with page flipping. Therefore, a dirty rectangle system is deemed unnecessary.
3) I don't like animated GUIs for applications. When using an app, I would like my menus to instantly pop up and not every time to go through some silly animation.
In the light of the above arguments, we can safely deduct that a GUI lib does not need a dirty rectangle system or any other drawing system. Remember, the library should be as thin as possible.
In case that you are so desperate and you need a dirty rectangle system whatsoever, you can always add the required functionality at the end of the 'redraw(x1, y1, x2, y2)' method, at the line that the buffered content is blitted to the screen. This can be applied to any other system of drawing.
What about text output? If you textout your strings directly to a bitmap, you're stuck with the text rendering system you're using. If you decide to use Allegro Fonts, this means no true type support without changing all widgets
This is already solved. True type rendering will be done by using the excellent AllegroFont library. My library will have a Font base class with text measuring and drawing methods, an AFont class for using Allegro fonts and a TFont class for using AllegroFont fonts. Example:
1 | //font base |
2 | class Font { |
3 | public: |
4 | virtual void textOut(BITMAP *bmp, const char *text, int x, int y, int color); |
5 | }; |
6 | |
7 | //Allegro font |
8 | class AFont { |
9 | public: |
10 | virtual void textOut(BITMAP *bmp, const char *text, int x, int y, int color) { |
11 | textout(bmp, _font, text, int x, int y, color); |
12 | } |
13 | private: |
14 | FONT *_font; |
15 | }; |
16 | |
17 | |
18 | //AllegroFont font |
19 | class TFont { |
20 | public: |
21 | virtual void textOut(BITMAP *bmp, const char *text, int x, int y, int color) { |
22 | alfont_textout(bmp, _font, text, int x, int y, color); |
23 | } |
24 | private: |
25 | ALFONT_FONT *_font; |
26 | }; |
Doubt that. You need blit, textout, rect, rectfill and line.
Nope. You need gui textout, raised 3d rect, sunken 3d rect, 3d circle, 3d line, line drawing with patterns for drawing the focus lines in buttons/ check boxes/radio buttons/list items/tree items and other items, ghouraud shading for drawing beatiful window captions, stretch blit for filling the application's background with a bitmap (as if it was the desktop/root window of a window system), skin blit for drawing those skins with a border, frame drawing (sunken, raised, window, bevel, whatever)...
Not only you need more stuff, but these extra drawing functionality is done by combining the various Allegro calls.
If somebody needs more, he can just add them to the GC and all apps can use them.
And if you really thing about the applications, they may need even more drawing routines: drawing a tilemap, drawing a line with width more than one pixel, etc. These complex drawing routines will be very slow if they go through a 'graphics context'.
So you can't really say to the programmer "what ever new drawing stuff you need, you need to encapsulate it in a new GraphicsContext class". That's stupid, and the programmer will blame you all day for this stupid decision. Why not allow him/her to draw everything using straight Allegro calls, as if the widget was alone on the screen ? and then the GUI system will take care of the updating as needed.
Exactly one more than before
But there are already 5 to 6 layers out there. If you add one more, then it will be super slow. There is no need anyway to add another layer, as I have previously demonstrated.
The system you're proposing is as limited as the current allegro gui. It just adds some fluffy classes arond basically the same functionality and adds containers.
You are totally wrong. Can the Allegro gui do the following:
1) draw overlapped widgets when a widget is redrawn ?
2) automatically manage geometry of the GUI ?
3) have bufferred mode drawing ?
4) do an event loop ?
5) do modal dialogs without interrupting the main event loop ?
6) have hierarchical accelerators that are registered dynamically so as that dialogs can be incorporated into the main GUI itself and work the same ?
7) browse the whole GUI using tab/shift + cursor keys ?
8) allow ancestor widgets to repaint themselves differently if a descentant widget has the input focus ? (so as that the input focus part of the screen is always easy to track down)...
Not only that, but the Allegro's GUI does not have toolbars, workspace, tree view and other very crucial widgets.
What am I saying ? the Allegro gui can't even allow the programmer to dynamically add widgets to a dialog!!!
Um , no you're not. YOu have started implementing already, this means that you have already a fixed plan in your mind. That's not a bad thing, but it makes planning pretty pointless, since you don't want to design a GUI, you want to implement one.
Hey, make a suggestion that's worthy enough and I will incorporate it in my implementation. Or you can incorporate it, but I really doubt it they way this discussion is going.
"some fluffy classes". This type of critism makes me very angry because it is unfair, and I don't see the reason why should I be insulted by you the way you do! You want to play smartass ? I don't, I just want a proper GUI for Allegro, that's all.
Hehee.. that's exactly what I said a couple of days ago. Who needs team projects anyway, heh.
Well, if you decide to jump on the train, or anybody else, it would be a team project. Your attitude is not very encouraging though.
I wish you good luck with your GUI.
Basically, you should say "I am sorry, I have not really understood your design"...By your comments, I can understand that you don't have absolutely no idea about the real GUI issues, since you only talk about drawing: geometry management (which is THE main problem), keyboard management and input focus, etc...the update method is a minor problem that can easily be solved in one place at the code (as I have said above).
P.S.: and a wide range of classes to cover all types of problems!!!
P.S.: I haven't made any design about the widget classes themselves. If any of you want to really help, you can do the design and coding of the classes from A to Z.
Spellcaster, I will be straight with you: I don't like this nitpicking of my words.
Hm.. the only thing you could interpreted as nitpicking was the frame/window discussion... and if you take at my post (the one before your post) I had already explained the meaning of words... but, anyway... I always like a good argument
1) applications only need double-buffering,
Um, why?
Don't you think there's a reason almost every gui there is has a dirty rect system?
And even if you think your applications don't need a DR system, why do you think it's bad?
My Widget class uses double-buffering to blit to the screen only the portion that is updated
Either you're saying "I'm blitting only the part I'm blitting" or you're saying "I'm only blitting the part that has changed".
The first one makes no sense, the second one is either a DR system or pretty inefficient.
2) The dirty rectangle system is needed only for games that their screen does not change so often.
Again, what you're doing here is to limit yourself for no particular reason.
If your game has a "play area" and "status/ control" area, only the game area needs to be repainted every frame. No need to blit the complete GUI if only one button has changed.
Also, the DR system would allow you also to invalidate parts of the GUI. Assume that your game draws something over the gui (say for a tutorial, or a very special effect).
Well you can blit everything, but why should you?
And even if you don't like DR systems, why do you force everybody not to use them?
3) I don't like animated GUIs for applications. When using an app, I would like my menus to instantly pop up and not every time to go through some silly animation.
People have written what they'd like to see in there gui of choice. Obviously stuff like animation is wanted. Obviously you don't care.
That's fine. As I said, do your own thing. That's ok and no problem at all.
In the light of the above arguments, we can safely deduct that a GUI lib does not need a dirty rectangle system or any other drawing system. Remember, the library should be as thin as possible.
You do realize that the whole idea of a group discussion is to get the idea of the group?
It's ok if you have a closed mind and a ficed feature set. But don't try to enforce it. First off all, it won't work, and secondly it's pretty pointless.
Regarding your idea of a Font doing text output... well, your choice. You're mixing here two different things though, but it's your GUI, so you're free to do what you want.
I could give you a couple of reasons why this is bad, and in what limits this will impose on you, but I'm quite sure that you have considered this already and dismissed it as "not needed".
Nope. You need gui textout, raised 3d rect, sunken 3d rect, 3d circle, 3d line, line drawing with patterns for drawing the focus lines in buttons/ check boxes/radio buttons/list items/tree items and other items, ghouraud shading for drawing beatiful window captions, stretch blit for filling the application's background with a bitmap (as if it was the desktop/root window of a window system), skin blit for drawing those skins with a border, frame drawing (sunken, raised, window, bevel, whatever)...
You do realize that this can be done with the methods I listed a Context capable of holding a state?
Besides the goroud shading But I'd say that a simple gradient floodfill should do the trick as well. No need to add 3d routines to a standard title bar.
The idea is to maintain a small interface, not to bloat it. If you list these drawing routines, and then start to group them by category, and then take a look at the similarities and differences, you're one step closer to a design.
Just a small example: Your sunken/raisen stuff. You do realise that if you have a shadedRect() or something a sunken and raised border is painted by the same routine, just with different parameters?
And that a "skin blit" is just a blit?
Don't make things more complicated than they are... try to make your interface simple
But there are already 5 to 6 layers out there. If you add one more, then it will be super slow.
No comment on that one. But I added it to my list of possible sigs
What am I saying ? the Allegro gui can't even allow the programmer to dynamically add widgets to a dialog!!!
I won't quote Prachett here, but it's very tempting.
Regarding adding widgets to dialogs: Of course you can do this. Requires you some minor tweaking of your DIALOG struct, but it works fine. Same for most other stuff on your list.
And the remaining feature are just layout / container related
Hey, make a suggestion that's worthy enough and I will incorporate it in my implementation.
As I said, you can implement what ever you want. As you said it's your GUI. I don't want to convince you, it's your code, your lib, feel free to do what ever you want.
But you do realize that this thread was about collecting ideas for a new gui. Designed and implemented in a team effort? And "Team" and "my gui" don't mix well
Well, if you decide to jump on the train, or anybody else, it would be a team project. Your attitude is not very encouraging though.
I see no point in jumping on your "wagon". As you pointed out already, it's your GUI. You also seem to be very convinced of your design. Well, I'm not. I've also explained what's limiting your approach.
By your comments, I can understand that you don't have absolutely no idea about the real GUI issues, since you only talk about drawing: geometry management (which is THE main problem), keyboard management and input focus, etc...the update method is a minor problem that can easily be solved in one place at the code (as I have said above).
You do realize that thes points have been addressed already? You did read the posts in this thread, right?
YOu do also realize, that the updating problem is the most limiting factor in the current gui?
You do also realize, that the proposed event model, with the outline event flow will keep track of input focus?
haven't made any design about the widget classes
No need to point that out. It was pretty obvious already.
If any of you want to really help
You really think that thi is about your GUI, don't you?
But I agree, that after this discussion, the chances are pretty bad to get team effort towards an allegro gui on the way.
I'd hate to think that one of my thread's started a flame war people..
Hm.. the only thing you could interpreted as nitpicking was the frame/window discussion...
Nope. You took every line that I wrote and replied to it. And the replies were not that good.
Um, why?
Don't you think there's a reason almost every gui there is has a dirty rect system?
Well, no real GUI has a dirty rectangle system: MS Windows, X-Windows, the Photon MicroGUI, the OpenWindows, the NEWS system all use regions.
MASKinG has dirty rectangles, because it implements animation on the desktop.
And even if you think your applications don't need a DR system, why do you think it's bad?
I did not say it is bad, I said it is unecessary. Let me repeat the reason:
Only the affected area is double-buffered.
Again, what you're doing here is to limit yourself for no particular reason.
If your game has a "play area" and "status/ control" area, only the game area needs to be repainted every frame. No need to blit the complete GUI if only one button has changed.
I am not limiting anything. Perhaps you have not understood my update system. Let me go through it, one more time:
A. there are two buffers at each given time: the screen, and a back buffer.
B. widgets draw on the back buffer; widgets behind them are redrawn and widgets above them are also redrawn on the back buffer; the end result is blitted on the screen.
C. Only the area affected by the redrawn widget is blitted to the screen.
So, if a button is updated, it will draw itself either directly to the screen, or to the back buffer. Then, the library will blit to the screen the affected area.
There are two cases here:
1. The button's state change is immediately drawn to the screen. This happens automatically when there is no back buffer.
2. The button's state change is immediately drawn to the buffer. This happens automatically when there is a back buffer.
Now, if there is a buffer that the game draws onto, you can:
1. Either draw the GUI directly to the screen, but have the special effect blitted from some other buffer.
2. Or draw the GUI on the widget back buffer, which will blit itself to the screen.
3. Or draw the GUI on the game buffer, using screen = game buffer, then blit the game buffer to the real screen.
I don't see any other case. So, I don't think there is a need for a dirty rectangle system.
People have written what they'd like to see in there gui of choice. Obviously stuff like animation is wanted. Obviously you don't care.
That's fine. As I said, do your own thing. That's ok and no problem at all
People have also stated that they need tools to write their games, and almost everyone has tried his own version of a tilemap editor, with no particular success, due to the lack of a good gui.
A GUI toolkit for writing Allegro tools is a priority over a GUI toolkit for games. How many games need special effects and combo boxes, for example ?
I think there is small minority that needs special FX in the GUI. And if you need it so badly, you can always animate widgets by the traditional way, i.e. change their state and geometry.
And even if you do want so much special effects, I have told you that it is the easiest thing to add a DRS system to my design. There is a method called 'redraw' which draws either directly to the screen or to the back buffer. The last line calls 'blit' to blit the back buffer to the screen. You can replace this call with a call to the DRS system.
The next version of my class will have it, so as that I prove to you that it is the easiest thing to do.
Besides the goroud shading But I'd say that a simple gradient floodfill should do the trick as well. No need to add 3d routines to a standard title bar
Yes, but how you can quicly draw a gradient floodfill ? Instead of making your own routine using Allegro, you can use 'quad3d' and achieve the same result.
The idea is to maintain a small interface, not to bloat it. If you list these drawing routines, and then start to group them by category, and then take a look at the similarities and differences, you're one step closer to a design.
But I am the one who doesn't want a 'GraphicsContext' class for avoid the bloat!!!
Just a small example: Your sunken/raisen stuff. You do realise that if you have a shadedRect() or something a sunken and raised border is painted by the same routine, just with different parameters?
Besides that, you don't have any other example.
And that a "skin blit" is just a blit?
Don't make things more complicated than they are... try to make your interface simple
It seems that you have not try skinning. Skin blit is not just a blit: it is a routine that creates 9 sub-bitmaps of the skin bitmap, uses the outer subbitmaps for the borders, and the inner one for the filling. Check out MASKinG. It's easy to talk when you don't know shit.
No comment on that one. But I added it to my list of possible sigs
Feel free to put it!!! and put my name next to it, but don't forget to put the rest of the text too...because it might look silly out of context, but I talked about COMBINATIONS of drawing calls and the ONE MORE LAYER that it will make it super slow.
As I said, you can implement what ever you want. As you said it's your GUI. I don't want to convince you, it's your code, your lib, feel free to do what ever you want.
But you do realize that this thread was about collecting ideas for a new gui. Designed and implemented in a team effort? And "Team" and "my gui" don't mix well
It's my gui because I designed it and wrote it so far. If you participate, it will be 'our gui'. Simple, isn't it ?
see no point in jumping on your "wagon". As you pointed out already, it's your GUI. You also seem to be very convinced of your design. Well, I'm not. I've also explained what's limiting your approach
Nope, you did not explain. You just said "other systems use DRS, so yours should also do the same" without justifying why.
As I said earlier, I am posting the code for demonstration reasons. I want people to look at it and say "ok, here is a bright idea, for so and so reasons", or "here is a silly idea, for so and so reasons". But I want arguments, no silly 'others do it this way, so we must do it that way'...
You do realize that thes points have been addressed already?
Geometry management ? were ? I am the only person that talks about it. I must be the only one then that gets irritated for having to specify each widget's geometry by hand!!!
You did read the posts in this thread, right?
I don't want to be ironic. Let's keep the discussion in a good level.
You do also realize, that the proposed event model, with the outline event flow will keep track of input focus?
I did not see any post about "event flow" or "input focus". I am not sure about what you mean, but I have already implemented it and it is indepentent of the event flow: you can change it by hand, if that is necessary.
You haven't realize a hidden trap in the focus management: that of toplevel windows coming forward when a control is clicked. In my widget class, when a widget gets the focus, all ancestor widgets are notified that a descentant of them got the focus. By this way, they can bring themselves forward and/or change their appearance, without ever knowing if they are toplevel or not.
This is quite a good design, don't you think ? I have never anywhere else someone talk about it, though.
<quote>
You really think that thi is about your GUI, don't you?
But I agree, that after this discussion, the chances are pretty bad to get team effort towards an allegro gui on the way.
<quote>
It's not about 'my gui', its about Allegro not having enough game tools or an easy way to write one.
Aigh... somehow I knew this would happen. But I agree this has become Alixmar's lib and not a team lib. It's not a bad thing that we've gotten a framework going, but because it kinda came out of nowhere there's going to be disagreements. For all I knew MSWindows and X-Windows uses a dirty rectangle systerm, but what is regions? I mean as long as you aren't using pure double buffering and you only redraw to the screen what has changed, then there's not much to worry about, both do the same thing. The reason you don't use double buffering is because the speed of the GUI will be dependent on the system and the resolution being rendered at. With a DRS system it's only noticeable if you are resizing or dragging a large dialog around at high resolutions or a large size. I'm not going to consider myself a GUI master or anything by far, I'm still learning on what it takes to make a decent GUI, and yes mine doesn't have a lot of the features algui has. But if we can't decide on how to work together, then yes, it'll just become alixmars project. I think partially it's due to the fact that this discussion is taking place in a forum where realtime discussion can't take place. I don't know, but it's nearly impossible to make everyone happy. But seriously, without knowing your plans we can't be expected to know how your GUI works or how to add to it as a team, I fear that yet another GUI thread has come to a close
Oh and you keep wanting a "quick" GUI implimentation, I understand you don't want to be writing this lib forever, but there is nothing "quick" to writing a GUI, if you take shortcuts or don't weigh the different possibilities then it will fail.
You took every line that I wrote and replied to it. And the replies were not that good.
That's not nitpicking
People have also stated that they need tools to write their games, and almost everyone has tried his own version of a tilemap editor, with no particular success, due to the lack of a good gui.
*cough*
It's my gui because I designed it and wrote it so far. If you participate, it will be 'our gui'. Simple, isn't it?
Yes, but the idea isn't to help you with your GUI, it's to get a design most people can agree and collaberate on.
Anyway, it looks like everyone's just going to write there own (which is about what happened last time this came up) so I guess we'll just see who can put their money where their mouth is. I was thinking of writing a GUI lib too, but since everyone praises Swing, maybe I'll wait until I've gotten into Java before I begin with an inferior design (I was just going to (mostly) emulate Win32) ...
Axilmar:
The problem I see is that you have presented a design within a few days of this thread being started and are now implementing it.
To me this suggests two possible reasons:
1) You are jumping into something without a proper design.
2) You have spent a lot of time previously on thinking about designs and are now implementing them.
I suspect you are going with reason number 2. Which in one way is really great as you can put your experience and knowledge to good use! However, this approach is in contrast to your earlier posts about debating the best design and having a democratic vote on these issues.
I think this is what is putting people off, you have not put up a proposal for debate. You put up a proposal and now are implementing it while the debate is still going on.
BTW: I didn't see Spellcaster's points as personal criticisms even if it seemed they bothered you. His debates with Korval were legendary on the board, but I never read the critiques he presented to Korval as personal (I'm thinking of the original critique in those threads, not the reactions!).
I think your experience and knowledge is very useful for making a good GUI. However IMHO you need to let people have a say in the design and features else it truly will be only your library.
What is a region, you ask ? a region is a table of rectangles that represent the visible portion of a window. All drawing operations are clipped within each rectangle of a region. Each time a window appears, disappears or resizes, the windows below get a new region.
In case you truly want to learn how regions work, have a look at MxWindows (which I wrote 3 years ago).
Basically, everything revolves around understanding how to 'subtract' a rectangle from another rectangle.
Let's say we have two rectangles, A and B. The operation A - B can have the following results:
No rectangle if B totally overlaps A.
1 rectangle if B overlaps half of A.
2 rectangles if B overlaps one quarter of A.
3 rectangles if B overlaps some side of A partially.
4 rectangles if B overlaps part of A but it is completely inside of A.
So, after the subtraction, the result set of rectangles is kept in a table, called a REGION.
Professional window systems use regions to make Windowing possible. Regions were first used in Apple's Quickdraw, back in the 80s.
A clever Dirty Rectangle System will use the same algorithm for overlapping updatable portions. When using a DRS, there is only one region that is calculated as the programmer adds dirty rectangles to it.
I have done a dirty rectangle system in 1996, where I wrote a MsPacMan conversion for DOS in Turbo Pascal 6 (which got me the first prize in the PC Master magazine!!! after two months, the excellent Pc-Pac and Pc-MsPac came out, and then after few months MAME was a reality...).
But I agree this has become Alixmar's lib and not a team lib. It's not a bad thing that we've gotten a framework going, but because it kinda came out of nowhere there's going to be disagreements.
Ok, forget my library (which is not a library at all, just a class). I am all ears.
cough
Sorry mate. I understand that you did quite a big amount of work. But, for example, can I take your tilemap editor to Linux ? Nope...because its a win32 only app.
I hope you understand now that a decent GUI lib for Allegro is needed.
Yes, but the idea isn't to help you with your GUI, it's to get a design most people can agree and collaberate on
But that's what I said. But...what is better than to communicate ideas other than the source code itself ?
The problem I see is that you have presented a design within a few days of this thread being started and are now implementing it
1) You are jumping into something without a proper design.
2) You have spent a lot of time previously on thinking about designs and are now implementing them.
Ooops. I didn't expect that people will think that I did not do a good design. As I have told before, I have made more than 50 tries to write a good gui. I have big experience with guis, and I know Win32, MFC, Qt, Swing, Photon, XLib and Motif/Lesstif.
Ok guys, as I said before, forget my library. I will personally use it as a testbed for new ideas. But...
Lets make a GUI, shall we ?
Sooner or later you will face the same problems as I did. And I can tell you, the drawing mechanism is the least of the problems. I can guarantee you that.
Ok, here are the requirements for now:
1) DRS (I hope Spell is happy now)
2) A graphics abstraction layer
Anything else ?
I agree with axilmar that DRS is NOT the way to go. I started thinking about DRS when working on my gui, and I realized even that wouldn't be enough. You want regions. plain double buffering is just wrong, so don't think about it (waaaaayyyyyy to slow)
Ok pardon my ignorance with DRS and regions, but we should all be open to ideas, and I have to give Axilimar credit if he did design or use all of those GUI's. Unfortunatly for me anyway a lot of this terminology goes over my head at times because I've never heard of it or implimented it myself. I guess I'd be more helpful for implimenting the stuff on top of the widget subsystem but I hope my discussion on how to impliment multiple theme types such as BeOS/Windows/Mac helped some. Another thing was the font class you had implimented with alfont, a simple textout won't really do it since that has no way of doing the && special character shortcut, text shadows (think beveled disabled text in windows), bold/italic/underline text, fixed width output, centered/right justified text, and maybe a way to justify it by clipping or adding the "..." at the end to make it fit. A font GUI class should be able to support at least that. I've managed in my lib to bring it down to a single function that handles everything via a bitwise flags variable. Another way would be a tagged text method where the format of the text is encoded within the text string that is sent in. I've had discussions on this implimentation myself and both methods have strong and weak ponts, the latter is better for a wordpad type application for example but would just call the first function within as new tags are read and set. Also a way to support multiple fonts should be implimented though the best way to do so is soemthing I have yet to figure out as well.
Ok with that said and Axilmar's apology lets keep this going please My first complaint though is that allegro shouldn't be the place for really decent discussion, remember it's post, wait, reply, wait which is a bottleneck. Is there some other way for realtime discussion and a way to post ideas/code snippets. Maybe a channel should be opened on IRC such as #algui or something where we can all get some realtime discussion going.
Also Axilmar remember that I'm rusty on C++, I have nothing against using it, but don't make it pure C++ or we will limit those who do not wish to switch to C++ just to use a lib. Also STL is broken on MingW so your <list> will need to be addressed, or is it just my install that's bad?
Anyway I'm glad to see this kind of flexibility, I thought you were just going to move on with your own implimentation but I'm glad things turned around for the best. For now though I'm going to read up on regions and how they fit in. Also if you want some quick region code look to me or Miran, we've both written code to split regions into non-overlapping regions/rectangles for our DRS implimentation. I do mine slightly differently though keeping the rectangles as wide as possible for scan line optimization.
It's true that you can't do anything serious with this type of discussion. We need to chat online about it.
DRS is only good if you have 1 level of widgets, like the Allegro gui does. If you have widget trees, you need to search the widget tree to see what widget to redraw, clip widgets, etc.
DRS/Regions are slow if you have lots of small widgets (tool buttons, for example). And if you have non-opaque widgets, then it gets very complicated. In Windows, for example, windows need to use other APIs to implement translucent windows than the normal ones (under Windows, a translucent window forces windows under it to draw themselves on a back buffer, and then the system updates the screen by blitting from the back buffer).
Double-buffering is good if you update and blit only the affected area. If you draw all widgets each time a tiny button changes, then you are out of luck. If you think about it, A gui usually has small changes and only the foreground widgets change often. So double buffering is enough and covers all cases of widget types (non-opaque, translucent, non-rectangular, small widgets, etc).
PS: I did not apologize. I don't need to apologise to anyone, because I haven't said anything to apologise for. Another person (*cough* Spell cough) must apologise to me for calling my code "some fluffy classes" without even understanding what is going on.
My event system is ready. It has the following capabilities:
-sloppy and click focus type
-drag-n-drop
-the main event loop is never delayed, even for "modal" dialogs and menus
-left handed mouse optional
-multiple click counter (depends on the widget to accept less than 3 clicks; it is provided for all those cases that a triple/multiple click is needed).
If anyone cares to see it, I will post it here.
I am working on my DRS system. Right now it works, and it takes 10 lines of code. I did it just to confirm that it is possible. I have to optimize it further though.
PS2: the DRS system is ready.
I would be VERY interested in seeing your system, axilmar. Just so you know.
Oh, and I remembered some more examples where action and a GUI system is required at the same time - MMORPGs. Look at Asheron's Call, or Tibia, or any other recent MMORPG, and you'll see what I mean.
Yes, I would also be interested in seeing this system.
Ok, here it is in the form of zipped source code(attached file which contains html help in the 'docs' directory: file 'index.html').
The DRS is enabled: every half second the screen is updated. See method 'setUpdateCallback'.
Press and hold the left button down over a widget and move it around with the mouse.
You can also change the video mode by using Alt+1, Alt+2, Alt+3 keys. You can exit the app by pressing Alt+X. All these are set using 'setAccelerator' at the root widget.
There are many other thing to see:
1) drag-n-drop (search for the 'beginDragAndDrop' call). Data query and exchange are done through the 'queryData' and 'getData' widget methods applied on the widget retrieved by the 'Widget::dragAndDropSourceWidget()' method.
2) event handling: uncomment 'textout' and 'textprintf' in the event handling methods to see event output as it happens.
3) geometry management: some widgets are maximized and always follow the size of the parents, some widgets enforce a specific layout on their children.
4) event grabbing: when you press a button over a widget, it grabs events, then it moves itself around on 'processGrabbedEvent' method. This allows for a widget to control the event flow without leaving the main event loop. My menus will be implemented like this.
5) focus management: press tab, tab+shift or the cursor keys to move the focus around. Widgets with the focus draw themselves with a thick black border. If you search the code, you will see that the focus goes to instances of the Test class that don't have children (method 'focusable()'.
6) sloppy focus: uncomment the line 'Event::setFocusFollowsMouse'.
7) timers: widget 'form1' has a timer that is called every 1 second. This will allow blinking cursors, button repeaters, autoscrolling when the mouse arrives at widget edges etc.
8) left handed mouse: uncomment the line with the call to 'setLeftHandedMouse'.
9) multiple clicks. The left button up handler prints the number of clicks. You can use 'Event::resetClicks()' to restrict the amount of clicks to 2 in each widget.
If I ever finish this library (highly impropable, since no one is willing to lend a hand), it will not use DRS by default, it will use double buffering, since the purpose of this library is to build Allegro tools.
(Right now I am off to designing my widget classes. If a development team is made to make the ultimate allegro gui, I will be very happy to participate and give a hand. When this happens, I will abandon my gui.)
PS: This work is the culmination of 7 years experience with guis.
That's a lot of code! When did you start writing all this? It all looks very impressive, even if I can't understand what's going on!
However, you did confuse me with this:
If I ever finish this library (highly impropable, since no one is willing to lend a hand), it will not use DRS by default, it will use double buffering, since the purpose of this library is to build Allegro tools.
(Right now I am off to designing my widget classes. If a development team is made to make the ultimate allegro gui, I will be very happy to participate and give a hand. When this happens, I will abandon my gui.)
So you probably won't finish this library and if people offer to help you to make a new GUI you will abandon it anyway? Or did I misunderstand..
I wrote it last week, Richard. As I have told earlier, I have done more than 50 tries to reach this state, so I was pretty sure of where to go. It's an amalgamation of various solutions I discovered on the go.
If people help me with this code, it will be finished. If people don't help me with this code, but they decide they will start from scratch, I will abandon mine and offer my workforce to the common cause.
Do not plan to abandon yours and join the common cause.
Do take note of the comments, but do not react on them. Let them work their way into your mind, and come back to them in a week, without the anger your feel now. You can't properly assess all what's been said here, now. And if you abandon yours to join one that's starting up now, it will most probably die out after a week of starting frenzy.
You'll need a load of will to do that though. It's
so easy to get disenheartened by the bad comments,
or those seen as bad. Which is why you should cool
off, then come back to the archive.
But, do not abandon yours.
EDIT:
I've downloaded the source and had a look:
Some comments on the source. Well, part of it (the event.h file and
the first half of widget.h).
I do realize you've just begun, but anyway, here goes:
don't assume left/middle/right buttons: make the button the message
applies to an argument of the message, like you do for keys (eg, you don't
have EVENT_KEY_A_UP, etc).
EVENT_LEFT_DROP:
eh ? you're assuming semantics here. Leave that to another higher level
layer of your lib only.
I'd suggest adding a type for your timestamps, not just int. It can prove
quite helpful down the road.
Also, add a type for geometry (eg, Maybe a vec2, which is two coords,
and Area, which is a couple of vec2s). This avoids passing 4 ints or
int*s to loads of routines and back.
Don't put everything and the rest in the event struct. It's supposed to
be akin to a virtual base class, in the ideal vision. X-Window, for one,
casts the rest of the event struct to a message specific struct, which
is not so pretty, but which conveys nicely the fact that the base event
is not dependant on whatever actual events you have. You can then add
events as you like without modifying the event struct.
I'd remove the routines from the event struct, and move them into a new
class, say, EventModifier, or something similar: you'd have a list of
them, which could filter the events as they're generated. You'd have
things like "LeftHandedMouseEventFilter", etc.
Have an EventQueue rather than statics in event to deal with the list.
This will help you have several possible policies for dealing with
those.
> Widget *root() const;
That should be static, I assume it's a typo.
Same for Widgets as for events, don't have too many statics, prefer a
manager instead, this yields more power to your API, due to possible
overriding.
Sometimes a pain, but, when it makes sense, add two versions of your
accessors, eg:
Widget *widgetAtXY(int,int);
const Widget *widgetAtXY(int,int) const;
///removes and destroys all children widgets
void destroyChildren()
{
while (_last) delete _last;
}
SIGSEGV
Good luck, and do not abandon it if you think it
could be a good thing to do for you and for others
to have.
Vincent, thanks for having a look at my code. Your comments are greatly appreciated.
Let me ask you a few questions and comment on your proposals.
don't assume left/middle/right buttons: make the button the message
applies to an argument of the message, like you do for keys (eg, you don't
have EVENT_KEY_A_UP, etc).
EVENT_LEFT_DROP:
eh ? you're assuming semantics here. Leave that to another higher level
layer of your lib only.
In other words, replace
EVENT_LEFT_BUTTON_DOWN, EVENT_RIGHT_BUTTON_DOWN, EVENT_MIDDLE_BUTTON_DOWN, EVENT_LEFT_BUTTON_UP, EVENT_RIGHT_BUTTON_UP, EVENT_MIDDLE_BUTTON_UP
with
EVENT_BUTTON_DOWN, EVENT_BUTTON_UP
so as that the class itself can choose what to do e ?
Something like this:
bool buttonDown(const Event &event) { switch (event.button()) { case LeftButton: <bla bla>... case RightButton: <bla bla>... case MiddleButton: <bla bla>... } }
Right now, if the buttons are reversed, the rightButtonDown and rightButtonUp will be called if the left button is pressed.
What are the advantages of the implementation you are proposing ?
I'd suggest adding a type for your timestamps, not just int. It can prove
quite helpful down the road.
I agree. How shall the type should be declared ? should it be global or part of the Event namespace ?
Also, add a type for geometry (eg, Maybe a vec2, which is two coords,
and Area, which is a couple of vec2s). This avoids passing 4 ints or
int*s to loads of routines and back
Ok, the Rect class will be put back. I was hoping that noone will notice this!!! I did it to save the library from one more class, but anyway it was a not so clever thought from the start. A Rect class is useful anyway.
Don't put everything and the rest in the event struct. It's supposed to
be akin to a virtual base class, in the ideal vision. X-Window, for one,
casts the rest of the event struct to a message specific struct, which
is not so pretty, but which conveys nicely the fact that the base event
is not dependant on whatever actual events you have. You can then add
events as you like without modifying the event struct
I wasn't planning for an event queue that custom events would go into. My definition of an event is either a mouse, a keyboard or a timer event. I.E. something coming from the hardware input devices only.
The way it is designed, it is impossible to do it, because the Event class is simply a place holder for the event data. If you check the Event internals, you will see that everything is in plain C; there is a reason for this: I don't want to mix event callbacks that might be called from interrupts with C++ (for example, I can't lock STL containers with LOCK_VARIABLE).
Another reason that I don't want Event to be a virtual base class is because if I did so, then the library would constantly allocate and deallocate Event-derived objects. Which means memory fragmentation. I don't think it is necessary to fragment the memory so much just to provide the programmer a feature to put his/her own events in the event queue.
In case you know from other libs (Qt for example) that custom events are useful in a multithreaded application, I have other solutions for thread communication (which would be a different library from a GUI anyway).
Have an EventQueue rather than statics in event to deal with the list.
This will help you have several possible policies for dealing with
those.
I would like only one event queue. If there was an event queue class, the programmer would be misled that each time he/she wanted to make an event loop, he/she has to declare an EventQueue (assumingly with different contents). By not having an EventQueue class, it is implicitely understood that all events come from the same queue.
To tell you the truth, I don't really see an advantage in what you are proposing. Would you care to elaborate with a few examples ?
> Widget *root() const;
That should be static, I assume it's a typo.
No, not at all. The static method is:
Widget *Widget::rootWidget();
which returns the current root (or desktop) widget.
The plain 'root()' method returns the Widget that is root of the widget tree that is called from, which might not be currently on the screen.
Same for Widgets as for events, don't have too many statics, prefer a
manager instead, this yields more power to your API, due to possible
overriding.
I tried that, but it did not look good enough to me. At one iteration of my design, I thought "let's make nothing static". The result was that I had to declare lots of "manager" type classes that holded the state of a gui, without significant gain: after all, there is only one screen, one widget tree active at a time, one mouse, one event queue...and this is unlikely to change in the near future. (Even if two or more screens where present, that would mean a "list" of root widgets, but not more than one event queues, not more than one case of drag-n-drop, etc).
And then there is another problem : how to retrieve the current 'static' information from inside the widget, without the widget knowing the "manager" variable that holds the current state. I would have to have statics, it can't be avoided.
So, instead of:
1 | class GuiStateManager { |
2 | public: |
3 | Widget *rootWidget(); |
4 | |
5 | static GuiStateManager * currentGuiStateManager(); |
6 | }; |
7 | |
8 | class Button { |
9 | public: |
10 | void popupMenu() { |
11 | currentGuiStateManager()->rootWidget()->add(popupMenu); |
12 | } |
13 | }; |
14 | |
15 | main() |
16 | { |
17 | GuiStateManager gui1; |
18 | EventQueue queue1; |
19 | Event event; |
20 | |
21 | while (app_loop) { |
22 | if (queue1.get(event)) event.dispatch(); |
23 | } |
24 | } |
I go for :
1 | class Button { |
2 | public: |
3 | void popupMenu() { |
4 | Widget::rootWidget()->add(popupMenu); |
5 | } |
6 | }; |
7 | |
8 | main() |
9 | { |
10 | Event event; |
11 | |
12 | while (app_loop) { |
13 | if (Event.get()) event.dispatch(); |
14 | } |
15 | } |
Which is much simpler and does the same job.
My line of thought is "keep it simple", simple enough to satisfy 90% of the user requirements. There is fine balance between simplicity and bloatness. I don't want my library to cross that limit. In other words, I don't want to put features that would be useful only in 10% of the use cases!!! the other 90% will blame me for not having followed the easiest way to do the job...
About events and mouse buttons: yes, that's what I meant.
> What are the advantages of the implementation you are proposing ?
Extensibility: when you decide to supprt a fourth button, you do
not have to add new event types; users who do not care which
button is pressed have their code still work; various things
like that.
> I agree. How shall the type should be declared ? should it be global
> or part of the Event namespace ?
Depends on whether you want it to be event specific or not. I'd
put it as a global one (or add an algui namespace on top of the whole
thing).
> My definition of an event is either a mouse, a keyboard or a timer event.
> .E. something coming from the hardware input devices only.
You don't cater for, say, tilt information from tablets. Even if you
don't plan to add more, it's often wise to leave you some space to do,
if you change your mind.
Another example: joystick: they would probably send other messages.
Allegro currently solves the problem by generating fake keyboard events
to replace joystick movement - not pretty.
> I can't lock STL containers with LOCK_VARIABLE
Interesting, I hadn't thought of that at all.
Could you keep a small core in C to do that, then have the C++ part that
checks for events do the conversion, since it would be out of interrupt
context ? That would solve the LOCK_VARIABLE problem while leaving you
able to use C++ fully.
> Another reason that I don't want Event to be a virtual base class
It was only an image - but the memory problem you mention would apply
too. A possible way to fix this is to have a given event log size. Say,
64 KB. You allocate this much, and use it as a rotating buffer (there
is a word for it, but I can't remember it right now: everytime you add
an event, you use the memory after the last event you created, and when
you get to the end of the buffer, you start again at the beginning of
the memory block, erasing what was here already. If events here have
not been used yet, you can either erase them (events too old are just
discarded) or block any new events from being registered until these
old events are read.
That's just one of the possibilitiesa nyway.
> In case you know from other libs (Qt for example)
Sorry, I don't. I'm just talking out of common sense and experience
with other programs
> I would like only one event queue.
My point was more with the ability to control how it behaves. It can
be a class and be a singleton, for isntance.
> If there was an event queue class, the programmer would be misled
> that each time he/she wanted to make an event loop, he/she has to
> declare an EventQueue
No, that is user error, and an easy one (assuming it is documented).
Do not deny yourself the ability to use something useful on the fears
(often without grounds) that someone could maybe misuse it. If you
fear that, state it in the docs. Add a FAQ. Add a comment in the header.
Better, try to catch the error at compile time, or, if not possible,
at creation time.
> To tell you the truth, I don't really see an advantage in what you
> are proposing. Would you care to elaborate with a few examples ?
Here's one:
I want my event queue to transform all occurences of right click and
left click occuring at the same time (or narrowly) into a middle click,
like was often done when PC mice had only two buttons and Sun stations'
ones had three (Ah, we had optical mice years ago, before MS decided
that yes, it was a good thing...)
Anyway.
I see your point in making the lib simple though. My comments would
aim to make it more powerful, but at a price of complexity. So feel free
to disregard.
Keep it up
Thanks Vincent, this is the type of discussion I expect on these boards.
Extensibility: when you decide to supprt a fourth button, you do
not have to add new event types; users who do not care which
button is pressed have their code still work; various things
like that.
Ok, proposition accepted.
Depends on whether you want it to be event specific or not. I'd
put it as a global one (or add an algui namespace on top of the whole
thing).
In the end, maybe I put a namespace to the library. The time type would be a global type.
You don't cater for, say, tilt information from tablets. Even if you
don't plan to add more, it's often wise to leave you some space to do,
if you change your mind.
Maybe in the end I'll change the event internals, if it is requested by many. Right now, I don't think that anyone will care.
Another example: joystick: they would probably send other messages.
Allegro currently solves the problem by generating fake keyboard events
to replace joystick movement - not pretty
Joystick to move the mouse around ? I don't see a reason for this. There is no computer without a mouse these days. Again, this will be implemented only if requested by many users.
It was only an image - but the memory problem you mention would apply
too. A possible way to fix this is to have a given event log size. Say,
64 KB. You allocate this much, and use it as a rotating buffer (there
is a word for it, but I can't remember it right now: everytime you add
an event, you use the memory after the last event you created, and when
you get to the end of the buffer, you start again at the beginning of
the memory block, erasing what was here already. If events here have
not been used yet, you can either erase them (events too old are just
discarded) or block any new events from being registered until these
old events are read.
Right now I am using a simple _EVENT struct to create a rotating buffer (struct _QUEUE). Very simple. If I add internal memory management on event allocation, I will make it very complex. I would like to avoid complexity.
Sorry, I don't. I'm just talking out of common sense and experience
with other programs
If I was to make a high level generic GUI toolkit, then I would definitely incorporate your proposals. Right now, I am thinking along the lines "a simple GUI library". Since I am solo, I don't have the time and appetite to add more functionality. I hope you understand this.
I want my event queue to transform all occurences of right click and
left click occuring at the same time (or narrowly) into a middle click,
like was often done when PC mice had only two buttons and Sun stations'
ones had three (Ah, we had optical mice years ago, before MS decided
that yes, it was a good thing...)
This can be easily done from inside the mouse_callback. Doesn't Allegro offer the capability to emulate a 3 button mouse anyway ?
I see your point in making the lib simple though. My comments would
aim to make it more powerful, but at a price of complexity. So feel free
to disregard.
Thank you. Really. A GUI lib is quite a complex thing, and I am alone. I have given much thought into many topics, but I can't handle it all alone. That's why I aim for simplicity and sacrifice a few complex functionalities.
Do you have any proposition for the widget set ? which widgets would you like to see ?
> Joystick to move the mouse around ? I don't see a reason for this.
No, this is just an example. The main point being,
if you ever need to add new messages, you want to
make this as easy and non intrusive as possible.
> This can be easily done from inside the
> mouse_callback. Doesn't Allegro offer the
> capability to emulate a 3 button mouse anyway ?
Yes, but, once again, it's an example to show that
you can do many things if you design an extensible
base - you don't have to have a large base, just
one that can be built upon.
Virtual functions help. And especially modifier
objects which can be attached to something.
You don't have to add plenty of things to cater
for all needs, just the ability to possibly add
things to cater for this or that.
Just putting my point forward, I do understand
that you're aiming at another level, not as
complex as what I'm describing.
Yes, that's my purpose. If something comes up, I will modify the library, but for now, "keep it simple!!!"...as simple as it gets!!!
Do you have a list of widgets that you would like to see implemented ?
EDIT:
Here is my list:
static widgets(widgets that don't respond to user input):
--------------
Label (for text)
Image (for bitmaps)
Separator (either horizontal or vertical)
Frame/Panel (either raised, sunken or flat)
buttons:
-------
PushButton
ToggleButton
CheckBox
RadioButton
ButtonFrame
ToolPushButton
ToolToggleButton
ToolCheckButton
ToolRadioButton
scrolling:
---------
Slider (horizontal or vertical)
ScrollBar (horizontal or vertical)
Menus:
------
MenuBar
PopupMenu
MenuBarItem
ActionMenuItem
CheckMenuItem
RadioMenuItem
SubmenuItem
Text editing:
-------------
TextBox (single line)
TextEditor (multi line)
SpinBox
ComboBox
views:
------
ListView (a.k.a. list box)
TreeView
TableView
HtmlView
IconView (for file dialogs)
Windowing:
----------
WindowFrame (caption, min/max buttons etc)
Dialog (A window frame that knows how to do a modal loop)
Framework:
----------
ToolBar (a window frame that positions its children according to selected docking)
Workspace (central widget for the main window)
MainWindow (a window frame that knows how to dock children toolbars)
Common dialogs:
---------------
MessageBox
FileDialog
PaletteDialog
FontDialog
Please note that widget names are not definitive: if someone proposes something better, it will be accepted. I tried to keep the names as close to the common terminology as possible.
The programming style would be very simple: just put one widget inside the either from parent to child, connect signals and slots and make an event loop. Example:
1 | #include <algui.h> |
2 | |
3 | |
4 | //called to create a new document |
5 | void onNewDocument() |
6 | { |
7 | } |
8 | |
9 | |
10 | //called to open a document |
11 | void onOpenDocument() |
12 | { |
13 | FileDialog dlg; |
14 | |
15 | dlg.setCaption("Open file"); |
16 | dlg.addFileType("exe", "Executable"); |
17 | dlg.addFileType("lib", "Library"); |
18 | dlg.addFileType("ico", "Icons"); |
19 | if (!dlg.exec()) return; |
20 | <bla bla open file> |
21 | } |
22 | |
23 | |
24 | //main |
25 | int main() |
26 | { |
27 | //install Allegro |
28 | <bla bla bla> |
29 | |
30 | //load configuration |
31 | <bla bla bla> |
32 | |
33 | //set video mode |
34 | <bla bla bla> |
35 | |
36 | //load resources |
37 | <bla bla bla> |
38 | |
39 | //create the main window |
40 | MainWindow *mainWindow = new MainWindow(SCREEN_W, SCREEN_H, "My Application"); |
41 | |
42 | //create the menu bar |
43 | MenuBar *menuBar = new MenuBar(mainWindow); |
44 | MenuBarItem *fileItem = new MenuBarItem(menuBar, "&File"); |
45 | MenuBarItem *editItem = new MenuBarItem(menuBar, "&Edit"); |
46 | MenuBarItem *viewItem = new MenuBarItem(menuBar, "&View"); |
47 | MenuBarItem *optionsItem = new MenuBarItem(menuBar, "&Options"); |
48 | |
49 | //create the file menu |
50 | PopupMenu *fileMenu = new PopupMenu(fileItem); |
51 | ActionMenuItem *newItem = new ActionMenuItem(fileMenu, "&New", "Ctrl+N", new FunctionSlot<>(onNewDocument)); |
52 | ActionMenuItem *openItem = new ActionMenuItem(fileMenu, "&Open...", "Ctrl+O", new FunctionSlot<>(onOpenDocument)); |
53 | |
54 | <bla bla create the rest of the menu items> |
55 | |
56 | //create the toolbar |
57 | ToolBar *standardToolBar = new ToolBar(mainWindow, "ToolBar"); |
58 | ToolButton *newButton = new ToolButton(standardToolBar, (BITMAP *)resources[DAT_NEW_BITMAP].dat, new FunctionSlot<>(onNewDocument)); |
59 | |
60 | <bla bla create the rest of the toolbars and toolbar items> |
61 | |
62 | //main loop |
63 | Event event; |
64 | while (app_loop) { |
65 | if (event.get()) event.dispatch(); |
66 | } |
67 | |
68 | return 0; |
69 | } |
Not particularly.
Though you'd better make your GUI independant of
the actual widgets - widgets use the GUI, so the
core can be coded without knowing the widgets.
After, you can add widgets, or maybe another lib
which adds widgets. So you can have a plethora of
widgets to choose from, without bloat. Of course,
some widgets will require other widgets (eg, a
button will require a label, which will in turn
require a base widget).
Do the widgets you like first, I'd say
Regarding labels: They do get userinput. One of the main features of a label is to provide a shortcut key for the widget it's associated to. So a label would react on the keypress by focusing / selecting the widget it's associated to.
I'd also suggest to avoid a single root widget. If you're using c++ use a list (or any other container) as root. If you just have one root widget it doesn't hurt, and it allows you to have several widgets which don't have a common parent.
If you decide to stick with your single root widget solution, don't make it a global. Create a class and/or struct to contain your global information, so the complete state of the gui is inside a single object. This allows the user to use several instances of the gui.
Not particularly.
Though you'd better make your GUI independant of
the actual widgets
Do you mean to have two libraries ? one for the 'window system' and one for the 'widgets' ? I tried that also. I tried to make a widget set using MxWindows as the gui. It works, but the actual window system is a tiny fraction of the toolkit, so, for simplicity reasons, I thought they should be intergrated. Just "#include <algui.h>", link with "algui.lib.so.whatever" and be done with it.
Regarding labels: They do get userinput. One of the main features of a label is to provide a shortcut key for the widget it's associated to. So a label would react on the keypress by focusing / selecting the widget it's associated to
Gotcha. This will be provided. Widget categorization above was for just showing what I have in my mind.
I'd also suggest to avoid a single root widget. If you're using c++ use a list (or any other container) as root. If you just have one root widget it doesn't hurt, and it allows you to have several widgets which don't have a common parent.
If you decide to stick with your single root widget solution, don't make it a global. Create a class and/or struct to contain your global information, so the complete state of the gui is inside a single object. This allows the user to use several instances of the gui
AlGui does have one "root" widget, i.e. only one root widget is currently on the screen. But widget trees can happily live off the screen, until they are added to a widget that is on-screen or be made "root" with a call to 'setRoot()'.
Here is the code:
//returns the current "root" widget, i.e. the root widget that is on the screen static Widget *Widget::rootWidget(); //returns the root widget of this tree Widget *Widget::root() const; //sets the current root widget bool Widget::setRoot();
If the user wants to make a multiscreen application, he/she can call 'setRoot' to set a widget as the current root. He/she can construct multiple widget trees, each one with their own root (the local tree's root), and activate each widget tree by calling 'setRoot'.
EDIT:
To all: do you find the above widget list adequate ? any other widget suggestions ?
I find the above widget list comprehensive.
It seems like you're going for a large set of
widgets, which encompass a wide array of functionalities (eg, an HTML renderer is not a
trivial thing to do). So it does not seem to fit
with your stated goal of "a simple GUI for simple
uses".
That said, I can't think of other non specialized
widgets for you to add to the list.
AlGui does have one "root" widget, i.e. only one root widget is currently on the screen. But widget trees can happily live off the screen, until they are added to a widget that is on-screen or be made "root" with a call to 'setRoot()'.
Assume you hae a game.
Assume you want to use some gui components for the game.
Assume the GUI should run with a lower priority than the renderer (say with 20fps instead of 60fps)
Assume you want to have several gui components on screen which are not connected (say a map width startegic control buttons / list, an area with messages, and one area with unit control buttons / infos).
The gui should be displayed on top of the game running at 60 fps, but it should render itself / react to events only with 20fps.
If you have several top level widgets, no problem. But how do you handle this with a single top level widget?
(eg, an HTML renderer is not a
trivial thing to do). So it does not seem to fit
with your stated goal of "a simple GUI for simple
uses".
It will only display HTML 1.0 and only some basic tags. It is necessary if an application must show help files. I have already done some of the work in past projects.
Actually, it is not very difficult to make an HTML renderer. Basically, each HTML renderer widget is a giant "list box". Each row of the list box can contain text, graphics, widgets, etc. You can fill this special "list box" from the HTML stream, as the stream is retrieved from the disk/network. Each row's size will be modified according to the size of the elements of the received data on the fly.
Assume you hae a game.
Assume you want to use some gui components for the game.
Assume the GUI should run with a lower priority than the renderer (say with 20fps instead of 60fps)
Assume you want to have several gui components on screen which are not connected (say a map width startegic control buttons / list, an area with messages, and one area with unit control buttons / infos).
The gui should be displayed on top of the game running at 60 fps, but it should render itself / react to events only with 20fps.
If you have several top level widgets, no problem. But how do you handle this with a single top level widget?
You make a root widget that is the container of all these unconnected widgets. This root widget will draw nothing, it will be positioned at the screen 0, 0 pixel and it will have the size of the screen; its children, the widgets you are interested in, will have a x, y position relative to the root equal their screen position, since their parent is at screen 0, 0.
In other words:
1 | class GameRootWidget : public Widget { |
2 | public: |
3 | protected: |
4 | //draw nothing |
5 | virtual void paint() { |
6 | } |
7 | }; |
8 | |
9 | |
10 | int main() |
11 | { |
12 | <bla bla initialize Allegro> |
13 | |
14 | //make the root widgets |
15 | GameRootWidget *wgt = new GameRootWidget(); |
16 | wgt->setGeometry(0, 0, SCREEN_W, SCREEN_H); |
17 | |
18 | //make the widgets you are interested in |
19 | Button *btn1 = new Button(root, 0, 0, 32, 32); |
20 | Button *btn2 = new Button(root, 32, 0, 32, 32); |
21 | } |
> Actually, it is not very difficult to make an
> HTML renderer.
It's hard to make a good one
If you're going for HTML 1.0, then it should be
OK, as you don't have tables. For the rest, you're
right that it's just mostly a collection of lines
with adjustable height, but there are lots of small
things, easy by themselves, but there are a lot of
them. Things like lists, blockquote spacing, etc.
You end up having a quite large "state" stack. At
least, I ended up that way.
It's hard to make a good one
It would be good to have one, though. For showing help files.
The stack can't be avoided.