Allegro.cc - Online Community

Allegro.cc Forums » Programming Questions » Best way to abstract allegro events for my gui library

This thread is locked; no one can reply to it. rss feed Print
Best way to abstract allegro events for my gui library
Edgar Reynaldo
Major Reynaldo
May 2007
avatar

So like the title says, I am trying to figure out the best method to abstract Allegro 5's event system into my own event system for use with my GUI. My GUI also needs to be compatible with other libraries like SDL / SFML / what the hell ever else someone wants a port of.... as well.

So, my first thought is to duplicate Allegro, so that A5 users don't have to adjust much to use my GUI's event system. This would mean copying most ALLEGRO_EVENT_BLAH_BLAH into EAGLE_EVENT_BLAH_BLAH and duplicating the event union and structures.

The main focus right now is to get the abstraction ready, so I can fill in the backend for Allegro 5 and start porting all my widgets to the new library.

So, any ideas / tips / helpful comments?

Arthur Kalliokoski
Second in Command
February 2005
avatar

Consider having several event loops, whether the gui is active or not could select one specialized for the gui, another could be for game movement keys, whatever.

They all watch too much MSNBC... they get ideas.

Edgar Reynaldo
Major Reynaldo
May 2007
avatar

Does anybody have any ideas for abstracting graphics libraries? I know theres AGUI, who wrote that again?

Here's what my gui looks like now, with A4 driving it :
{"name":"607020","src":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/f\/0\/f06a193524d99eb5d5a68d3eacf3de3f.png","w":812,"h":632,"tn":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/f\/0\/f06a193524d99eb5d5a68d3eacf3de3f"}607020

Do you like the way it looks? What options should a user have when drawing widgets in a gui library?

What is the best way to integrate layout managers? Give every widget handler (frame / window / subwindow) a layout manager?

So - thoughts?

axilmar
Member #1,204
April 2001

So, any ideas / tips / helpful comments?

The best way to deal with it is to create one event for Allegro, named CAllegroEvent or whatever your naming convention is.

This event will not be available to your users though, unless they explicitly process it. Your event loop will catch this event and create other events for the GUI, like mouse down/mouse up, mouse/move etc.

In this way, you can put other event systems in, if you wish. For example, you could have an CWin32MessageEvent for all Win32 messages, and then have your GUI create the same mouse down/up/move etc events that are also created for CAllegroEvent.

Does anybody have any ideas for abstracting graphics libraries?

Provide a CCanvas class with drawing methods, selecting pens and brushes, etc. Then let widgets draw to that.

Quote:

Do you like the way it looks?

I'll be honest: I don't like it very much.

Quote:

What options should a user have when drawing widgets in a gui library?

The user should be allowed to skin widgets, i.e. select all its graphical aspects at runtime.

Quote:

What is the best way to integrate layout managers? Give every widget handler (frame / window / subwindow) a layout manager?

Create widgets for layout, i.e. integrate the layout mechanism with widgets. Each widget should have its own layout, and a special group of your widgets will be about layout only.

Using a secondary tree of layout objects makes it more difficult to create layouts.

Elias
Member #358
May 2000

axilmar said:

Using a secondary tree of layout objects makes it more difficult to create layouts.

I'd say that's the same mistaken believe which makes CSS as hard as it is. <table><tr><td> elements in HTML are extremely simple, everyone can for example make a 3 column layout in a minute. CSS on the other hand is not simple at all, a 3 column layout requires hours of research.

--
"Either help out or stop whining" - Evert

Thomas Fjellstrom
Member #476
June 2000
avatar

I haven't really found a simpler/better method of widget layouts than what Qt uses. Other than explicit widgets that layout their children.

axilmar said:

Provide a CCanvas class with drawing methods, selecting pens and brushes, etc. Then let widgets draw to that.

I do something similar for my little Canva5 library. I used Qt's setup as an example, so its fairly similar:

http://git.tomasu.org/canva5.git/blob/refs/heads/prototype:/core/src/Display.cpp
http://git.tomasu.org/canva5.git/blob/refs/heads/prototype:/core/src/Bitmap.cpp
http://git.tomasu.org/canva5.git/blob/refs/heads/prototype:/core/src/PaintEngine.cpp
http://git.tomasu.org/canva5.git/blob/refs/heads/prototype:/core/src/Drawable.cpp
http://git.tomasu.org/canva5.git/blob/refs/heads/prototype:/core/src/Painter.cpp

