I LÖVE callbacks

General discussion about LÖVE, Lua, game development, puns, and unicorns.
User avatar
slime
Solid Snayke
Posts: 3161
Joined: Mon Aug 23, 2010 6:45 am
Location: Nova Scotia, Canada
Contact:

Re: I LÖVE callbacks

Post by slime »

airstruck wrote:It would have been possible with addListener, of course.
addListener only handled events pushed through love.event – so love.draw, love.update, love.load, and love.errhand could not be 'listened to' with addListener.
User avatar
airstruck
Party member
Posts: 650
Joined: Thu Jun 04, 2015 7:11 pm
Location: Not being time thief.

Re: I LÖVE callbacks

Post by airstruck »

slime wrote:
airstruck wrote:It would have been possible with addListener, of course.
addListener only handled events pushed through love.event – so love.draw, love.update, love.load, and love.errhand could not be 'listened to' with addListener.
I remember ;)

I thought it was a step in the right direction, anyway. Library code requiring the user to do something in draw and update isn't nearly as gruesome as requiring them to do stuff in the 20-odd handlers that were covered by addListener (I'm assuming most library code doesn't want to do anything with errhand).
User avatar
Inny
Party member
Posts: 652
Joined: Fri Jan 30, 2009 3:41 am
Location: New York

Re: I LÖVE callbacks

Post by Inny »

airstruck wrote:I think it is. Doesn't mean it's a great standard. Consider this code:

Code: Select all

function love.draw (...)
    somelibrary.handledraw(...)
end
function love.resize (...)
    somelibrary.handleresize(...)
end
function love.mousepressed (...)
    somelibrary.handlemousepressed(...)
end
function love.mousereleased (...)
    somelibrary.handlemousereleased(...)
end
function love.mousemoved (...)
    somelibrary.handlemousemoved(...)
end
function love.keypressed (...)
    somelibrary.handlekeypressed(...)
end
function love.keyreleased (...)
    somelibrary.handlekeyreleased(...)
end
function love.textinput (...)
    somelibrary.handletextinput(...)
end
-- and so on
I would probably ask that they standardized on the names to match the event they are intended to be called from, and take the parameters that the event is provided. At the very least this would make it so we could write a standard wrapper, which I'm going to spitball here:

Code: Select all

StandardHandler = {}

function StandardHandler.AddLibrary(lib)
  local wrapper = {}
  for _, v in ipairs { 'update', 'draw', 'keypressed' } do -- you can fill in the rest
    wrapper[v] = lib[v] or function() end
  end
  StandardHandler[#StandardHandler+1] = wrapper
end

function StandardHandler.call(event, ...)
  for _, wrapper in ipairs(StandardHandler) do
    wrapper[event](...)
  end
end
That way each of the love event handlers, you'd only have to call StandardHandler.call('draw') or StandardHandler.call('update', dt). Not that I think this should actually be the standard, just that it would be a possibility and not a terrible one to get behind.
User avatar
kikito
Inner party member
Posts: 3153
Joined: Sat Oct 03, 2009 5:22 pm
Location: Madrid, Spain
Contact:

Re: I LÖVE callbacks

Post by kikito »

airstruck wrote:Consider this code:

Code: Select all

function love.draw (...)
    somelibrary.handledraw(...)
end
function love.resize (...)
    somelibrary.handleresize(...)
end
...
-- and so on

In my view that's annoying and borderline unacceptable. The point of library code is to encapsulate functionality. When you have to wire up a shit ton of events by hand like this, that's the opposite of encapsulating functionality.
Consider that if a library needs to be wired to many different subsystems, the problem might be that the library is trying to do too many things. A library must encapsulate, but it must also have focus. When possible, split big libraries into several smaller ones (or simply ignore libraries which require too much wiring, as they are probably doing too much stuff).
When I write def I mean function.
User avatar
airstruck
Party member
Posts: 650
Joined: Thu Jun 04, 2015 7:11 pm
Location: Not being time thief.

Re: I LÖVE callbacks

Post by airstruck »

Inny wrote:I would probably ask that they standardized on the names to match the event they are intended to be called from, and take the parameters that the event is provided. At the very least this would make it so we could write a standard wrapper
You can sort of do this already with pairs(love.handlers). That leaves out the callbacks that aren't event handlers, but I think that's only update, draw and errhand. That still leaves library authors to choose between having the user wire up a bunch of events or destructively hooking everything in library code.
kikito wrote:Consider that if a library needs to be wired to many different subsystems, the problem might be that the library is trying to do too many things.
That example draws from some code in a UI library I'm working on. It does need to use all of those events; it also uses wheelmoved which I forgot to add to that example, and will likely use one or two more window-related events in the future.
User avatar
kikito
Inner party member
Posts: 3153
Joined: Sat Oct 03, 2009 5:22 pm
Location: Madrid, Spain
Contact:

Re: I LÖVE callbacks

Post by kikito »

airstruck wrote:some code in a UI library I'm working on. It does need to use all of those events; it also uses wheelmoved which I forgot to add to that example, and will likely use one or two more window-related events in the future.
Please realize that what I am going to say is not in any way a personal attack and I don't mean to discourage a fellow library developer from working on their own library. But it seems to me that your quote proves my point more than counter it. My opinion is that your library seems to be doing more stuff than it should (as a single library).
When I write def I mean function.
User avatar
airstruck
Party member
Posts: 650
Joined: Thu Jun 04, 2015 7:11 pm
Location: Not being time thief.

Re: I LÖVE callbacks

Post by airstruck »

kikito wrote:My opinion is that your library seems to be doing more stuff than it should (as a single library).
I'd be interested to hear your thoughts on how that could be improved.

As a UI library, it needs to draw stuff to the screen, so it needs love.draw. It captures mouse input events, because it's focused on primarily mouse-driven UIs (I'll add better touch support in the future). It provides easy integration of keyboard shortcuts, for example you can give a widget a property like `shortcut = "ctrl-s"`, so it captures keyboard input events. It captures text events so the user can type in the text box widget. And it provides a responsive layout manager that reshapes the UI when the shape of the window changes, so it captures the window resize event. That pretty much only leaves out gamepad events, which I don't care about at the moment.

