Explorar o código

Add support for animated cursor to JS, HLDX and HLSDL targets. (#429)

Pavel Alexandrov %!s(int64=7) %!d(string=hai) anos
pai
achega
1a9786ebf8
Modificáronse 4 ficheiros con 144 adicións e 30 borrados
  1. 46 5
      hxd/Cursor.hx
  2. 35 12
      hxd/System.hl.hx
  3. 28 3
      hxd/System.js.hx
  4. 35 10
      samples/Cursor.hx

+ 46 - 5
hxd/Cursor.hx

@@ -17,9 +17,9 @@ class CustomCursor {
 	var offsetX : Int;
 	var offsetY : Int;
 	#if hlsdl
-	var alloc : sdl.Cursor;
+	var alloc : Array<sdl.Cursor>;
 	#elseif hldx
-	var alloc : dx.Cursor;
+	var alloc : Array<dx.Cursor>;
 	#elseif flash
 	static var UID = 0;
 	var name : String;
@@ -29,7 +29,14 @@ class CustomCursor {
 	#else
 	var alloc : Dynamic;
 	#end
-
+	
+	// Heaps-side cursor animation for target that do not support native animated cursors.
+	#if (hlsdl || hldx || js)
+	var frameDelay : Float;
+	var frameTime : Float;
+	var frameIndex : Int;
+	#end
+	
 	public function new( frames, speed, offsetX, offsetY ) {
 		this.frames = frames;
 		this.speed = speed;
@@ -38,7 +45,37 @@ class CustomCursor {
 		#if flash
 		name = "custom_" + UID++;
 		#end
+		#if (hlsdl || hldx || js)
+		frameDelay = 1 / speed;
+		frameTime = 0;
+		frameIndex = 0;
+		#end
+	}
+	
+	#if (hlsdl || hldx || js)
+	public function reset() : Void {
+		frameTime = 0;
+		frameIndex = 0;
 	}
+	
+	public function update( dt : Float ) : Int {
+		var newTime : Float = frameTime + dt;
+		var delay : Float = frameDelay;
+		var index : Int = frameIndex;
+		while( newTime >= delay ) {
+			newTime -= delay;
+			index++;
+		}
+		frameTime = newTime;
+		
+		if ( index >= frames.length ) index %= frames.length;
+		if ( index != frameIndex ) {
+			frameIndex = index;
+			return index;
+		}
+		return -1;
+	}
+	#end
 
 	public function dispose() {
 		for( f in frames )
@@ -46,11 +83,15 @@ class CustomCursor {
 		frames = [];
 		if( alloc != null ) {
 			#if hlsdl
-			alloc.free();
+			for (cur in alloc) {
+				cur.free();
+			}
 			#elseif flash
 			flash.ui.Mouse.unregisterCursor(name);
 			#elseif hldx
-			alloc.destroy();
+			for (cur in alloc) {
+				cur.destroy();
+			}
 			#elseif js
 			// alloc set to null below.
 			#else

+ 35 - 12
hxd/System.hl.hx

@@ -41,6 +41,7 @@ class System {
 
 	// -- HL
 	static var currentNativeCursor : hxd.Cursor = Default;
+	static var currentCustomCursor : hxd.Cursor.CustomCursor;
 	static var cursorVisible = true;
 
 	public static function getCurrentLoop() : Void -> Void {
@@ -172,6 +173,7 @@ class System {
 		if( c.equals(currentNativeCursor) )
 			return;
 		currentNativeCursor = c;
+		currentCustomCursor = null;
 		if( c == Hide ) {
 			cursorVisible = false;
 			Cursor.show(false);
@@ -191,19 +193,25 @@ class System {
 			throw "assert";
 		case Custom(c):
 			if( c.alloc == null ) {
-				if( c.frames.length > 1 ) throw "Animated cursor not supported";
-				var pixels = c.frames[0].getPixels();
-				pixels.convert(BGRA);
-				#if hlsdl
-				var surf = sdl.Surface.fromBGRA(pixels.bytes, pixels.width, pixels.height);
-				c.alloc = sdl.Cursor.create(surf, c.offsetX, c.offsetY);
-				surf.free();
-				#elseif hldx
-				c.alloc = dx.Cursor.createCursor(pixels.width, pixels.height, pixels.bytes, c.offsetX, c.offsetY);
-				#end
-				pixels.dispose();
+				c.alloc = new Array();
+				for ( frame in c.frames ) {
+					var pixels = frame.getPixels();
+					pixels.convert(BGRA);
+					#if hlsdl
+					var surf = sdl.Surface.fromBGRA(pixels.bytes, pixels.width, pixels.height);
+					c.alloc.push(sdl.Cursor.create(surf, c.offsetX, c.offsetY));
+					surf.free();
+					#elseif hldx
+					c.alloc.push(dx.Cursor.createCursor(pixels.width, pixels.height, pixels.bytes, c.offsetX, c.offsetY));
+					#end
+					pixels.dispose();
+				}
 			}
-			cur = c.alloc;
+			if ( c.frames.length > 1 ) {
+				currentCustomCursor = c;
+				c.reset();
+			}
+			cur = c.alloc[c.frameIndex];
 		}
 		cur.set();
 		if( !cursorVisible ) {
@@ -212,6 +220,18 @@ class System {
 		}
 		#end
 	}
+	
+	#if (hlsdl || hldx)
+	static function updateCursor() : Void {
+		if (currentCustomCursor != null)
+		{
+			var change = currentCustomCursor.update(hxd.Timer.deltaT);
+			if (change != -1) {
+				currentCustomCursor.alloc[change].set();
+			}
+		}
+	}
+	#end
 
 	public static function getDeviceName() : String {
 		#if usesys
@@ -310,6 +330,9 @@ class System {
 		sentinel = new hl.UI.Sentinel(30, function() throw "Program timeout (infinite loop?)");
 		haxe.MainLoop.add(timeoutTick, -1) #if (haxe_ver >= 4) .isBlocking = false #end;
 		#end
+		#if (hlsdl || hldx)
+		haxe.MainLoop.add(updateCursor, -1);
+		#end
 	}
 
 }

+ 28 - 3
hxd/System.js.hx

@@ -32,6 +32,8 @@ class System {
 
 	// JS
 	static var loopInit = false;
+	static var currentNativeCursor:hxd.Cursor;
+	static var currentCustomCursor:hxd.Cursor.CustomCursor;
 
 	public static function getCurrentLoop() : Void -> Void {
 		return loopFunc;
@@ -59,6 +61,10 @@ class System {
 	}
 
 	public static function setNativeCursor( c : Cursor ) : Void {
+		if( c.equals(currentNativeCursor) )
+			return;
+		currentNativeCursor = c;
+		currentCustomCursor = null;
 		var canvas = @:privateAccess hxd.Stage.getInstance().canvas;
 		if( canvas != null ) {
 			canvas.style.cursor = switch( c ) {
@@ -69,13 +75,16 @@ class System {
 			case Hide: "none";
 			case Custom(cur):
 				if ( cur.alloc == null ) {
-					if ( cur.frames.length > 1 ) throw "Animated cursor not supported";
 					cur.alloc = new Array();
 					for ( frame in cur.frames ) {
 						cur.alloc.push("url(\"" + frame.toNative().canvas.toDataURL("image/png") + "\") " + cur.offsetX + " " + cur.offsetY + ", default");
 					}
 				}
-				cur.alloc[0];
+				if ( cur.frames.length > 1 ) {
+					currentCustomCursor = cur;
+					cur.reset();
+				}
+				cur.alloc[cur.frameIndex];
 			};
 		}
 	}
@@ -95,6 +104,18 @@ class System {
 	public static function exit() : Void {
 	}
 
+	static function updateCursor() : Void {
+		if ( currentCustomCursor != null ) {
+			var change = currentCustomCursor.update(hxd.Timer.deltaT);
+			if ( change != -1 ) {
+				var canvas = @:privateAccess hxd.Stage.getInstance().canvas;
+				if ( canvas != null ) {
+					canvas.style.cursor = currentCustomCursor.alloc[change];
+				}
+			}
+		}
+	}
+	
 	// getters
 
 	static function get_width() : Int return Math.round(js.Browser.document.body.clientWidth * js.Browser.window.devicePixelRatio);
@@ -104,5 +125,9 @@ class System {
 	static function get_screenDPI() : Int return 72;
 	static function get_allowTimeout() return false;
 	static function set_allowTimeout(b) return false;
-
+	
+	static function __init__() : Void {
+		haxe.MainLoop.add(updateCursor, -1);
+	}
+	
 }

+ 35 - 10
samples/Cursor.hx

@@ -1,5 +1,5 @@
 class Cursor extends hxd.App {
-
+	
 	override function init() {
 
 		engine.backgroundColor = 0xFF202020;
@@ -10,11 +10,29 @@ class Cursor extends hxd.App {
 		bmp.line(0, 0, 0, 31, 0xFF0000FF);
 		bmp.line(0, 31, 31, 31, 0xFFFF0000);
 		bmp.line(31, 0, 31, 31, 0xFF00FF00);
-
-		var cursors : Array<hxd.Cursor> = [Default,Button,Move,TextInput,Hide,Custom(new hxd.Cursor.CustomCursor([bmp],0.,16,16))];
+		
+		var animationSupported = false;
+		#if (flash || js || hldx || hlsdl)
+		animationSupported = true;
+		#end
+		
+		var animatedFrames = [bmp];
+		if (animationSupported)
+		{
+			var bmp2 = new hxd.BitmapData(32, 32);
+			bmp2.clear(0x80FF0000);
+			bmp2.line(0, 0, 31, 0, 0xFFFFFFFF);
+			bmp2.line(0, 0, 0, 31, 0xFF0000FF);
+			bmp2.line(0, 31, 31, 31, 0xFFFF0000);
+			bmp2.line(31, 0, 31, 31, 0xFF00FF00);
+			bmp2.fill(15, 15, 2, 2, 0xFFFF00FF);
+			animatedFrames.push(bmp2);
+		}
+		
+		var cursors : Array<hxd.Cursor> = [Default,Button,Move,TextInput,Hide,Custom(new hxd.Cursor.CustomCursor([bmp],10,16,16)),Custom(new hxd.Cursor.CustomCursor(animatedFrames,10,16,16))];
 		var pos = 0;
 		for( c in cursors ) {
-			var i = new h2d.Interactive(100, 20, s2d);
+			var i = new h2d.Interactive(120, 20, s2d);
 			var tf = new h2d.Text(hxd.res.DefaultFont.get(), i);
 			tf.text = c.getName();
 			tf.x = 5;
@@ -22,12 +40,19 @@ class Cursor extends hxd.App {
 			i.y = pos++ * 20;
 			i.cursor = c;
 			i.backgroundColor = Std.random(0x1000000) | 0xFF000000;
-
-			var supported = true;
-
-			if( !supported ) {
-				tf.textColor = 0xFF0000;
-				i.cursor = Default;
+			
+			switch(c) {
+				case Custom(cur):
+					
+					if (@:privateAccess cur.frames.length > 1) {
+						tf.text = "Custom (animated)";
+						if (!animationSupported) {
+							i.cursor = Default;
+							tf.textColor = 0xFF0000;
+						}
+					}
+					
+				default:
 			}
 		}
 	}