I rather like it. All I have to do is implement a new Bitmap class and a new Display class, and a new PaintEngine class and the lib is instantly ported to another backend.

append: One of these days I intend on adding some GUI classes to it. But it can be used as an engine for 2d games quite nicely. Just spam some Item's of various types at the Model, and it'll take care of everything for you. You can even create multiple views into the same world! It's really quite neat.

--
Thomas Fjellstrom - [website] - [email] - [Allegro Wiki] - [Allegro TODO]
"If you can't think of a better solution, don't try to make a better solution." -- weapon_S
"The less evidence we have for what we believe is certain, the more violently we defend beliefs against those who don't agree" -- https://twitter.com/neiltyson/status/592870205409353730

Luiji99
Member #12,254
September 2010

I really like the Qt/GTK+ design. It requires much less compile-and-test sequences since most of the layout is calculated automatically. The alternative would be a GUI designer, but I tend to find WYSIWYG output hideous.

Programming should be fun. That's why I hate Java.

Edgar Reynaldo
Major Reynaldo
May 2007
avatar

axilmar said:

I'll be honest: I don't like it very much.

Being honest doesn't help much if you are not also useful.....

What is it you do not like?
The style of the buttons / sliders / radio / menu / drop down / file browser - what? ? ? ? Of course there will be image buttons so you can make it look whatever way you want it to, and you can define hit boxes. Simple event based system, widgets give messages about what happens and then you cherry pick the messages you want and hook them up to other things. It's also very easy to derive new widgets, all you have to do is implement 3 simple methods, HandleEvent(Event e), Update(double dt), and Display(GraphicsContext gc).

What exactly is this QT style that you all mention? I am not familiar with their source code or programming with their library...

Also, what kind of interface do you like? Constructors with every parameter possible, or basic constructors and specialized setup functions....???

Thomas Fjellstrom
Member #476
June 2000
avatar

What exactly is this QT style that you all mention? I am not familiar with their source code or programming with their library...

It provides a dynamic layout mechanism. For the most part there is no hand placement of widgets.

Quote:

Also, what kind of interface do you like? Constructors with every parameter possible, or basic constructors and specialized setup functions....???

I'd go with simple ctors and a bunch of methods.

--
Thomas Fjellstrom - [website] - [email] - [Allegro Wiki] - [Allegro TODO]
"If you can't think of a better solution, don't try to make a better solution." -- weapon_S
"The less evidence we have for what we believe is certain, the more violently we defend beliefs against those who don't agree" -- https://twitter.com/neiltyson/status/592870205409353730

Edgar Reynaldo
Major Reynaldo
May 2007
avatar

It provides a dynamic layout mechanism. For the most part there is no hand placement of widgets.

Moose, you are referring to the somewhat javax / swing type widget layout mechanisms? Where you provide a window / container with a layout widget? I will be providing layout widgets this time, the widget handlers will own a layout manager, whether a dumb default one where you can hand place widgets or a smart one that lays out and resizes to fill / fit / align / whatever else you want.....

Quote:

I'd go with simple ctors and a bunch of methods.

I think this is simpler and better too. More like Javax and AWT. That way you don't have to memorize long constructor parameters. :P

jmasterx
Member #11,410
October 2009

I know theres AGUI, who wrote that again?

I wrote Agui.

Thomas Fjellstrom
Member #476
June 2000
avatar

Moose, you are referring to the somewhat javax / swing type widget layout mechanisms? Where you provide a window / container with a layout widget? I will be providing layout widgets this time, the widget handlers will own a layout manager, whether a dumb default one where you can hand place widgets or a smart one that lays out and resizes to fill / fit / align / whatever else you want.....

Qt has a special layout object you assign to widgets, any widgets. But dedicated layout widgets also work. Same idea. Just so long as you don't force me to layout my gui by hand, I won't hate you.

