Allegro.cc - Online Community

Allegro.cc Forums » The Depot » lgui: a new gui for Allegro 5

This thread is locked; no one can reply to it. rss feed Print
 1   2 
lgui: a new gui for Allegro 5
Edgar Reynaldo
Major Reynaldo
May 2007
avatar

frank.n said:

Yeah, they are declared individually per widget, the widget base class has none. Currently, it's usually just one per widget with one distinct signature each, mostly emitting one value only. They are intended for the semantically interesting stuff that is specific for the widget.

That seems problematic if you want a clean interface. I like the way my WidgetMsg class works. It wraps a WidgetBase*, an unsigned int for the topic, and an unsigned int for the message.

Then when you want to connect functionality, you check the gui queue for a WidgetMsg matching your source and topic and message.

while (gui.HasMessages()) {
   WidgetMsg wmsg = gui.TakeNextMessage();
   if (wmsg == WidgetMsg(&mybutton , TOPIC_BUTTON_WIDGET , BUTTON_CLICKED)) {
      /// user pressed our special button, do something
   }
}

This actually gives me an idea to combine widget messaging with signals.

Say you have the following structure :

std::map<WidgetMsg , WidgetCallbackFunctionList> dispatcher;

then you could do the following to connect a signal :

dispatcher[WidgetMsg(&mybutton , TOPIC_DIALOG , BUTTON_CLICKED)] = [&gui](){gui.Close();};

And you could execute it with :

dispatcher.execute_callbacks(gui.TakeNextMessage());

And all the slots would be signalled automagically.

That's the one drawback of my system, the potential for long message processing code.

frank.n said:

That's a very complex interface for styles.

Yes. I'm not entirely satisfied with it, especially as every new widget added (to the lgui "core", that is) will introduce additional methods. Maybe I should have an abstract style class for each widget, with the style bundling them together and some generic method to get the appropriate style object instances. Or find some other useful abstraction over things to draw. But the original idea was to keep things simple, so I'll go with that existing solution for now. :P

I applaud your attempt to keep code organized and clean, but the way it is, its pretty messy. My attempts to do this in my own gui have met with moderate success. I have abstracted away background and style into a WidgetPainter class. Every widget has a reference to a WidgetPainter and it takes care of drawing its basics like the padding, border, and margin, and any background it might have.

It's a good idea to separate out 'generic' drawing functions, so that your widgets can use them in combination to draw themselves. I appreciate what you want to do style wise, but I think it would be good to reconsider how to go about doing that.

I just made a major commit to Eagle core branch. I finished reworking my gui classes to use the new WidgetBase class. It should simplify things greatly. Now I need to do some serious testing. Still got a shit ton of TODO items. I wonder if I'm ever going to release Eagle. I've been working on this for so long now, almost 10 years. I think after I get done stress testing stuff I'm going to make a feature freeze and start work on the build system and documentation. Gotta make a release. >:(

frank.n
Member #16,879
July 2018

That seems problematic if you want a clean interface.

It's interesting how much of this seems to depend on taste, really. Here, I prefer placing things down the hierarchy if they are specific. However, with the styles, our conclusions seem to be just the other way round.

In my mind, the signals are sort of the output layer and represent the state of the widget as a virtual thing the user can interact with. The GUI has already (centrally) delivered the "raw" events (mouse button pressed, key down etc.) to them and it is the job of the widgets to aggregate and interpret them. Each widget does that in its own way. That's why I think of this as being specific and decentralized. An advantage of that is that the signals can emit the appropriate values directly. A disadvantage is that you cannot / should not do certain things in a signal handler deep down the GUI call stack.

Quote:

I wonder if I'm ever going to release Eagle.

Yeah, all the refactoring, optimizing, wanting to perfect things really gets in the way of that. ;D

That's why I postponed refactoring of the styles, I'd have to see how it works with a skinned style first. Plus, some mobile support would be more important for me. I still haven't thought of how to handle things like scrolling / swiping on mobile. Drag and drop seems to be one of the (few) things to translate rather well.

Edgar Reynaldo
Major Reynaldo
May 2007
avatar

frank.n said:

I still haven't thought of how to handle things like scrolling / swiping on mobile. Drag and drop seems to be one of the (few) things to translate rather well.

Swipes should be fairly easy. Implement a camera that you can scroll the view of, and then listen for touch events and move the camera. Nothing needs to change position except for the view. You then have to account for the view offset when sending touch or mouse events to the widgets though. In my GUI I allow for middle mouse button drag scrolling of the view. Just have your top level widget monitor for swipes, set the view, and offset the event positions by the view.