Are you suggesting that the stuff that captures user input (from mouse, keyboard, window manager) should be moved into a separate library? If it were, would that solve the problem with the user having to wire up events or the library having to hook them?
User avatar
kikito
Inner party member
Posts: 3153
Joined: Sat Oct 03, 2009 5:22 pm
Location: Madrid, Spain
Contact:

Re: I LÖVE callbacks

Post by kikito »

airstruck wrote:Are you suggesting that the stuff that captures user input (from mouse, keyboard, window manager) should be moved into a separate library?
Not necessarily. All I have said is that they should not be on the GUI but somewhere else. Where this is, exactly, depends on the particular game we're talking about.

What I am saying is that your GUI library should not be concerned about how the mouse, the keyboard, and the pads work, because that is not its job. It should not be doing that.

Let's talk about what GUI needs, as inputs:
  • 4 directional inputs related to the "currently highlighted control" (move the selection up, down, left, right). These are usually the arrow keys in the keyboard and the d-pad in a joystick. But a GUI library should not know that. It just needs someone to tell it "move the highlighted item up". Maybe the programmer wants to simulate that the player is drunk, and changes the inputs on purpose. The GUI can't know that.
  • If there is a pointing devise (like a mouse), then the library needs to interact with it. But there might be screen transformations at play (cameras, resolution changes, retina display corrections, etc). So the GUI should not "take over the mouse". It needs someone to tell it "for you, the mouse is here". Maybe in the current game state the GUI does not need to know about the mouse, even if it's there. Maybe the programmer is simulating a software mouse in a tutorial. It is not up to the gui to know that.
  • The library needs a way to tell when the currently highlighted control is activated. This could be the "enter key", or the "joystick A button", or the mouse button, or a touch in a touch screen. But again, the library does not need to know this. It just needs someone to tell it "activate the highlighted control now".
  • In some particular cases, when dealing with text fields, it also needs to know the keys the user presses. Even in that case, it is not ok that it simply "takes control of the global keyboard". There are scenarios for this: maybe the programmer using the library wants to simulate an NPC using the GUI to perform some action. Maybe there is some sort of "macro" in place by which pressing a button, several controls change, and some input fields are automatically filled up. The point again is that the gui should "present the input for key presses" and allow the programmer to use it, as opposed to "grabbing the global keyboard".
There are other secondary GUI inputs (next/previous highlighted control, go back to previous screen, etc) but I hope you get the idea: A GUI should present inputs, like a gun presents a trigger; It should not "shoot by itself".

Output-wise, there are two main thing a GUI does:
  • The first thing is changing game state. This can be done with Lua functions, and I think is not relevant for this particular conversation.
  • The second one is drawing itself. But this does not mean that all that love.draw needs to do is drawing the GUI. In games GUIs are quite often drawn along something else. Maybe the gui is a minimap with buttons on the side, like the bottom of a Starcraft game. Maybe the user wants to apply filters to the whole screen (i.e. a screenshake) before drawing the GUI. The GUI must not know about this. It just needs to draw itself when it is told to.
To a GUI lib, the screen, the mouse, the keyboard and the d-pad should be no different than the physics system, the sound system, or the network library - things it should not directly "touch" (unless the player hooks them up some way or the other - via callbacks, an event system provided by another library, or magical rainbows).

This said, it's desirable to include examples about how to hook things up in the docs and demos. You might even provide "default hooking mechanisms for the most obvious options"; but these should be opt-in and easily deactivable.
airstruck wrote:If it were, would that solve the problem with the user having to wire up events or the library having to hook them?
I am not sure I would characterize that as a "problem". To me it sounds like ... programming. It is on the same category as "The user needs to write a for loop every time he wants to iterate over a table". Maybe I am not understanding your concern correctly.
When I write def I mean function.
User avatar
airstruck
Party member
Posts: 650
Joined: Thu Jun 04, 2015 7:11 pm
Location: Not being time thief.

Re: I LÖVE callbacks

Post by airstruck »

I think we have two very different views on what a GUI library can/should be. In my opinion a GUI library is primarily concerned with receiving user input, and should be able to do that "on its own" without having to be explicitly told to do so. The programmer shouldn't have to say "hey GUI library, there was a mouse press at these coordinates" when that information is already available to the library and is accurate in 99% of cases. The programmer shouldn't have to rig up a zillion common keyboard behaviors like tab navigation, arrows to move things around, ctrl-x/c/v to cut/copy/paste, etc.

The point of a high-level library is to let the programmer to get things done without screwing with a bunch of lower-level behavior. This includes wiring up a bunch of events by copying some boilerplate from an example somewhere. Sure, there's a cost associated with that; if you have a weird coordinate system or are scaling everything someplace else, mouse input won't work. You sacrifice some of that low-level control, and in exchange you get the behavior you're going to want 99% of the time with very little hassle (an API that's expressive and easy to remember, a lot of canned functionality to speed up development for the usual cases, not having to write much boilerplate, etc.).
You might even provide "default hooking mechanisms for the most obvious options"; but these should be opt-in and easily deactivable.
Why should they be opt-in? For that matter, why should they be easy to deactivate? If someone else wants to design their own libraries like that, it's not unreasonable, but it's certainly not the only reasonable approach and I think our views differ here. I want common behavior (stuff that you do without thinking about it, like ctrl-x to cut text) to be ready to go out-of-the-box without the programmer having to do anything at all. No reading the manual, no copying from examples, no remembering obscure parts of the API, just ready to go. If they want to disable something like ctrl-x in text widgets, they have a different vision for the project and can write their own text widget, fork the project, or use some other library.

Still, the library does provide most of the lower-level things you mention. For example, there are methods to change the focused control. That's something I consider to be a low-level part of the API and something that users shouldn't have to use in most cases. On top of that layer, there's another layer that does listen for specific keyboard input (tab advances the focused control, shift-tab goes in the other direction, enter activates it). The library is written with the usual desktop UI toolkit keyboard input metaphors in mind, and I want things like that to "just work." If you're using the library, you're using it because you also want stuff like this to "just work."

And in order for that to work, at some point the library actually has to check for keyboard input, which brings us back to the problem as I see it: there's no way for it to check that keyboard input that is not destructive and does not require the user to wire all that shit up by hand.
User avatar
kikito
Inner party member
Posts: 3153
Joined: Sat Oct 03, 2009 5:22 pm
Location: Madrid, Spain
Contact:

Re: I LÖVE callbacks

Post by kikito »

I have said everything I wanted to say and I don't wish to continue discussing this. Good luck with your library.
When I write def I mean function.
Post Reply

Who is online

Users browsing this forum: No registered users and 4 guests