Prechádzať zdrojové kódy

separated scene events into hxd.SceneEvents

ncannasse 9 rokov pred
rodič
commit
6375552589
5 zmenil súbory, kde vykonal 347 pridanie a 208 odobranie
  1. 24 32
      h2d/Interactive.hx
  2. 45 175
      h2d/Scene.hx
  3. 10 1
      hxd/App.hx
  4. 1 0
      hxd/Event.hx
  5. 267 0
      hxd/SceneEvents.hx

+ 24 - 32
h2d/Interactive.hx

@@ -1,6 +1,6 @@
 package h2d;
 
-class Interactive extends Drawable {
+class Interactive extends Drawable implements hxd.SceneEvents.Interactive {
 
 	public var width : Float;
 	public var height : Float;
@@ -50,13 +50,8 @@ class Interactive extends Drawable {
 
 	override function onDelete() {
 		if( scene != null ) {
-			scene.removeEventTarget(this);
-			if( scene.currentOver == this ) {
-				scene.currentOver = null;
-				hxd.System.setCursor(Default);
-			}
-			if( scene.currentFocus == this )
-				scene.currentFocus = null;
+			scene.removeEventTarget(this, true);
+			scene = null;
 		}
 		super.onDelete();
 	}
@@ -68,8 +63,11 @@ class Interactive extends Drawable {
 		}
 	}
 
-	@:allow(h2d.Scene)
-	function handleEvent( e : hxd.Event ) {
+	@:noCompletion public function getInteractiveScene() : hxd.SceneEvents.InteractiveScene {
+		return getScene();
+	}
+
+	@:noCompletion public function handleEvent( e : hxd.Event ) {
 		if( isEllipse && checkBounds(e) ) {
 			var cx = width * 0.5, cy = height * 0.5;
 			var dx = (e.relX - cx) / cx;
@@ -96,6 +94,13 @@ class Interactive extends Drawable {
 					onClick(e);
 			}
 			isMouseDown = -1;
+		case EReleaseNoClick:
+			if( enableRightButton || e.button == 0 ) {
+				e.kind = ERelease;
+				onRelease(e);
+				e.kind = EReleaseNoClick;
+			}
+			isMouseDown = -1;
 		case EOver:
 			hxd.System.setCursor(cursor);
 			onOver(e);
@@ -107,10 +112,8 @@ class Interactive extends Drawable {
 			onWheel(e);
 		case EFocusLost:
 			onFocusLost(e);
-			if( !e.cancel && scene != null && scene.currentFocus == this ) scene.currentFocus = null;
 		case EFocus:
 			onFocus(e);
-			if( !e.cancel && scene != null ) scene.currentFocus = this;
 		case EKeyUp:
 			onKeyUp(e);
 		case EKeyDown:
@@ -120,7 +123,7 @@ class Interactive extends Drawable {
 
 	function set_cursor(c) {
 		this.cursor = c;
-		if( scene != null && scene.currentOver == this )
+		if( isOver() )
 			hxd.System.setCursor(cursor);
 		return c;
 	}
@@ -166,32 +169,21 @@ class Interactive extends Drawable {
 	}
 
 	public function focus() {
-		if( scene == null )
+		if( scene == null || scene.events == null )
 			return;
-		var ev = new hxd.Event(null);
-		if( scene.currentFocus != null ) {
-			if( scene.currentFocus == this )
-				return;
-			ev.kind = EFocusLost;
-			scene.currentFocus.handleEvent(ev);
-			if( ev.cancel ) return;
-		}
-		ev.kind = EFocus;
-		handleEvent(ev);
+		scene.events.focus(this);
 	}
 
 	public function blur() {
-		if( scene == null )
-			return;
-		if( scene.currentFocus == this ) {
-			var ev = new hxd.Event(null);
-			ev.kind = EFocusLost;
-			scene.currentFocus.handleEvent(ev);
-		}
+		if( hasFocus() ) scene.events.blur();
+	}
+
+	public function isOver() {
+		return scene != null && scene.events != null && @:privateAccess scene.events.currentOver == this;
 	}
 
 	public function hasFocus() {
-		return scene != null && scene.currentFocus == this;
+		return scene != null && scene.events != null && @:privateAccess scene.events.currentFocus == this;
 	}
 
 	public dynamic function onOver( e : hxd.Event ) {

+ 45 - 175
h2d/Scene.hx

@@ -1,7 +1,7 @@
 package h2d;
 import hxd.Math;
 
-class Scene extends Layers implements h3d.IDrawable {
+class Scene extends Layers implements h3d.IDrawable implements hxd.SceneEvents.InteractiveScene {
 
 	public var width(default,null) : Int;
 	public var height(default, null) : Int;
@@ -13,19 +13,11 @@ class Scene extends Layers implements h3d.IDrawable {
 
 	var fixedSize : Bool;
 	var interactive : Array<Interactive>;
-	var pendingEvents : Array<hxd.Event>;
+	var eventListeners : Array< hxd.Event -> Void >;
 	var ctx : RenderContext;
 	var stage : hxd.Stage;
-
-	@:allow(h2d.Interactive)
-	var currentOver : Interactive;
 	@:allow(h2d.Interactive)
-	var currentFocus : Interactive;
-
-	var pushList : Array<Interactive>;
-	var currentDrag : { f : hxd.Event -> Void, onCancel : Void -> Void, ref : Null<Int> };
-	var eventListeners : Array< hxd.Event -> Void >;
-	var fakeMove : hxd.Event;
+	var events : hxd.SceneEvents;
 
 	public function new() {
 		super(null);
@@ -34,12 +26,15 @@ class Scene extends Layers implements h3d.IDrawable {
 		width = e.width;
 		height = e.height;
 		interactive = new Array();
-		pushList = new Array();
 		eventListeners = new Array();
 		stage = hxd.Stage.getInstance();
 		posChanged = true;
 	}
 
+	public function setEvents(events) {
+		this.events = events;
+	}
+
 	function get_zoom() {
 		return Std.int(h3d.Engine.getCurrent().width / width);
 	}
@@ -75,26 +70,11 @@ class Scene extends Layers implements h3d.IDrawable {
 		}
 	}
 
-	override function onAlloc() {
-		stage.addEventTarget(onEvent);
-		super.onAlloc();
-	}
-
-	override function onDelete() {
-		stage.removeEventTarget(onEvent);
-		super.onDelete();
-	}
-
-	function onEvent( e : hxd.Event ) {
-		if( pendingEvents != null )
-			pendingEvents.push(e);
-	}
-
-	function screenXToLocal(mx:Float) {
+	inline function screenXToLocal(mx:Float) {
 		return mx * width / (stage.width * scaleX) - x;
 	}
 
-	function screenYToLocal(my:Float) {
+	inline function screenYToLocal(my:Float) {
 		return my * height / (stage.height * scaleY) - y;
 	}
 
@@ -106,9 +86,8 @@ class Scene extends Layers implements h3d.IDrawable {
 		return screenYToLocal(stage.mouseY);
 	}
 
-	function dispatchListeners( event : hxd.Event ) {
-		event.propagate = true;
-		event.cancel = false;
+	public function dispatchListeners( event : hxd.Event ) {
+		screenToLocal(event);
 		for( l in eventListeners ) {
 			l(event);
 			if( !event.propagate ) break;
@@ -162,25 +141,23 @@ class Scene extends Layers implements h3d.IDrawable {
 		return null;
 	}
 
-	function emitEvent( event : hxd.Event ) {
-		var x = event.relX, y = event.relY;
+	public function screenToLocal( e : hxd.Event ) {
+		var x = screenXToLocal(e.relX);
+		var y = screenYToLocal(e.relY);
 		var rx = x * matA + y * matB + absX;
 		var ry = x * matC + y * matD + absY;
-		var handled = false;
-		var checkOver = false, checkPush = false, cancelFocus = false;
-		switch( event.kind ) {
-		case EMove: checkOver = true;
-		case EPush: cancelFocus = true; checkPush = true;
-		case ERelease: checkPush = true;
-		case EKeyUp, EKeyDown, EWheel:
-			if( currentFocus != null ) {
-				currentFocus.handleEvent(event);
-				if( !event.propagate )
-					return;
-			}
-		default:
-		}
-		for( i in interactive ) {
+		e.relX = rx;
+		e.relY = ry;
+	}
+
+	public function handleEvent( event : hxd.Event, last : hxd.SceneEvents.Interactive ) : hxd.SceneEvents.Interactive {
+		screenToLocal(event);
+		var rx = event.relX;
+		var ry = event.relY;
+		var index = last == null ? 0 : interactive.indexOf(cast last);
+		for( idx in index...interactive.length ) {
+			var i = interactive[idx];
+			if( i == null ) break;
 
 			var dx = rx - i.absX;
 			var dy = ry - i.absY;
@@ -227,130 +204,12 @@ class Scene extends Layers implements h3d.IDrawable {
 			if( event.cancel ) {
 				event.cancel = false;
 				event.propagate = true;
-			} else if( checkOver ) {
-				if( currentOver != i ) {
-					var old = event.propagate;
-					if( currentOver != null ) {
-						event.kind = EOut;
-						// relX/relY is not correct here
-						currentOver.handleEvent(event);
-					}
-					event.kind = EOver;
-					event.cancel = false;
-					i.handleEvent(event);
-					if( event.cancel )
-						currentOver = null;
-					else {
-						currentOver = i;
-						checkOver = false;
-					}
-					event.kind = EMove;
-					event.cancel = false;
-					event.propagate = old;
-				} else
-					checkOver = false;
-			} else {
-				if( checkPush ) {
-					if( event.kind == EPush )
-						pushList.push(i);
-					else
-						pushList.remove(i);
-				}
-				if( cancelFocus && i == currentFocus )
-					cancelFocus = false;
-			}
-
-			if( event.propagate ) {
-				event.propagate = false;
 				continue;
 			}
 
-			handled = true;
-			break;
-		}
-		if( cancelFocus && currentFocus != null ) {
-			event.kind = EFocusLost;
-			currentFocus.handleEvent(event);
-			event.kind = EPush;
-		}
-		if( checkOver && currentOver != null ) {
-			event.kind = EOut;
-			currentOver.handleEvent(event);
-			event.kind = EMove;
-			currentOver = null;
-		}
-		if( !handled && event != fakeMove ) {
-			if( event.kind == EPush )
-				pushList.push(null);
-			dispatchListeners(event);
-		}
-	}
-
-	function hasEvents() {
-		return interactive.length > 0 || eventListeners.length > 0;
-	}
-
-	public function checkEvents() {
-		if( pendingEvents == null ) {
-			if( !hasEvents() )
-				return;
-			pendingEvents = new Array();
-		}
-		var old = pendingEvents;
-		if( old.length == 0 )
-			return;
-		pendingEvents = null;
-		var checkMoved = false;
-		for( e in old ) {
-			var ox = e.relX, oy = e.relY;
-			e.relX = screenXToLocal(ox);
-			e.relY = screenYToLocal(oy);
-			if( e.kind == EMove ) checkMoved = true;
-
-			if( currentDrag != null && (currentDrag.ref == null || currentDrag.ref == e.touchId) ) {
-				currentDrag.f(e);
-				if( e.cancel ) {
-					e.relX = ox;
-					e.relY = oy;
-					continue;
-				}
-			}
-
-			emitEvent(e);
-
-			/*
-				We want to make sure that after we have pushed, we send a release even if the mouse
-				has been outside of the Interactive (release outside). We will reset the mouse button
-				to prevent click to be generated
-			*/
-			if( e.kind == ERelease && pushList.length > 0 ) {
-				e.relX = screenXToLocal(ox);
-				e.relY = screenYToLocal(oy);
-				for( i in pushList ) {
-					if( i == null )
-						dispatchListeners(e);
-					else {
-						@:privateAccess i.isMouseDown = -1;
-						// relX/relY not good here
-						i.handleEvent(e);
-					}
-				}
-				pushList = new Array();
-			}
-
-			e.relX = ox;
-			e.relY = oy;
-		}
-
-		if( !checkMoved && currentDrag == null ) {
-			if( fakeMove == null ) fakeMove = new hxd.Event(EMove);
-			fakeMove.relX = mouseX;
-			fakeMove.relY = mouseY;
-			emitEvent(fakeMove);
+			return i;
 		}
-
-		if( hasEvents() )
-			pendingEvents = new Array();
+		return null;
 	}
 
 	public function addEventListener( f : hxd.Event -> Void ) {
@@ -362,17 +221,26 @@ class Scene extends Layers implements h3d.IDrawable {
 	}
 
 	public function startDrag( f : hxd.Event -> Void, ?onCancel : Void -> Void, ?refEvent : hxd.Event ) {
-		if( currentDrag != null && currentDrag.onCancel != null )
-			currentDrag.onCancel();
-		currentDrag = { f : f, ref : refEvent == null ? null : refEvent.touchId, onCancel : onCancel };
+		events.startDrag(function(e) {
+			screenToLocal(e);
+			f(e);
+		},onCancel, refEvent);
 	}
 
 	public function stopDrag() {
-		currentDrag = null;
+		events.stopDrag();
 	}
 
 	public function getFocus() {
-		return currentFocus;
+		if( events == null )
+			return null;
+		var f = events.getFocus();
+		if( f == null )
+			return null;
+		var i = Std.instance(f, h2d.Interactive);
+		if( i == null )
+			return null;
+		return interactive[interactive.indexOf(i)];
 	}
 
 	@:allow(h2d)
@@ -428,12 +296,14 @@ class Scene extends Layers implements h3d.IDrawable {
 	}
 
 	@:allow(h2d)
-	function removeEventTarget(i) {
+	function removeEventTarget(i,notify=false) {
 		for( k in 0...interactive.length )
 			if( interactive[k] == i ) {
 				interactive.splice(k, 1);
 				break;
 			}
+		if( notify && events != null )
+			@:privateAccess events.onRemove(i);
 	}
 
 	public function dispose() {

+ 10 - 1
hxd/App.hx

@@ -5,6 +5,7 @@ class App {
 	public var engine : h3d.Engine;
 	public var s3d : h3d.scene.Scene;
 	public var s2d : h2d.Scene;
+	public var sevents : hxd.SceneEvents;
 
 	public var wantedFPS(get, set) : Float;
 
@@ -38,6 +39,8 @@ class App {
 		s3d = new h3d.scene.Scene();
 		s2d = new h2d.Scene();
 		s3d.addPass(s2d);
+		sevents = new hxd.SceneEvents();
+		sevents.addScene(s2d);
 		loadAssets(function() {
 			initDone = true;
 			init();
@@ -47,6 +50,12 @@ class App {
 			hxd.Key.initialize();
 		});
 	}
+	
+	function dispose() {
+		s2d.dispose();
+		s3d.dispose();
+		sevents.dispose();
+	}
 
 	function loadAssets( onLoaded ) {
 		onLoaded();
@@ -57,7 +66,7 @@ class App {
 
 	function mainLoop() {
 		hxd.Timer.update();
-		s2d.checkEvents();
+		sevents.checkEvents();
 		update(hxd.Timer.tmod);
 		s2d.setElapsedTime(Timer.tmod/60);
 		s3d.setElapsedTime(Timer.tmod / 60);

+ 1 - 0
hxd/Event.hx

@@ -11,6 +11,7 @@ enum EventKind {
 	EFocusLost;
 	EKeyDown;
 	EKeyUp;
+	EReleaseNoClick;
 }
 
 class Event {

+ 267 - 0
hxd/SceneEvents.hx

@@ -0,0 +1,267 @@
+package hxd;
+
+interface InteractiveScene {
+	public function setEvents( s : SceneEvents ) : Void;
+	public function screenToLocal( e : Event ) : Void;
+	public function handleEvent( e : Event, last : Interactive ) : Interactive;
+	public function dispatchListeners( e : Event ) : Void;
+}
+
+interface Interactive {
+	public function handleEvent( e : Event ) : Void;
+	public function getInteractiveScene() : InteractiveScene;
+}
+
+class SceneEvents {
+
+	var scenes : Array<InteractiveScene>;
+
+	var currentOver : Interactive;
+	var currentFocus : Interactive;
+
+	var pendingEvents : Array<hxd.Event>;
+	var pushList : Array<Interactive>;
+	var currentDrag : { f : hxd.Event -> Void, onCancel : Void -> Void, ref : Null<Int> };
+	var mouseX = -1.;
+	var mouseY = -1.;
+
+	var focusLost = new hxd.Event(EFocusLost);
+	var fakeMove = new hxd.Event(EMove);
+	var onOut = new hxd.Event(EOut);
+
+	public function new() {
+		scenes = [];
+		pendingEvents = [];
+		pushList = [];
+		hxd.Stage.getInstance().addEventTarget(onEvent);
+	}
+
+	function onRemove(i) {
+		if( i == currentFocus )
+			currentFocus = null;
+		if( i == currentOver )
+			currentOver = null;
+		pushList.remove(i);
+	}
+
+	public function addScene( s : InteractiveScene, ?index : Int ) {
+		s.setEvents(this);
+		if( index == null ) scenes.push(s) else scenes.insert(index, s);
+	}
+
+	public function removeScene( s : InteractiveScene ) {
+		if( scenes.remove(s) ) s.setEvents(null);
+	}
+
+	public function dispose() {
+		hxd.Stage.getInstance().removeEventTarget(onEvent);
+	}
+
+	public function focus( i : Interactive ) {
+		if( currentFocus == i )
+			return;
+		if( i == null ) {
+			blur();
+			return;
+		}
+		if( currentFocus != null ) {
+			blur();
+			if( currentFocus != null ) return;
+		}
+		var e = new hxd.Event(EFocus);
+		i.handleEvent(e);
+		if( !e.cancel )
+			currentFocus = i;
+	}
+
+	public function blur() {
+		if( currentFocus == null )
+			return;
+		focusLost.cancel = false;
+		currentFocus.handleEvent(focusLost);
+		if( !focusLost.cancel )
+			currentFocus = null;
+	}
+
+	function emitEvent( event : hxd.Event ) {
+		var oldX = event.relX, oldY = event.relY;
+		var handled = false;
+		var checkOver = false, checkPush = false, cancelFocus = false;
+		switch( event.kind ) {
+		case EMove: checkOver = true;
+		case EPush: cancelFocus = true; checkPush = true;
+		case ERelease: checkPush = true;
+		case EKeyUp, EKeyDown, EWheel:
+			if( currentFocus != null ) {
+				event.relX = event.relY = 0;
+				currentFocus.handleEvent(event);
+				event.relX = oldX;
+				event.relY = oldY;
+				if( !event.propagate )
+					return;
+			}
+		default:
+		}
+
+		for( s in scenes ) {
+			var last = null;
+			while( true ) {
+				var i = s.handleEvent(event, last);
+
+				if( i == null ) break;
+
+				if( checkOver ) {
+					if( currentOver != i ) {
+						onOut.cancel = false;
+						if( currentOver != null )
+							currentOver.handleEvent(onOut);
+						if( !onOut.cancel ) {
+							var old = event.propagate;
+							event.kind = EOver;
+							event.cancel = false;
+							i.handleEvent(event);
+							if( event.cancel )
+								currentOver = null;
+							else {
+								currentOver = i;
+								checkOver = false;
+							}
+							event.kind = EMove;
+							event.cancel = false;
+							event.propagate = old;
+						}
+					} else
+						checkOver = false;
+				} else {
+					if( checkPush ) {
+						if( event.kind == EPush )
+							pushList.push(i);
+						else
+							pushList.remove(i);
+					}
+					if( cancelFocus && i == currentFocus )
+						cancelFocus = false;
+				}
+
+				event.relX = oldX;
+				event.relY = oldY;
+
+				if( !event.propagate ) {
+					handled = true;
+					break;
+				}
+
+				last = i;
+				event.propagate = false;
+			}
+			if( handled )
+				break;
+		}
+
+		if( cancelFocus && currentFocus != null )
+			blur();
+
+		if( checkOver && currentOver != null ) {
+			onOut.cancel = false;
+			currentOver.handleEvent(onOut);
+			if( !onOut.cancel )
+				currentOver = null;
+		}
+
+		if( !handled && event != fakeMove ) {
+			if( event.kind == EPush )
+				pushList.push(null);
+			else if( event.kind == ERelease )
+				pushList.remove(null);
+			dispatchListeners(event);
+		}
+
+		/*
+			We want to make sure that after we have pushed, we send a release even if the mouse
+			has been outside of the Interactive (release outside). We don't generate a click in that case.
+		*/
+		if( event.kind == ERelease && pushList.length > 0 ) {
+			for( i in pushList ) {
+				if( i == null )
+					dispatchListeners(event);
+				else {
+					var s = i.getInteractiveScene();
+					if( s == null ) continue;
+					event.kind = EReleaseNoClick;
+					s.screenToLocal(event);
+					i.handleEvent(event);
+					event.kind = ERelease;
+					event.relX = oldX;
+					event.relY = oldY;
+				}
+			}
+			pushList = new Array();
+		}
+	}
+
+	public function checkEvents() {
+		var old = pendingEvents;
+		if( old.length == 0 )
+			return;
+
+		pendingEvents = [];
+		var checkMoved = false;
+		for( e in old ) {
+			var ox = e.relX, oy = e.relY;
+			if( e.kind == EMove ) {
+				checkMoved = true;
+				mouseX = e.relX;
+				mouseY = e.relY;
+			}
+
+			if( currentDrag != null && (currentDrag.ref == null || currentDrag.ref == e.touchId) ) {
+				currentDrag.f(e);
+				e.relX = ox;
+				e.relY = oy;
+				if( e.cancel )
+					continue;
+			}
+
+			emitEvent(e);
+		}
+
+		if( !checkMoved && currentDrag == null ) {
+			fakeMove.relX = mouseX;
+			fakeMove.relY = mouseY;
+			fakeMove.cancel = false;
+			fakeMove.propagate = false;
+			emitEvent(fakeMove);
+		}
+	}
+
+	public function startDrag( f : hxd.Event -> Void, ?onCancel : Void -> Void, ?refEvent : hxd.Event ) {
+		if( currentDrag != null && currentDrag.onCancel != null )
+			currentDrag.onCancel();
+		currentDrag = { f : f, ref : refEvent == null ? null : refEvent.touchId, onCancel : onCancel };
+	}
+
+	public function stopDrag() {
+		currentDrag = null;
+	}
+
+	public function getFocus() {
+		return currentFocus;
+	}
+
+	function onEvent( e : hxd.Event ) {
+		pendingEvents.push(e);
+	}
+
+	function dispatchListeners( event : hxd.Event ) {
+		var ox = event.relX, oy = event.relY;
+		event.propagate = true;
+		event.cancel = false;
+		for( s in scenes ) {
+			s.dispatchListeners(event);
+			event.relX = ox;
+			event.relY = oy;
+			if( !event.propagate ) break;
+		}
+	}
+
+}