View Single Post
Old 05-12-2016, 08:43 PM   #27
Lokasenna
Human being with feelings
 
Lokasenna's Avatar
 
Join Date: Sep 2008
Location: Calgary, AB, Canada
Posts: 6,551
Default

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...
__________________
I'm no longer using Reaper or working on scripts for it. Sorry. :(
Default 5.0 Nitpicky Edition / GUI library for Lua scripts / Theory Helper / Radial Menu / Donate

Last edited by Lokasenna; 06-19-2017 at 03:35 PM.
Lokasenna is offline   Reply With Quote