frank.n said:

https://github.com/frank256/lgui/blob/master/src/lgui/signal.h

...

In my mind, the signals are sort of the output layer and represent the state of the widget as a virtual thing the user can interact with. The GUI has already (centrally) delivered the "raw" events (mouse button pressed, key down etc.) to them and it is the job of the widgets to aggregate and interpret them. Each widget does that in its own way. That's why I think of this as being specific and decentralized. An advantage of that is that the signals can emit the appropriate values directly. A disadvantage is that you cannot / should not do certain things in a signal handler deep down the GUI call stack.

I've been reading your signal.h header and I seem to have a basic grasp of what is going on. Not sure what is going on with delayed emitting. Tell me, you used variadic templates to pass a variable set of arguments, correct? And I see you did some kind of overloading for const and non const member functions, but I don't follow it very well. Is that C++ 14? My library is still C++11, are things like that possible in C++11?

frank.n said:

Yeah, all the refactoring, optimizing, wanting to perfect things really gets in the way of that. ;D

I've had a bad case of feature creep over the last few years. It's not a bad thing in and of itself, but it means you're constantly in development phase, and not release mode.

So far my GUI has gone through 3 major phases. First version worked with Allegro 4 only. This was during the period when Allegro 4 was being phased out in favor of the new Allegro 5 branch. I decided I needed to remove any direct dependencies on third party libraries through abstracting away all the interfaces and hiding Allegro inside an Eagle system driver. My latest phase is a total redo of the WidgetBase class and everything that goes with it. I've made things much simpler, much more extensible, and much more flexible.

I've been reading quite a few of this forums old gui threads, and man. Everybody gets all excited - "Hey let's make a GUI!" and then there are like 5 pages of arguments about C vs C++ and how best to design the library. axilmar and I have gone at it a few times about gui design. He's got extensive experience with guis and a GUI library of his own. I believe it's been long abandoned though. Let me check. Yep, ALGUI is gone. axilmar took it down off github.

Long story short, the Ultimate GUI for Allegro has been an elusive target.

axilmar said:

Quote:

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!!!

frank.n
Member #16,879
July 2018

Not sure what is going on with delayed emitting. Tell me, you used variadic templates to pass a variable set of arguments, correct? And I see you did some kind of overloading for const and non const member functions, but I don't follow it very well. Is that C++ 14? My library is still C++11, are things like that possible in C++11?

Yes. The const and non-const member functions are just convenience overloads to be able to directly pass a pointer-to-member-function and an object instance as described above. I had to do it for const and non-const member functions.
Delayed emitting is there for the case when a slot connects the signal to another slot while being called by the same signal. Without precautions, the newly connected slot would be emitted right away.

I used a std::list here in order to not invalidate iterators of a std::vector when something is connected while emitting. It could probably be improved by picking another container, maybe a std::vector is possible anyway.

Edgar Reynaldo
Major Reynaldo
May 2007
avatar

There should be a predefined time when your slots get signaled. I don't see the need for making sure you don't add a slot when it's being signaled. For example in my gui, the only time a widget can raise an event is during HandleEvent or Update. Makes everything so much simpler and easier than worrying about contention.

Niunio
Member #1,975
March 2002
avatar

Let's see, what's the tally now?

TGUI - Trent Gamblin
ALGUI - axilmar
AGUI - jmasterx
LGUI - frank.n
EAGLE - Edgar Reynaldo
WidgetZ - SiegeLord

Who else have I forgotten?

The obligatory "why haven't we all been working on the same GUI for the last 10 years?" question is open.

Yep: MinGRo not actually a GUI and no officially published yet, but there it is.

And I've working on it because nobody else seems to use Object Pascal there. ::)

-----------------
Current projects: Allegro.pas | MinGRo

Edgar Reynaldo
Major Reynaldo
May 2007
avatar

frank.n
Member #16,879
July 2018

There should be a predefined time when your slots get signaled. I don't see the need for making sure you don't add a slot when it's being signaled.

It's there to handle a situation where connections of a signal are added / changed in a slot connected to the same signal. If the callee / slot / handler decides to alter the signal's connections while it is being emitted, bad things could happen.

Quote:

For example in my gui, the only time a widget can raise an event is during HandleEvent or Update.

It's the same with lgui actually. Usually, signals are emitted because of an external event having been processed by the GUI and delivered to the widget.

Edgar Reynaldo
Major Reynaldo
May 2007
avatar

