|
|
| Questions about GUI design |
|
Thomas Fjellstrom
Member #476
June 2000
|
bamccaig said: This is all I was referring to. If the abstraction layer needs to change in order to accommodate the other backends then it's not really an abstraction layer. You have to start somewhere. Most people aren't going to sit down and look at all of the possible backends and design the ONE TRUE API that fits all of them. Most start with one or two, and will refactor as needed. -- |
|
LennyLen
Member #5,313
December 2004
|
bamccaig said: It's a redundant layer (i.e., it doesn't serve a purpose yet, and will need to change before it could). By that definition, all unfinished code is redundant. Edgar's abstraction layer is simply incomplete.
|
|
axilmar
Member #1,204
April 2001
|
bamccaig said: so that pretty much makes the current abstraction layer not really an abstraction at all, but a redundant layer. This is all I was referring to. If the abstraction layer needs to change in order to accommodate the other backends then it's not really an abstraction layer. It's a redundant layer (i.e., it doesn't serve a purpose yet, and will need to change before it could). Not trying to insult your library which I know nothing about (I admire your aspirations and efforts). Exactly. Thomas Fjellstrom said: You have to start somewhere. Most people aren't going to sit down and look at all of the possible backends and design the ONE TRUE API that fits all of them. Most start with one or two, and will refactor as needed. By that definition, all unfinished code is redundant. Edgar's abstraction layer is simply incomplete. Refactoring is one thing, throwing away the code and starting over is another. Why don't you people go review Edgar's code? you will understand what I and bamccaig is saying. Edgar has also incorporated Allegro event sources to his library. We are talking about design 1:1 to Allegro. Come on people, let's quickly finish this round of discussion. Edgar's code may contain some good pieces, we may reuse them. Why don't we open a design document for our GUI? I've started one: https://docs.google.com/document/d/1_epQPxQpJjUosAgUfKW3kGW9x5HBjuAlvmbmD7L3CYQ/edit?usp=sharing |
|
jmasterx
Member #11,410
October 2009
|
You may also want to abstract away cursors. For example, Agui sets the cursor when mouse enters textbox. Possibly also transformations. Timers can be useful too but may be harder to abstract. Transformations are useful if say, the native buffer size is 640x480 and the user goes fullscreen with that bitmap. You want the mouse to still correctly detect widgets. Supporting UTF-8 is nice too. For features, it could have all 3 messaging styles like QT: How you handle skinning is important too. It would be nice if: When making Agui I realized the importance of having both. This is where the idea of margins came into play. So for skinning, all rendering could be done possibly using the Visitor design pattern http://en.wikipedia.org/wiki/Visitor_pattern or by creating a class with virtual methods: buttonPaintBackground(Graphics& g) ... etc. This way the user can skin without subclassing all the widgets (One of the pitfalls of Agui). Another interesting problem you need to think about is: How do events bubble up. This is sort of another pitfall in Agui. The event in Agui goes straight to the designated widget and all attached listeners. But what happens if you have a TextBox with a scrollbar inside. The textbox needs to scroll itself when the scrollbar is scrolled. The only somewhat ugly way to do this in Agui is for the textbox to internally attach a listener to its scrollbar.(The even better way is to put it in a scroll panel). There are a few better ways to handle this. Some GUIs send the event to the parent first, then if the parent is not interested, it is sent to the widget itself. Other guis will use the chain pattern: You will need to decide what is best and how listeners should consume events, etc. Where this went most horribly wrong in Agui is my ScrollPanel widget. It attaches all kinds of listeners to its children to detect when to show / hide the scrollbars. Agui GUI API -> https://github.com/jmasterx/Agui |
|
axilmar
Member #1,204
April 2001
|
Added mouse cursors to the list of things to abstract. For the rest you mention, I updated the document. |
|
Edgar Reynaldo
Major Reynaldo
May 2007
|
axilmar said: Why don't you people go review Edgar's code? you will understand what I and bamccaig is saying. Edgar has also incorporated Allegro event sources to his library. We are talking about design 1:1 to Allegro. What are you talking about? Event sources copied 1:1? Here is my EagleEventSource header : 1
2class EagleEventSource {
3
4private :
5 std::vector<EagleEventListener*> listeners;
6
7 bool OnList(EagleEventListener* l);
8
9public :
10 EagleEventSource() : listeners() {}
11 virtual ~EagleEventSource();
12
13 void EmitEvent(EagleEvent e);
14
15 void SubscribeListener(EagleEventListener* l);
16 void UnsubscribeListener(EagleEventListener* l);
17 bool HasListeners() {return !listeners.empty();}
18
19 virtual void ReadEvents()=0;/// TODO : Has this been implemented in any subclasses? What was it for again? Polling?
20};
That is certainly not a 1:1 copy of anything. And if you're talking about using ALLEGRO_EVENT_QUEUE's inside the Allegro5EventHandler, why not? It's an a5 driver, it should be using a5 functionality. I used the same names and values for events to make it easier to port from a5 to my library. All they have to do is rename their events to EAGLE_ instead of ALLEGRO_. And the id values are the same so I can just perform a simple assignment of the variable value instead of having to map to different values. :/ That is pretty much the only thing that is the same about my library and allegro 5. If I didn't use ALLEGRO_EVENT_QUEUE's internally I would have had to implement a separate thread for every input source instead of one thread per event handler. And I would have had to implement some version of locking and sleeping using pthreads on top of it then. I attached both Events.hpp and Allegro5EventHandler.hpp. Look for yourself and see. My Website! | EAGLE GUI Library Demos | My Deviant Art Gallery | Spiraloid Preview | A4 FontMaker | Skyline! (Missile Defense) Eagle and Allegro 5 binaries | Older Allegro 4 and 5 binaries | Allegro 5 compile guide |
|
axilmar
Member #1,204
April 2001
|
Edgar Reynaldo said: That is certainly not a 1:1 copy of anything. The idea of having an event source is a copy of Allegro's relevant idea. I didn't say you copied the implementation. Let's stop with this nonsense and collaborate on a GUI. Did you read the design document I started? you can edit it if you wish. We can bring anything we need from EagleGUI into the new GUI, if it's good and available. |
|
jmasterx
Member #11,410
October 2009
|
The document looks good. However, I have an alternate suggestion for skinning. You seem to imply that the Widget class has a virtual setSkin. This means the user needs to subclass the Skin class for each widget they want to custom render since it is per-Widget. Instead, it might be better if you have setSkin in your Gui class of sorts. This way calling gui.render() will actually do: for each widget widget.paintBackground(skin,g); widget.paintComponent(skin,g); ... Widget::paintBackground(Skin s, Graphics g) { s.paintBackground(this,g); } This sort of thing is possible with the visitor pattern and double dispatch. A user should be able to subclass Skin and custom paint any and all widgets without having to subclass the skin class for each widget. Your way may be able to do this and I may have misunderstood. I apologize if I did. But one of the caveats of Agui is having to subclass all Widgets just to paint them in a custom way. It would be cool to have something more like Java Swing. Agui GUI API -> https://github.com/jmasterx/Agui |
|
axilmar
Member #1,204
April 2001
|
jmasterx said: This means the user needs to subclass the Skin class No, the Skin class would not need to be subclassed. It's a resource manager. Each widget will know how to get resources from it and apply it to its painting algorithm. |
|
Pho75_
Member #12,377
November 2010
|
Design doc looks like a great start. Requested Additions: -drag and drop. -a default event loop to give control to GUI -input injection methods for all input devices. -gesture support |
|
Edgar Reynaldo
Major Reynaldo
May 2007
|
My idea for skinning was to have each widget own a widget area object that has a background component, and 9 panels ie center + margins. All you do is call WidgetBase::Draw in your SubClassWidget::Draw method. My Website! | EAGLE GUI Library Demos | My Deviant Art Gallery | Spiraloid Preview | A4 FontMaker | Skyline! (Missile Defense) Eagle and Allegro 5 binaries | Older Allegro 4 and 5 binaries | Allegro 5 compile guide |
|
axilmar
Member #1,204
April 2001
|
I have added drag and drop and gestures. Edgar, are you in? |
|
Edgar Reynaldo
Major Reynaldo
May 2007
|
You put your left foot in, you put your right foot out, you do the hokey pokey and you shake your self about. That's what it's all about. Just so you know, my library already meets about half to two thirds of those elements listed on your design doc... IDK... Can we co-develop? Ie. contribute to both code bases at once maybe at times? I don't really want to abandon EAGLE... My Website! | EAGLE GUI Library Demos | My Deviant Art Gallery | Spiraloid Preview | A4 FontMaker | Skyline! (Missile Defense) Eagle and Allegro 5 binaries | Older Allegro 4 and 5 binaries | Allegro 5 compile guide |
|
jmasterx
Member #11,410
October 2009
|
axilmar said: No, the Skin class would not need to be subclassed. It's a resource manager. Each widget will know how to get resources from it and apply it to its painting algorithm. With this technique, how do you deal with ninepatch backgrounds? Ninepatch is very important for drawing backgrounds. How will you differentiate from a normal resource and a ninepatch resource? Other silly little things that come to mind: What if a user only wants a 2 state button, and does not want to provide a focus texture, a highlight texture, and a disabled texture. Will this do something like: If has resource loaded for state, use it, else, use default resource. I also wanted to note a rare case: In my card game, I have a Widget that renders a bitmap that has some opaque and some transparent areas on top of what is normally rendered and then Widgets are rendered on top of that: Ofcourse the user could just override paint methods to achieve this, but, it is a case where subclassing a widget is needed to paint. Your method is simpler and easier for 95% of cases, but introduces subclassing in rare cases. That's not a problem, but I wanted to bring that sort of weird corner case to your attention. That brings another feature to mind: It would be good to have a visibility clipping mechanism. For example, being able to put a ListBox in a ScrollPanel and have the ListBox only render items visible to the user. In Agui, since scrollable widgets directly take in scroll bars in their constructor this is easy to do, but it would be better to have it generalized. Agui GUI API -> https://github.com/jmasterx/Agui |
|
bamccaig
Member #7,536
July 2006
|
In terms of buttons, you can have multiple implementations. Default buttons that are entirely based on simple attributes like colors and dimensions and the like. That's all you really need for traditional GUI-style buttons that aren't themselves images. You can have alternative implementations for more customizable rendering. Basically, a good library handles as many reasonable cases for you so that you don't have to do a ton of redundant work for nothing. -- acc.js | al4anim - Allegro 4 Animation library | Allegro 5 VS/NuGet Guide | Allegro.cc Mockup | Allegro.cc <code> Tag | Allegro 4 Timer Example (w/ Semaphores) | Allegro 5 "Winpkg" (MSVC readme) | Bambot | Blog | C++ STL Container Flowchart | Castopulence Software | Check Return Values | Derail? | Is This A Discussion? Flow Chart | Filesystem Hierarchy Standard | Clean Code Talks - Global State and Singletons | How To Use Header Files | GNU/Linux (Debian, Fedora, Gentoo) | rot (rot13, rot47, rotN) | Streaming |
|
jmasterx
Member #11,410
October 2009
|
bamccaig said: In terms of buttons, you can have multiple implementations. Default buttons that are entirely based on simple attributes like colors and dimensions and the like. That's all you really need for traditional GUI-style buttons that aren't themselves images. You can have alternative implementations for more customizable rendering. Basically, a good library handles as many reasonable cases for you so that you don't have to do a ton of redundant work for nothing.
Yeah, I agree. I was overkill-trying to separate logic and presentation Oh just thought of another feature I like: resizeToContents in the base widget Agui GUI API -> https://github.com/jmasterx/Agui |
|
axilmar
Member #1,204
April 2001
|
Edgar Reynaldo said: Can we co-develop? Ie. contribute to both code bases at once maybe at times? I don't really want to abandon EAGLE... You obviously are not looking for a joined project. I don't want to work on Eagle, it's not up to my coding and design standards, so obviously we are off and I am not going to ask you again about collaboration. jmasterx said: With this technique, how do you deal with ninepatch backgrounds? Ninepatch is very important for drawing backgrounds. How will you differentiate from a normal resource and a ninepatch resource? The widgets that need ninepatches can query the Skin instance about the bitmap or bitmaps they want and any metrics that come along with them. The Skin class itself can have a configuration data type which represents a ninepatch. Quote: If has resource loaded for state, use it, else, use default resource. Yeah, absolutely. When you request a resource from Skin, you also pass a default value. If the resource doesn't exist, you get the default value back. Quote: I wanted to bring that sort of weird corner case to your attention. I do not understand the case. Could you elaborate? what is the actual problem? Quote: It would be good to have a visibility clipping mechanism. For example, being able to put a ListBox in a ScrollPanel and have the ListBox only render items visible to the user. Yes, absolutely, and this is a top priority. In many games, for example in the one I work now, there is a huge problem of resources for big lists and you have to make miracles in order to cut resources down, due to the framework not supporting clipping properly. jmasterx said: Oh just thought of another feature I like: resizeToContents in the base widget That would be taken care of in the packing phase of the layout. |
|
jmasterx
Member #11,410
October 2009
|
The ace of Spades container is weird because you essentially have: Actually, now that I remember how I did it: I have a class Frame in Agui which allows me to put Widgets in the frame's content panel, and in the frame itself. So the X, cancel and Okay are Widgets of the frame while the inner ones, scrollbar and options are widgets of the content panel. In this strange scenario, I had to subclass my Frame so that I can do this painthing: Which, now that I think about it, is a scenario where subclassing is pretty much unavoidable. I was wondering, since each Widget has a setSkin method, who calls setSkin? Is it the Gui class that has the current skin and gives it to the widget when it is added? What happens in the case where the skin of the entire gui were to be changed at runtime. Is this possible in a singe line of code? I'm thinking of Java's Swing where the look and feel of the entire gui can be dynamically changed very easily. Agui GUI API -> https://github.com/jmasterx/Agui |
|
axilmar
Member #1,204
April 2001
|
jmasterx said: Which, now that I think about it, is a scenario where subclassing is pretty much unavoidable. You don't need subclassing. You can do it with multiple widget containers stacked on top of each other. Quote: who calls setSkin? The programmer. Quote: Is this possible in a singe line of code? Yes: myGUI->setSkin(Skin("myskin.txt"));
|
|
jmasterx
Member #11,410
October 2009
|
That's cool! Agui GUI API -> https://github.com/jmasterx/Agui |
|
Edgar Reynaldo
Major Reynaldo
May 2007
|
axilmar said: You obviously are not looking for a joined project. I don't want to work on Eagle, it's not up to my coding and design standards, so obviously we are off and I am not going to ask you again about collaboration. It's a two way street axilmar. If you expect me to donate any or all of my library or time to the community gui library, it might be nice to have something in return ie... freedom to use code from the new library in Eagle as well. As far as not being up to standards, I did say that a lot of the issues on your list were on my todo list, things that I had planned to do anyway. The fact that you're not even willing to discuss my methods suggests you are not a team player. Why would I want to work on a team with you? What do I get out of it? And what kind of license do you want this library to have? My Website! | EAGLE GUI Library Demos | My Deviant Art Gallery | Spiraloid Preview | A4 FontMaker | Skyline! (Missile Defense) Eagle and Allegro 5 binaries | Older Allegro 4 and 5 binaries | Allegro 5 compile guide |
|
axilmar
Member #1,204
April 2001
|
Edgar Reynaldo said: The fact that you're not even willing to discuss my methods suggests you are not a team player. I am willing to discuss them. Which one would you like to discuss? Quote: And what kind of license do you want this library to have? An open source one, of course. |
|
Edgar Reynaldo
Major Reynaldo
May 2007
|
axilmar said: I am willing to discuss them. Which one would you like to discuss? Well for one, the gui needs to communicate within itself. Which method do you prefer among these or another? 1) WidgetMessages passed from child to parent 2) Listeners 3) Signals and Slots 4) other. Now explain why and how you plan to handle this in the proposed gui. My Website! | EAGLE GUI Library Demos | My Deviant Art Gallery | Spiraloid Preview | A4 FontMaker | Skyline! (Missile Defense) Eagle and Allegro 5 binaries | Older Allegro 4 and 5 binaries | Allegro 5 compile guide |
|
axilmar
Member #1,204
April 2001
|
Edgar Reynaldo said: Well for one, the gui needs to communicate within itself. Which method do you prefer among these or another? 1) WidgetMessages passed from child to parent 2) Listeners 3) Signals and Slots 4) other. I prefer the event handler pattern:
I.e.: 1class EventHandler {
2public:
3 virtual void handleEvent(const EventPtr &event);
4};
5
6class EventDispatcher : public EventHandler {
7public:
8 virtual void handleEvent(const EventPtr &event) {
9 dispatchEvent(event);
10 }
11
12 void dispatchEvent(const EventPtr &event);
13 EventListenerId addEventListener(EventType eventType, const std::function<void(const EventPtr &)>);
14};
15 void removeEventListener(const EventListenerId &id);
16
17class DisplayObject : public EventDispatcher {
18};
19
20class DisplayObjectContainer : public DisplayObject {
21public:
22 virtual void handleEvent(const EventPtr &event) {
23 DisplayObject::handleEvent(event);
24 if (event->isAccepted()) return;
25 switch (event->getType()) {
26 case MouseEvent::CLICK:
27 //dispatch event to children
28 break;
29 }
30 }
31};
The above design has all the advantages of widget messages, listeners and signals and slots, and more:
EDIT: fixed inheritance of EventDispatcher from EventHandler. |
|
pkrcel
Member #14,001
February 2012
|
I guess class EventDispatcher in your code snipped should inherit from EventHander? It is unlikely that Google shares your distaste for capitalism. - Derezo |
|
|
|