--
Thomas Fjellstrom - [website] - [email] - [Allegro Wiki] - [Allegro TODO]
"If you can't think of a better solution, don't try to make a better solution." -- weapon_S
"The less evidence we have for what we believe is certain, the more violently we defend beliefs against those who don't agree" -- https://twitter.com/neiltyson/status/592870205409353730

Edgar Reynaldo
Major Reynaldo
May 2007
avatar

jmasterx
Member #11,410
October 2009

Since Agui favors Listeners over bubbling messages, the way I did it might not be exactly what you need (since I think you mentioned integrating your messages?)

But here is basically how I did it.

I have a base class Image, Font, Font Loader, Image Loader, Graphics Context...

I expect the back-end to implement all of these.

The loaders are pretty straight forward:

  class AGUI_BACKEND_DECLSPEC Allegro5FontLoader :
    public FontLoader
  {
  public:
    Allegro5FontLoader(void) {}
    ~Allegro5FontLoader(void) {}

    virtual Font* loadFont(const std::string &fileName, int height);
  };

They implement the base class's load method and return an abstract Font, Image, etc.

The way Input works is as follows.
I have a base input class that allows you to queue up Mouse and Keyboard events.

The Input Handler base looks like:

#SelectExpand
1namespace agui 2{ 3 4 /** 5 * Abstract class for Input. 6 * 7 * Should implement: 8 * 9 * A method to receive a back end specific event and convert it 10 * to MouseInput or KeyboardInput. 11 * 12 * getTime (default uses std::clock) 13 * 14 * Should respect: 15 * 16 * isMouseEnabled 17 * 18 * isKeyboardEnabled 19 * @author Joshua Larouche 20 * @since 0.1.0 21 */ 22 23 class AGUI_CORE_DECLSPEC Input 24 { 25 double startTime; 26 std::queue<MouseInput> mouseEvents; 27 std::queue<KeyboardInput> keyboardEvents; 28 bool mouseEnabled; 29 bool keyboardEnabled; 30 protected: 31 /** 32 * Default constructor. 33 */ 34 Input(void); 35 public: 36 /** 37 * Called by the Gui in its logic loop. Used for non event driven back ends. 38 */ 39 virtual void pollInput(); 40 /** 41 * Pushes a mouse event which will be dequeued and processed in the next logic loop. 42 */ 43 void pushMouseEvent(const MouseInput &input); 44 /** 45 * Pushes a keyboard event which will be dequeued and processed in the next logic loop. 46 */ 47 void pushKeyboardEvent(const KeyboardInput &input); 48 /** 49 * @return True if no mouse events are queued. 50 */ 51 bool isMouseQueueEmpty() const; 52 /** 53 * @return True if no keyboard events are queued. 54 */ 55 bool isKeyboardQueueEmpty() const; 56 /** 57 * Called by the Gui to process the event. 58 * @return The keyboard event information and removes it from the queue. 59 */ 60 const KeyboardInput dequeueKeyboardInput(); 61 /** 62 * Called by the Gui to process the event. 63 * @return The mouse event information and removes it from the queue. 64 */ 65 const MouseInput dequeueMouseInput(); 66 /** 67 * @return The amount of time the application has been running in seconds. 68 */ 69 virtual double getTime() const; 70 /** 71 * Set whether or not keyboard input is enabled for the Gui. 72 */ 73 void setKeyboardEnabled(bool enabled); 74 /** 75 * Set whether or not mouse input is enabled for the Gui. 76 */ 77 void setMouseEnabled(bool enabled); 78 /** 79 * @return True if mouse input is enabled for the Gui. 80 */ 81 bool isMouseEnabled() const; 82 /** 83 * @return True if keyboard input is enabled for the Gui. 84 */ 85 bool isKeyboardEnabled() const; 86 /** 87 * Default destructor. 88 */ 89 virtual ~Input(void); 90 }; 91}

The way this works is, there is a Gui object that handles just about everything event related. It takes in an Input and GraphicsContext.
Example:

#SelectExpand
1class Scene : public agui::ActionListener, public agui::WidgetListener, 2 public SceneEventProvider 3 { 4 Timer* m_gameTimer; 5 DeviceManager* m_settings; 6 DynamicUIManager* m_dynamicUI; 7 SceneManagerMessage* m_sceneMessenger; 8 9 agui::Gui m_gui; 10 agui::Allegro5Input m_a5GuiInput; 11 agui::Allegro5Graphics m_a5GuiGraphics; 12 agui::Allegro5CursorProvider m_a5GuiCursor; 13..... 14 15 Scene::Scene(void) 16 : m_gameTimer(NULL), 17 m_sceneMessenger(NULL), 18 m_settings(NULL), 19 m_frame(NULL) 20 { 21 m_gui.setInput(&m_a5GuiInput); 22 m_gui.setGraphics(&m_a5GuiGraphics); 23 m_gui.setCursorProvider(&m_a5GuiCursor); 24 m_gui.setToolTipShowLength(10.0); 25 m_gui.setHoverInterval(0.75); 26 m_gui.setMaxToolTipWidth(400); 27 28 }

What the Gui does with this Input is:
In each logic() update it:
-calls input->poll()
-dequeues and dispatches queued, abstracted input.

It is the backend implementation's responsibility to convert whatever input it receives into agui::MouseInput and agui::KeyboardInput.

Now, in a case of A4 or SDL 1.2, the Input::poll() method would be implemented.

In an event driven style such as A5, I created a processInput(ALLEGRO_EVENT& evt).

That is called like this:

#SelectExpand
1 void Scene::processGuiInputEvent( ALLEGRO_EVENT* evt ) 2 { 3 m_a5GuiInput.processEvent(*evt); 4 } 5 6... 7In Scene Manager... 8//main loop 9 while(m_gameIsRunning) 10 { 11 12 handled = false; 13 al_wait_for_event(queue,&evt); 14 15 bool hasNext = al_peek_next_event(queue,&next); 16 if(hasNext && next.type == ALLEGRO_EVENT_TIMER) 17 { 18 al_drop_next_event(queue); 19 } 20 //render the scene 21 if(m_needsRedraw && al_is_event_queue_empty(queue)) 22 { 23 m_currentScene->render(); 24 m_needsRedraw = false; 25 } 26 27 defaultBeginEventHandler(&evt); 28 m_currentScene->processEvent(&evt,handled); 29 30... 31 void SceneManager::defaultBeginEventHandler( ALLEGRO_EVENT*evt ) 32 { 33 34 m_currentScene->processGuiInputEvent(evt); 35 36 if(evt->type == ALLEGRO_EVENT_TIMER && evt->timer.source == m_gameTimer) 37 { 38 m_needsRedraw = true; 39 m_currentScene->processGuiLogic(); 40 m_devices->getNetClient()->tick(); 41 } 42 else if(evt->type == ALLEGRO_EVENT_DISPLAY_RESIZE) 43 { 44 sendResizeMessage(evt->display.width,evt->display.height); 45 } 46 }

That is the basic idea... create an abstract set of functionality that any backend should be able to support, then implement it as a subclass and use polymorphism (and casting) to do the backend specific action.

As for Layouts, I probably should have made every Widget own a layout, but instead I made it so that a Layout has complete control over the Widgets it is laying out.

I prefer the idea of every Widget having a layout, and preferably a NULL layout can be supported.

As to what you should work on with it... I guess games or level editors...

I like your GUI, I think it looks quite nice. The default skin doesn't matter anyways :p

Take Agui for example, it looks much nicer in action!

{"name":"607025","src":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/c\/5\/c5f02ac525f2c373d6fa7ae914a70bf9.png","w":476,"h":281,"tn":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/c\/5\/c5f02ac525f2c373d6fa7ae914a70bf9"}607025

axilmar
Member #1,204
April 2001

@Edgar:

I do not like the artistic style of your GUI. It's primitive.

Regarding the API, another way to handle events is to attach event handlers to interactive objects, like Flash does. You can read the Actionscript 3 documentation online, in Adobe's site.

Thomas Fjellstrom
Member #476
June 2000
avatar

axilmar said:

Regarding the API, another way to handle events is to attach event handlers to interactive objects

ie: listeners?

--
Thomas Fjellstrom - [website] - [email] - [Allegro Wiki] - [Allegro TODO]
"If you can't think of a better solution, don't try to make a better solution." -- weapon_S
"The less evidence we have for what we believe is certain, the more violently we defend beliefs against those who don't agree" -- https://twitter.com/neiltyson/status/592870205409353730

axilmar
Member #1,204
April 2001

Yes, listeners. In Flash, every interactive object has the methods addEventListener and removeEventListener.

My favorite thing though is signals and slots.

Edgar Reynaldo
Major Reynaldo
May 2007
avatar

axilmar said:

@Edgar:I do not like the artistic style of your GUI. It's primitive.

It's just a default style. You can style it any way you want, or at least you will be able to. I'm going to shuffle things around a bit now and let the user create the gui components themselves, like scroll bar buttons and scrollers. So you can make it look just like OSX if you want to. I'm not going to try and create system themes, but I should have a mechanism for loading guis from script files so the user could write their own system themes.

@Everyone
Should I include support for nine patches, for dynamic resizing of images?

What is your favorite way to design a gui? Would you like a widget placement editor? Ie.. a gui editor?

Would writing handlers for extra large video bitmaps that the gpu can't handle be useful to anyone? Do you want support for large video bitmaps? (Made up of several smaller video bitmaps and pieced together like a jigsaw)

@Jmasterx
What is the AGUI_BACKEND_DECLSPEC declaration for? Do you need that for building on different platforms?

For my input I decided to follow A5 and use a EagleEventHandler base class along with base classes for system, graphics context, timers, and input. Every backend has to implement these to function.

jmasterx said:

In an event driven style such as A5, I created a processInput(ALLEGRO_EVENT& evt). That is called like this:

void Scene::processGuiInputEvent( ALLEGRO_EVENT* evt )
  {
    m_a5GuiInput.processEvent(*evt);
  }

Doesn't this make your library depend on A5?

axilmar
Member #1,204
April 2001

What is your favorite way to design a gui?

Code. I've never seen a GUI being totally 100% designable by an editor.

jmasterx
Member #11,410
October 2009

Doesn't this make your library depend on A5?

No, because processEvent(ALLEGRO_EVENT* ...) is a method that is specific to Allero5Input class.

The GUI is only aware of the Input class.

If you used Agui with SDL you might have an SDLInput which might have processEvent(SDL_EVENT*...)

agui::Allegro5Input::processEvent(...) calls Input::queueMouseEvent and queueKeyboardEvent. So there is no dependency on Allegro5.

These are generically dequeued in Gui::logic();

In my example, my game uses Allegro5 to power it so I find it acceptable to make Allegro5 calls in it.

But Agui itself can be compiled without any exteral libraries, check out the cmake file.

The DECL_SPEC allow the library to be compiled as static or as a DLL; MSVC requires each class to be exported using _declspec when building as a DLL. I have 1 for core and 1 for backend to avoid conflicts in certain compilation situations.

But because of how MSVCRT and such work, I don't recommend anyone use agui as a DLL unless you're using the exact same runtime. But it does work.

Agui has nine patch support if you want to look into that. It is incredibly useful if you're going to support layouts.

Edgar Reynaldo
Major Reynaldo
May 2007
avatar

Well, I decided to just make a thin wrapper over EagleEvent's. The events and the keycodes and everything mesh with allegro - ALLEGRO_EVENT_KEY_UP is now EAGLE_EVENT_KEY_UP and so on.... So I guess it will just be a superset of all the library backends it supports. :P

Here is the sourceforge project page I setup :
https://sourceforge.net/p/eagle5gui/

And the svn repo :
svn checkout "svn://svn.code.sf.net/p/eagle5gui/code/trunk eagle5gui-code"

Please take a look at what I have so far, and tell me what you think of it! :)

jmasterx
Member #11,410
October 2009

The way you do it is how I did it too. I also mapped the gui to Allegro5 ;)

I looked over the code you committed and it looks fine. I didn't find Widgets but I guess you'll add those soon.

Edgar Reynaldo
Major Reynaldo
May 2007
avatar

Yeah, I haven't ported any widgets yet, first I have to finish the backend for A5 and then I can rebuild my gui on top of it. I shouldn't have to change a lot to port my widgets, but I will have to change all the drawing routines, and their input checks will change to use events. So, a little modification is necessary.

Go to: