So for a while i have been searching for customizeable GUI libraries/frameworks but haven't found any that were consistent or that i liked. I want to be able to build every GUI element that i could imagine, design every part of it, add custom animations and events. I found this Javascript GUI/View library called "ReactJS" and i liked, how it handled data flow, it's component paradigm and how it forced you to write GUI code in consistent way and i got idea to build my own GUI framework. I want to build framework, where you are not limited by predefined GUI components, but you could write every GUI component yourself, add components together and be able to reuse components in other projects or other part of your game. All the existing Löve2d GUI libraries provide you with some predefined elements that give you some events and way to design them but it's usually very minimal and i would like to design mine from ground up.
NOTE:
The main purpose of this framework/library would not be to provide you with predefined GUI elements, but to provide you with basic building blocks and to force you to write code in specific organized way and make GUI components more reusable.
In short, how the framework would work:
The framework would revolve around "component" classes that are essentially just tables that have access to all love API(window, keyboard-events, mouse-events and other love API's including sound) but they would also have few of their own functions like render/draw that accepts some data how the GUI would be drawn(color-data, size, position and also your own custom data), it's main purpose would just be draw on the screen. It's kinda similar how most love2d state/scene manager libraries work, they hook to love2d events and you write your own logic into these functions.
1) How to nicely handle input?
Now the tricky part would be how to nicely handle user-input, checking if user clicked inside certain element, imagine writing user-input code for every GUI element separately, it's nightmare. I thought about providing some kind of building block's(rectangles, Text) that already handle all the user-input stuff, you could then build your Components out of these simple building blocks and not worry about doing these nasty inBound checks to see, whether mouse is inside certain region or not, it would also handle keyboard/controller and touch events you would just have to listen for the events.
Here's https://roblox.github.io/roact/guide/components/ Lua version of "ReactJS" called "Roact", i know it's for different game engine but the main idea remains the same.
Need advice on building React like framework for love2d
-
- Prole
- Posts: 10
- Joined: Sun Jun 25, 2017 5:29 pm
-
- Party member
- Posts: 234
- Joined: Mon Aug 29, 2016 8:51 am
Re: Need advice on building React like framework for love2d
I can definitely see this as something I'd contribute to!
Re: Need advice on building React like framework for love2d
I would suggest starting by picking an object orientation framework for lua, and making yourself familiar with it. There are a bunch of them, or you can write your own. Your post sounds a bit like you're in the middle between procedural and object oriented, with you calling things "tables" that should be classes. I think you can address pretty much all your requirements by designing this in an object oriented fashion from the start. Especially reusability is something that comes very easily with object orientation.
Anyway, I also have a suggestion specifically about your questions regarding user input. What I would recommend is that you structure this so that your individual UI elements don't know where they are on screen. Start with a base control class with a draw() method that only knows how to draw a rectangle at (0, 0), with a width and height given in attributes. Implement a text control that subclasses the base one, and also renders text in that rectangle.
Make a button that subclasses the text one, and also has a few methods like mousepressed() and mousereleased() that know how to handle mouse events, again assuming the top left corner of the control is at (0, 0) and all the mouse coordinates are relative to that. So testing whether the mouse is inside the button is triviai: If mouse x or y is negative, or if x is >= the button's width, or y is >= the button's height, the mouse is outside the button, and you can ignore the mousepressed event. In mousereleased() you either consider the button "clicked" if the mouse is inside, or not clicked if it's outside, but reset the selected state either way.
To test this, you just call the draw() method of your button in love.draw(), the mousepressed() method in love.mousepressed(), and so on. It'll be at the top left corner of the screen, but it should work.
To make the button do something, you simply add a call to another empty method to mousereleased(). Let's say,if the mouse pointer is inside the button in mousereleased(), and the button is in its selected state, you call self:onClick() which is simply an empty function. But users creating one of your buttons for their UI can override it and put their own code that handles the click in it. With the way OOP tends to work in lua, you end up with this kind of pattern when using UI elements:
Once you have a few classes like this to test with, you then make a subclass of your base control that has a list of child elements in a table. Let's say we start with something easy, and just arrange these child elements vertically. This class has a draw() method that just goes through all its children. For each child, it calls the child's draw() method. Then it uses love.graphics.translate() to translate the graphics context by the height of the child it just drew. This will make the next child appear below it (or above if you get the sign wrong ).
For mouse events you do something similar. You go through the list of children but maintain variables with the coordinates of the child's top left corner, starting at (0, 0). For each child, you call the mouse event method, but subtract that position from the mouse position. Then you add the height of the child to your top left corner position, and proceed with the loop.
At this point you can probably see where I'm going with this. You can make other classes that do layout differently. Arranging children in a grid can be immensely useful, for example. Automatic layout based on constraints is great, although that is really complicated and I would recommend saving it for later.
Even things like scroll views or windows are almost free with this approach. None of your other elements need to know about them. You just need to implement a container control that has exactly one child, and when drawing it, translates the graphics context by however many pixels the view is scrolled, or respectively, by the window's position. Something similar applies to mouse events etc. You'll probably also want to set a scissor and so on, but the point is none of your other components need to be aware of any of this, they just need to be able to work under the assumption that they're in the top left corner.
Another thing you get for free is rendering to canvases. I like my windows to be canvas backed, because UI elements rarely need to be re-drawn 60 times a second. So my windows tend to set a canvas before drawing children, and the children don't care because all they want to do is draw themselves at (0, 0), no matter what that means. And then of course the window's draw method doesn't always draw the children, but draws the canvas instead if it thinks it doesn't need to be refreshed.
Keyboard input requires a bit more work. You'll need to track which of your UI elements has focus. The simple and direct approach is just having a global variable that stores a reference to the element that has focus. When a control detects a click, it sets that variable to itself. And then you send your key events and textinput events from your corresponding love event handlers to whatever that variable points to. For more complex systems it makes sense to structure this a bit differently, e.g. I like having my individual windows keep track of focus. They build a list of child controls that can have focus, and then based on that I can implement things like cycling through focus-able elements with tab. But for starting out, you don't really need that.
Anyway, I also have a suggestion specifically about your questions regarding user input. What I would recommend is that you structure this so that your individual UI elements don't know where they are on screen. Start with a base control class with a draw() method that only knows how to draw a rectangle at (0, 0), with a width and height given in attributes. Implement a text control that subclasses the base one, and also renders text in that rectangle.
Make a button that subclasses the text one, and also has a few methods like mousepressed() and mousereleased() that know how to handle mouse events, again assuming the top left corner of the control is at (0, 0) and all the mouse coordinates are relative to that. So testing whether the mouse is inside the button is triviai: If mouse x or y is negative, or if x is >= the button's width, or y is >= the button's height, the mouse is outside the button, and you can ignore the mousepressed event. In mousereleased() you either consider the button "clicked" if the mouse is inside, or not clicked if it's outside, but reset the selected state either way.
To test this, you just call the draw() method of your button in love.draw(), the mousepressed() method in love.mousepressed(), and so on. It'll be at the top left corner of the screen, but it should work.
To make the button do something, you simply add a call to another empty method to mousereleased(). Let's say,if the mouse pointer is inside the button in mousereleased(), and the button is in its selected state, you call self:onClick() which is simply an empty function. But users creating one of your buttons for their UI can override it and put their own code that handles the click in it. With the way OOP tends to work in lua, you end up with this kind of pattern when using UI elements:
Code: Select all
button = Button("Click me!")
function button:onClick()
print("Button clicked!")
end
For mouse events you do something similar. You go through the list of children but maintain variables with the coordinates of the child's top left corner, starting at (0, 0). For each child, you call the mouse event method, but subtract that position from the mouse position. Then you add the height of the child to your top left corner position, and proceed with the loop.
At this point you can probably see where I'm going with this. You can make other classes that do layout differently. Arranging children in a grid can be immensely useful, for example. Automatic layout based on constraints is great, although that is really complicated and I would recommend saving it for later.
Even things like scroll views or windows are almost free with this approach. None of your other elements need to know about them. You just need to implement a container control that has exactly one child, and when drawing it, translates the graphics context by however many pixels the view is scrolled, or respectively, by the window's position. Something similar applies to mouse events etc. You'll probably also want to set a scissor and so on, but the point is none of your other components need to be aware of any of this, they just need to be able to work under the assumption that they're in the top left corner.
Another thing you get for free is rendering to canvases. I like my windows to be canvas backed, because UI elements rarely need to be re-drawn 60 times a second. So my windows tend to set a canvas before drawing children, and the children don't care because all they want to do is draw themselves at (0, 0), no matter what that means. And then of course the window's draw method doesn't always draw the children, but draws the canvas instead if it thinks it doesn't need to be refreshed.
Keyboard input requires a bit more work. You'll need to track which of your UI elements has focus. The simple and direct approach is just having a global variable that stores a reference to the element that has focus. When a control detects a click, it sets that variable to itself. And then you send your key events and textinput events from your corresponding love event handlers to whatever that variable points to. For more complex systems it makes sense to structure this a bit differently, e.g. I like having my individual windows keep track of focus. They build a list of child controls that can have focus, and then based on that I can implement things like cycling through focus-able elements with tab. But for starting out, you don't really need that.
-
- Prole
- Posts: 10
- Joined: Sun Jun 25, 2017 5:29 pm
Re: Need advice on building React like framework for love2d
Thanks for the reply but i have already made OOP GUI framework https://github.com/mastermarkus/simplify and that's why i made this post in the first place, i would rather create framework that prefers composition over inheritance something similar to "ReactJS" framework. In lua there's version of it called "Roact" for "Roblox" game engine, take a look at this https://roblox.github.io/roact/
- holywyvern
- Prole
- Posts: 8
- Joined: Wed Apr 05, 2017 7:06 pm
- Contact:
Re: Need advice on building React like framework for love2d
I totally agree the component schema of react is really good.
If I can lend a hand on it, I will for sure.
If I can lend a hand on it, I will for sure.
Re: Need advice on building React like framework for love2d
I'm sorry, it seems like I got stuck on the technical side of things. Both React and Roact "cheat" when it comes to actually implementing the UI, in that they use existing UI features that love2d doesn't have and components configure them but don't have to deal with events, actual rendering, etc. They also create hierarchies of object instances, and use composition only for the configuration part. Your post didn't sound like you wanted to emulate that, because then you wouldn't have to worry about e.g. user input - if you used an existing UI toolkit that would already be solved just like it is when React uses HTML elements, or when Roact uses Roblox UI classes.
On the other hand, composition vs inheritance is not black and white. OO knows concepts like multiple inheritance, mixins, etc. that are similar to composition. The resulting structure is the same, you just choose how to express relationships between components slightly differently.
I think what I wrote applies either way. I believe proper OO is a better tool if you have to implement the actual UI functionality yourself, but in principle, there is no reason why composition wouldn't also work. You're literally just removing a tool from your arsenal that's nice to have for complex structures, but definitely not required.
On the other hand, composition vs inheritance is not black and white. OO knows concepts like multiple inheritance, mixins, etc. that are similar to composition. The resulting structure is the same, you just choose how to express relationships between components slightly differently.
I think what I wrote applies either way. I believe proper OO is a better tool if you have to implement the actual UI functionality yourself, but in principle, there is no reason why composition wouldn't also work. You're literally just removing a tool from your arsenal that's nice to have for complex structures, but definitely not required.
Who is online
Users browsing this forum: No registered users and 5 guests