I've finished my main upgrade and refactor on the core branch. Almost ready to merge back in with master.

But now I've got two dilemmas.

1) Most layouts do not automatically size a widget. Their size must be set according to the rules of the layout.

2) All widgets must be drawn within their parent's inner areas. This causes problems when I want to make a widget with separate parts, like a drop down list. I can add the list box and the drop down box separately no problem, but what area should the parent have? It doesn't make sense. So I need a way to add just the children when the parent is tracked by the widget handler (gui).

How do you deal with these problems in your gui frank.n?

frank.n
Member #16,879
July 2018

Most layouts do not automatically size a widget. Their size must be set according to the rules of the layout.

I don't know exactly what you mean but I guess that's probably why I finally switched to the 2-pass layout. In order to be able to set the size of a widget, the layouts need to know how large the children want to become (in some cases). In order to know that, the children need to know how large the parent would be (in some cases). This can become kind of a chicken-and-egg situation.

I've documented the layout system in a bit more detail here:
https://github.com/frank256/lgui/blob/master/src/doc/lgui.dox

You should be able to see this rendered when you do a "make doc" in the CMake dir if you have Doxygen installed, I hope. :)

Quote:

All widgets must be drawn within their parent's inner areas. This causes problems when I want to make a widget with separate parts, like a drop down list. I can add the list box and the drop down box separately no problem, but what area should the parent have? It doesn't make sense. So I need a way to add just the children when the parent is tracked by the widget handler (gui).

I've solved the drop-down case by introducing a "modal widget". This lives above anything else and receives all events as long as it is active. It may constitute the top of a separate hierarchy.

Edgar Reynaldo
Major Reynaldo
May 2007
avatar

make doc didn't work for me last time but mysteriously it did this time.

frank.n said:

Most layouts do not automatically size a widget. Their size must be set according to the rules of the layout.

I don't know exactly what you mean but I guess that's probably why I finally switched to the 2-pass layout. In order to be able to set the size of a widget, the layouts need to know how large the children want to become (in some cases). In order to know that, the children need to know how large the parent would be (in some cases). This can become kind of a chicken-and-egg situation.

A better explanation would be, each layout has a set of areas defined by the layout's rules. For example, a Pin layout stores a position and an alignment, and never resizes a widget. Another example is the Relative layout, which stores areas as fractional areas of the parent. The Grid layout stores grid positions, etc.... But in each case they store their own kind of layout position, which needs to be set before a widget attached to that position can receive an area.

In my gui, all children explicitly receive their position and dimensions from their parent, which is either another widget, or a layout.

Some layouts allow reposition, some allow resize, some allow both. But none of them know how to set their widget's area until 1) Their area is set, and 2) Their layout position is set.

axilmar and I got into a big semantics argument over the difference between a minimum size and a preferred size, but that's another story.

I've come up with an idea, and that is for my layouts to store elements of a layout position base class. Then I can derive the sub class from the base layout element, and add dynamic layout positions to a standard layout class. Move the inheritance from the Layout class to the LayoutElement class. This would also allow me to mix and match layout elements. I think it could be very powerful.

But of course, I've got lots of other things to do. I need to work on my scripting interface, I need more widgets, want to finish my resource manager, etc...

frank.n said:

I've solved the drop-down case by introducing a "modal widget". This lives above anything else and receives all events as long as it is active. It may constitute the top of a separate hierarchy.

In my gui, a 'modal' widget is as simple as a widget that always consumes the input as long as it has the focus. I can return DIALOG_INPUT_USED from my HandleEvent function, and that has the same effect as making it a modal widget. Don't be greedy with the input if you don't have focus though!!

EDIT
Question; How long did it take you to make custom docs with doxygen? I plan on using doxygen when I'm ready to make docs, but I don't have any experience with customizing the output. Also, it threw tons of errors during the make process, but managed to complete anyway, so I guess they were more warnings...

frank.n
Member #16,879
July 2018

Hmm, our approaches to layout seem to be quite different.

How long did it take you to make custom docs with doxygen?

There's nothing especially custom about it, I've only altered some of the values in the config file, I think. Took a short look at how to customize it further but decided it was not worth the effort.

Quote:

Also, it threw tons of errors during the make process, but managed to complete anyway, so I guess they were more warnings...

Maybe it's due to a missing dependency for creating the inheritance diagrams? I remember getting lots of warnings instead of those once on Windows, but it's been a while since I tried.

Edgar Reynaldo
Major Reynaldo
May 2007
avatar

 1   2 


Go to: