5. Getting user input (part 1)
So far we've learned how open a window, keep it there, pick crayons out of a box, draw funny squiggles with the crayons, and write our names in big fancy letters. Given that this ia a GUI tutorial, I have to assume some of you are wondering "man, when is this fucking guy gonna get to the point?"
To you I say "Get lost. You're not the boss of me." To everyone else, let's learn how to let the user do stuff.
gfx.mouse_cap
Yet another variable, although this one might be a bit weird if you haven't programmed much before.
gfx.mouse_cap stores the current state of the mouse; position, all the buttons, modifiers like Ctrl, Shift, etc, all in one binary number called a bitfield. The values you can access are as follows:
Code:
1 Left mouse button
2 Right mouse button
4 Control key
8 Shift key
16 Alt key
32 Windows key
64 Middle mouse button
To check them, you have to AND-compare
gfx.mouse_cap with the value you're looking for, like so:
Code:
-- Is the left button down?
if gfx.mouse_cap & 1 == 1 then
...do stuff...
else
...do other stuff?...
end
-- How about Shift + Right button?
if gfx.mouse_cap & 8 == 1 and gfx.mouse_cap & 2 == 1 then
...do stuff...
else
...do other stuff...
end
We also need to know WHERE the mouse is so we can tell if the user is clicking on our button or just flailing away at empty space like those metalcore kids fighting invisible ninjas in the mosh pit.
gfx.mouse_x, gfx.mouse_y
Such imaginative names. Two variables again, which return the coordinates of the mouse cursor within your graphics window. The top-left corner is 0, 0, the bottom-right corner will be 0 + gfx.w, 0 + gfx.h. I shouldn't have to explain what those two are.
Determining if the mouse is on top of our button is just a matter of "Is x between this and this? Is y between this and this? Cool." As with most of the other stuff we're dealing with in this thread, it's the sort of task that just screams for a helper function.
Code:
-- Are these coordinates inside the given area?
local function IsInside(x, y, w, h)
local mouse_x, mouse_y = gfx.mouse_x, gfx.mouse_y
local inside =
mouse_x >= x and mouse_x < (x + w) and
mouse_y >= y and mouse_y < (y + h)
return inside
end
For those not familiar with programming, that weird-looking set of comparisons is a much simpler way to say "if ___ then if ____ then if ____ then if ____". Because we only need
IsInside() to return
true or
false, setting a variable equal to the result of all those comparisons shortens things nicely.
Now that we're able to check the mouse position and the state of its buttons, we're ready to make our button clickable. What we want do is something like this:
Code:
Is the left button down?
If so, is the cursor inside our button?
If so, toggle the button's state
However, there's a wee problem with that; since this code is run every time the window updates, it will also toggle states if you simply haven't let go of the mouse button yet. Let's add a couple of things to that list:
Code:
Is the left button down?
Was it down the last time we checked?
If it wasn't, then the user has clicked, so:
Is the cursor inside our button?
If so, toggle the button's state
Store the mouse button's current state so we can check it next time
Translated into grown-up language, that works out to something like this:
Code:
-- If the left button is down
if gfx.mouse_cap & 1 == 1 then
-- If the cursor is inside the rectangle AND the button wasn't down before
if IsInside(x, y, w, h) and not mouse_btn_down then
mouse_btn_down = true
state = not state
end
-- If the left button is up
else
mouse_btn_down = false
end
Throwing that in with the code from the previous lesson should give you this:
There are, however, a couple of easy mistakes to watch out for:
-
state needs to hang around between
Main() loops, so it has to be either a global variable or declared locally outside the
Main() function.
- This code needs to be pasted somewhere after the
x,y,w,h variables are declared so that it can access them.
We've got a few other things to cover before we get around to making this button actually do stuff, but I'll leave you something easy to practice with:
Code:
retval = reaper.MB("Are you a human being?", "Voight-Kampff Test", 4)
That function will pop up a Yes/No message box, with the user's answer stored in retval. If you wanted to, you could take that number and have it determine the text of another string in the window. I'm just saying, that might be a useful thing to know how to do...