Home
Blog
Tutorials
Work
Resume
Keyboard
Mouse Buttons
Mouse Buttons

The mouse has been one of the primary forms of imput for modern computers for quite some time. It is a convenient way of interacting with computers in a more visual fashion. With the cross-platform support of NME, mouse interactions can be easily translated into touch-screen controls. This makes an abstracted mouse button class extremely valuable for development. In this tutorial, we will be looking at a way to manage standard mouse interactions for Haxe + NME projects. Let's begin...

Here are some shortcut links to the various sections of this tutorial...
Button
ButtonTrial
MouseButton
Working Example

The Button Class
The Button class is going to be the backbone of your mouse handling. It will extend from the Sprite class, in order to give us access to most of the mouse interactions available to the standard Flash API. By default, the class will implement the six mouse interactions that I usually run into when developing applications. There are more available, and you can add whatever you need to your own implementation by following the existing examples.

Button.hx

package src;

import nme.display.Sprite;
import nme.events.MouseEvent;

class Button extends Sprite
{
	private var _mouseOver:Dynamic;
	private var _mouseOut:Dynamic;
	private var _mouseUp:Dynamic;
	private var _mouseDown:Dynamic;
	private var _mouseClick:Dynamic;
	private var _mouseDoubleClick:Dynamic;
	
	public function new()
	{
		super();
		
		_mouseOver = DefaultOver;
		_mouseOut = DefaultOut;
		_mouseUp = DefaultUp;
		_mouseDown = DefaultDown;
		_mouseClick = DefaultClick;
		_mouseDoubleClick = DefaultDoubleClick;
		
		this.addEventListener(MouseEvent.MOUSE_OVER, _mouseOver);
		this.addEventListener(MouseEvent.MOUSE_OUT, _mouseOut);
		this.addEventListener(MouseEvent.MOUSE_UP, _mouseUp);
		this.addEventListener(MouseEvent.MOUSE_DOWN, _mouseDown);
		this.addEventListener(MouseEvent.CLICK, _mouseClick);
		this.addEventListener(MouseEvent.DOUBLE_CLICK, _mouseDoubleClick);
	}
	
	public function DefaultOver(overEvent:MouseEvent):Void
	{
	}
	public function DefaultOut(outEvent:MouseEvent):Void
	{
	}
	public function DefaultUp(upEvent:MouseEvent):Void
	{
	}
	public function DefaultDown(downEvent:MouseEvent):Void
	{
	}
	public function DefaultClick(clickEvent:MouseEvent):Void
	{
	}
	public function DefaultDoubleClick(doubleClickEvent:MouseEvent):Void
	{
	}
	
	public var MouseOver(getMouseOver, setMouseOver):Dynamic;
	private function getMouseOver():Dynamic
	{
		return _mouseOver;
	}
	private function setMouseOver(freshOver:Dynamic):Dynamic
	{
		this.removeEventListener(MouseEvent.MOUSE_OVER, _mouseOver);
		_mouseOver = freshOver;
		this.addEventListener(MouseEvent.MOUSE_OVER, _mouseOver);
		return _mouseOver;
	}
	
	public var MouseOut(getMouseOut, setMouseOut):Dynamic;
	private function getMouseOut():Dynamic
	{
		return _mouseOut;
	}
	private function setMouseOut(freshOut:Dynamic):Dynamic
	{
		this.removeEventListener(MouseEvent.MOUSE_OUT, _mouseOut);
		_mouseOut = freshOut;
		this.addEventListener(MouseEvent.MOUSE_OUT, _mouseOut);
		return _mouseOut;
	}
	
	public var MouseUp(getMouseUp, setMouseUp):Dynamic;
	private function getMouseUp():Dynamic
	{
		return _mouseUp;
	}
	private function setMouseUp(freshUp:Dynamic):Dynamic
	{
		this.removeEventListener(MouseEvent.MOUSE_UP, _mouseUp);
		_mouseUp = freshUp;
		this.addEventListener(MouseEvent.MOUSE_UP, _mouseUp);
		return _mouseUp;
	}
	
	public var MouseDown(getMouseDown, setMouseDown):Dynamic;
	private function getMouseDown():Dynamic
	{
		return _mouseDown;
	}
	private function setMouseDown(freshDown:Dynamic):Dynamic
	{
		this.removeEventListener(MouseEvent.MOUSE_DOWN, _mouseDown);
		_mouseDown = freshDown;
		this.addEventListener(MouseEvent.MOUSE_DOWN, _mouseDown);
		return _mouseDown;
	}
	
	public var MouseClick(getMouseClick, setMouseClick):Dynamic;
	private function getMouseClick():Dynamic
	{
		return _mouseClick;
	}
	private function setMouseClick(freshClick:Dynamic):Dynamic
	{
		this.removeEventListener(MouseEvent.CLICK, _mouseClick);
		_mouseClick = freshClick;
		this.addEventListener(MouseEvent.CLICK, _mouseClick);
		return _mouseClick;
	}
	
	public var MouseDoubleClick(getMouseDoubleClick, setMouseDoubleClick):Dynamic;
	private function getMouseDoubleClick():Dynamic
	{
		return _mouseDoubleClick;
	}
	private function setMouseDoubleClick(freshDoubleClick:Dynamic):Dynamic
	{
		this.removeEventListener(MouseEvent.DOUBLE_CLICK, _mouseDoubleClick);
		_mouseDoubleClick = freshDoubleClick;
		this.addEventListener(MouseEvent.DOUBLE_CLICK, _mouseDoubleClick);
		return _mouseDoubleClick;
	}
}
						

The Button class looks complex at first glance, but the structure is actually very basic. It is comprised of a private variable, a public function, and a public property for each of the six basic mouse interactions. The import calls at the top are are the bare minimum of what we require. The class extends the Sprite class, so that one is obvious, and the MouseEvent class should also be self-explanatory. The private variables are Dynamic instances because they will be assigned functions as values. In Haxe, the Dynamic class is used when referencing functions, or using functions as arguments.

The series of Default functions are assigned to the private variables in the constructor. Event functions in the Flash API follow a standard structure. They take an instance of the related event as an argument, and they always return Void. It's also common for these functions to be private. Our Default functions are public because this is a base class, and we want to be able to override these functions in extended classes. At the end of the constructor, all of the private variables, (which are now referencing the Default event functions) are attached to event listeners for this class.

The properties for this class simply provide access for getting and setting our private variables. You'll notice that they all remove and then re-add the event listeners for the various mouse interactions. This is necessary when changing the behaviour for a button. The Flash API will only recognize one function per mouse event at a time. You can't assign a new function without first removing the existing event listener.

The ButtonTrial Class
The ButtonTrial class is an extension of the Button class that we just created. This class is pure implementation of our the foundation from the Button class. Here we use the Flash drawing API to add some graphics to our button, and we use our event listeners to add some functionality that we will be able to see.

ButtonTrial.hx

package src;

import src.Button;
import nme.events.MouseEvent;

class ButtonTrial extends Button
{
	private var _bWidth:Float;
	private var _bHeight:Float;
	private var _basicColor:Int;
	private var _overColor:Int;
	private var _downColor:Int;
	
	public function new(buttonWidth:Float, buttonHeight:Float)
	{
		super();
		this.cacheAsBitmap = true;
		_bWidth = buttonWidth;
		_bHeight = buttonHeight;
		_basicColor = 0x888888;
		_overColor = 0x666666;
		_downColor = 0x222222;
		
		DrawButton(_basicColor);
	}
	
	private function DrawButton(buttonColor:Int):Void
	{
		this.graphics.clear();
		this.graphics.beginFill(buttonColor);
		this.graphics.drawRect( -(_bWidth / 2.0), -(_bHeight / 2.0), _bWidth, _bHeight);
		this.graphics.endFill();
	}
	
	override public function DefaultOver(overEvent:MouseEvent):Void
	{
		DrawButton(_overColor);
	}
	override public function DefaultOut(outEvent:MouseEvent):Void
	{
		DrawButton(_basicColor);
	}
	override public function DefaultDown(downEvent:MouseEvent):Void
	{
		DrawButton(_downColor);
	}
	override public function DefaultUp(upEvent:MouseEvent):Void
	{
		DrawButton(_overColor);
	}
}
						

For the imports on this class, we are going to need our recently created Button class, as well as the MouseEvent class. Once that's out of the way, it's time to move on to the constructor. For this constructor we are going to use two arguments for the width and height of the button. Most of the drawing functions in Flash take Float values, so that's what we'll go with for our width and height arguments. You'll note the private variables for our colors. If we wanted to, we could add arguments for those color values as well, but for now we'll go without.

The DrawButton function does most of the heavy lifting for making our buttons appear. It accepts a color Int value, and draws a rectangle around the origin of this Button based on the width and height we fed it in the constructor. We will eventually be centering this button, and the calculations for that are easier when the button is drawn around its own origin. We call the DrawButton function once at the end of the constructor to insure there are graphics there by default.

And speaking of default, it's time to override our Default event functions. The original Default functions were empty, so our overridden versions don't need to reference the originals. In these override functions we add calls to the DrawButton function, which clears our button's graphics and re-draws them with the color specified. This will change the color of our button for each of our different mouse states. Now any button we create using this class will have these visual changes built in by default.

The MouseButton Class
The MouseButton class serves as our entry point for the application. Like all root objects in Flash, it extends the Sprite class. Our primary purpose here is to create an instance of our ButtonTrial class, and then customize its behaviour.

MouseButton.hx

package src;

import nme.display.Sprite;
import nme.display.StageAlign;
import nme.display.StageScaleMode;
import nme.events.MouseEvent;
import nme.Lib;
import src.ButtonTrial;

class MouseButton extends Sprite
{
	private var _freshButton:ButtonTrial;
	
	public function new()
	{
		super ();
		initialize ();
	}
	
	private function initialize():Void
	{
		Lib.current.stage.align = StageAlign.TOP_LEFT;
		Lib.current.stage.scaleMode = StageScaleMode.NO_SCALE;
		
		_freshButton = new ButtonTrial(240.0, 62.0);
		this.addChild(_freshButton);
		_freshButton.x = cast(Lib.current.stage.stageWidth, Float) / 2.0;
		_freshButton.y = cast(Lib.current.stage.stageHeight, Float) / 2.0;
		_freshButton.MouseClick = ClickHappened;
		_freshButton.MouseDown = DownHappened;
	}
	
	private function ClickHappened(clickEvent:MouseEvent):Void
	{
		trace("Button Location X:" + cast(clickEvent.target, Sprite).x + ", Y:"
			 + cast(clickEvent.target, Sprite).y);
	}
	
	private function DownHappened(downEvent:MouseEvent):Void
	{
		cast(downEvent.target, ButtonTrial).DefaultDown(downEvent);
		cast(downEvent.target, Sprite).graphics.beginFill(0xFFFFFF);
		cast(downEvent.target, Sprite).graphics.drawCircle(0.0, 0.0, 16.0);
		cast(downEvent.target, Sprite).graphics.endFill();
	}
	
	// Entry point
	public static function main()
	{
		Lib.current.addChild (new MouseButton ());
	}
}
						

The imports here are fairly standard, especially for an entry-point class. Note that we are importing the ButtonTrial class that we already created. The main function and constructor functions are also very run-of-the-mill.

The ClickHappened and DownHappened functions are where things start to get interesting. Both of these functions follow the standard structure for event functions, and that's what we will be using them for. In our ButtonTrial class, we didn't bother to override the DefaultClick function. So the ClickHappened function just does whatever it pleases. The DownHappened function is different. We have a default behaviour for that function, and we don't want to lose it. The first line of the DownHappened function calls the DefaultDown function. This insures that our default behaviour will still remain. The rest of the DownHappened function will be in addition to the DefaultDown function.

Heading back up to the constructor, we actually put it all together. We create our new ButtonTrial instance, and give it a width and height. Then we add it to the display list, and re-position it so that it is centered within the application. Finally, we use its MouseClick and MouseDown properties to change the behaviour particular to this instance.

Working Example - Flash

The Flash content is going to go here


Source Code
Click Here To Download the Source Code
The source code for this project includes all of the original source files, the folder structure, and the NMML command file that I used to compile the project. This software is distributed here without any license attached. You may do with it as you please. Will Hawthorne makes no promises and claims no liability for what is done with this software. Please use responsibly.

EMail
Google+
LinkedIn
Facebook
© 2008 - 2017 William Hawthorne