diff --git a/haxe/ui/animation/transition/Transition.hx b/haxe/ui/animation/transition/Transition.hx new file mode 100644 index 000000000..f11a248aa --- /dev/null +++ b/haxe/ui/animation/transition/Transition.hx @@ -0,0 +1,80 @@ +package haxe.ui.animation.transition; + +import haxe.ui.core.Component; +class Transition { + public var inAnimations:Array = []; + public var outAnimations:Array = []; + + public var id:String; + + public var componentMap:Map = new Map(); + + public function new() { + + } + + public function addInAnimation(animation:Animation):Void { + inAnimations.push(animation); + } + + public function addOutAnimation(animation:Animation):Void { + outAnimations.push(animation); + } + + public function setInComponent(id:String, component:Component) { + componentMap.set(id, component); + } + + public function getComponent(id:String):Component { + return componentMap.get(id); + } + + public function start(onComplete:Void->Void = null):Void { + var animationCallback:Void->Void = null; + + if (onComplete != null) { + var total = inAnimations.length + outAnimations.length; + var current = 0; + animationCallback = function() { + if (++current >= total) { + onComplete(); + } + }; + } + + for (a in inAnimations) { + a.start(animationCallback); + } + + for (a in outAnimations) { + a.start(animationCallback); + } + } + + public function stop():Void { + for (a in inAnimations) { + a.stop(); + } + + for (a in outAnimations) { + a.stop(); + } + } + + public function clone():Transition { + var c:Transition = new Transition(); + c.id = this.id; + + for (a in inAnimations) { + var ca = a.clone(); + c.inAnimations.push(ca); + } + + for (a in outAnimations) { + var ca = a.clone(); + c.outAnimations.push(ca); + } + + return c; + } +} diff --git a/haxe/ui/animation/transition/TransitionManager.hx b/haxe/ui/animation/transition/TransitionManager.hx new file mode 100644 index 000000000..b4e1f4ddd --- /dev/null +++ b/haxe/ui/animation/transition/TransitionManager.hx @@ -0,0 +1,87 @@ +package haxe.ui.animation.transition; + +import haxe.ui.core.Component; + +class TransitionManager { + private static var _instance:TransitionManager; + public static var instance(get, never):TransitionManager; + private static function get_instance():TransitionManager { + if (_instance == null) { + _instance = new TransitionManager(); + } + return _instance; + } + + //*********************************************************************************************************** + // Instance + //*********************************************************************************************************** + private var _transitions:Map = new Map(); + public function new() { + + } + + public function registerTransition(id:String, transition:Transition) { + _transitions.set(id, transition); + } + + public function run(id:String, inComponents:Map = null, inVars:Map = null, + outComponents:Map = null, outVars:Map = null, complete:Void->Void = null):Transition { + var t:Transition = initTransition(id, inComponents, inVars, outComponents, outVars); + if (t != null) { + t.start(function() { + if (complete != null) { + complete(); + } + }); + } + return t; + } + + private function initTransition(id:String, inComponents:Map = null, inVars:Map = null, + outComponents:Map = null, outVars:Map = null):Transition { + var t:Transition = get(id); + if (t != null) { + if (inComponents != null) { + for (k in inComponents.keys() ) { + for (a in t.inAnimations) { + a.setComponent(k, inComponents.get(k)); + } + } + } + + if (outComponents != null) { + for (k in outComponents.keys() ) { + for (a in t.outAnimations) { + a.setComponent(k, outComponents.get(k)); + } + } + } + + if (inVars != null) { + for (k in inVars.keys()) { + for (a in t.inAnimations) { + a.setVar(k, inVars.get(k)); + } + } + } + + if (outVars != null) { + for (k in outVars.keys()) { + for (a in t.outAnimations) { + a.setVar(k, outVars.get(k)); + } + } + } + } + return t; + } + + public function get(id:String):Transition { + var t:Transition = _transitions.get(id); + if (t == null) { + return null; + } + return t.clone(); + } + +} \ No newline at end of file diff --git a/haxe/ui/components/complex/ImageGallery.hx b/haxe/ui/components/complex/ImageGallery.hx new file mode 100644 index 000000000..ad6d7ee22 --- /dev/null +++ b/haxe/ui/components/complex/ImageGallery.hx @@ -0,0 +1,234 @@ +package haxe.ui.components.complex; + +import haxe.ui.constants.TransitionMode; +import haxe.ui.containers.Stack; +import haxe.ui.core.Component; +import haxe.ui.core.MouseEvent; +import haxe.ui.core.Screen; +import haxe.ui.layouts.AbsoluteLayout; + +class ImageGallery extends Stack { + public function new() { + super(); + + layout = new AbsoluteLayout(); + + transitionMode = TransitionMode.HORIZONTAL_SLIDE; + } + + //****************************************************************************************** + // Overrides + //****************************************************************************************** + + private override function set_transitionMode(value:TransitionMode):TransitionMode { + if (_transitionMode == value) { + return value; + } + + switch (value) { + case TransitionMode.HORIZONTAL_SLIDE_FROM_LEFT, TransitionMode.HORIZONTAL_SLIDE_FROM_RIGHT: + value = TransitionMode.HORIZONTAL_SLIDE; + + case TransitionMode.VERTICAL_SLIDE_FROM_TOP, TransitionMode.VERTICAL_SLIDE_FROM_BOTTOM: + value = TransitionMode.VERTICAL_SLIDE; + + case TransitionMode.NONE, TransitionMode.HORIZONTAL_SLIDE, TransitionMode.VERTICAL_SLIDE: //nothing to change + + case _: //Not supported + return _transitionMode; + } + + super.transitionMode = value; + + return value; + } + + public override function addComponent(child:Component):Component { + if (Std.is(child, Image)) { + return super.addComponent(child); + } else { + throw "You can only add Image components in the ImageGallery"; + } + } + + //TODO --> https://github.com/haxeui/haxeui-core/pull/93 + /*public override function addComponentAt(child:Component, index:Int):Component { + if (Std.is(child, Image)) { + return super.addComponentAt(child, index); + } else { + throw "You can only add Image components in the ImageGallery"; + } + }*/ + + private override function createChildren() { + super.createChildren(); + + registerEvent(MouseEvent.MOUSE_DOWN, _onMouseDown); + } + + //****************************************************************************************** + // Public API + //****************************************************************************************** + + /** + Minimum percent size to change from the current image to another. + **/ + @:clonable public var percentToChange:Int = 20; + + //****************************************************************************************** + // Events + //****************************************************************************************** + + private var _currentPosX:Float; + private var _currentPosY:Float; + private var _dragging:Bool = false; + + private function _onMouseDown(e:MouseEvent):Void { + if (_dragging == true) { + return; + } + + _dragging = true; + + Screen.instance.registerEvent(MouseEvent.MOUSE_MOVE, _onMouseMove); + Screen.instance.registerEvent(MouseEvent.MOUSE_UP, _onMouseUp); + + _currentPosX = e.screenX; + _currentPosY = e.screenY; + + if (_currentTransition != null) { + _currentTransition.stop(); + _currentTransition = null; + } + } + + private function _onMouseMove(e:MouseEvent):Void { + var newX:Float = e.screenX; + var newY:Float = e.screenY; + + _applyOffsetPosition(e.screenX, e.screenY); + + _currentPosX = newX; + _currentPosY = newY; + } + + private function _onMouseUp(e:MouseEvent):Void { + if (_dragging == false) { + return; + } + + _dragging = false; + + Screen.instance.unregisterEvent(MouseEvent.MOUSE_MOVE, _onMouseMove); + Screen.instance.unregisterEvent(MouseEvent.MOUSE_UP, _onMouseUp); + + var currentComponent:Component = getComponentAt(_selectedIndex); + + switch (_transitionMode) { + case TransitionMode.VERTICAL_SLIDE: + if (currentComponent.top > height * percentToChange / 100) { + selectedIndex -= 1; + } else if (currentComponent.top < -height * percentToChange / 100) { + selectedIndex += 1; + } else if (currentComponent.top != layout.paddingTop) { + var currentIndex:Int = _selectedIndex; + _selectedIndex = currentComponent.top > layout.paddingTop ? _selectedIndex - 1 : _selectedIndex + 1; + selectedIndex = currentIndex; + } + + case _: + + if (currentComponent.left > width * percentToChange / 100) { + selectedIndex -= 1; + } else if (currentComponent.left < -width * percentToChange / 100) { + selectedIndex += 1; + } else if (currentComponent.left != layout.paddingLeft) { + var currentIndex:Int = _selectedIndex; + _selectedIndex = currentComponent.left > layout.paddingLeft ? _selectedIndex - 1 : _selectedIndex + 1; + selectedIndex = currentIndex; + } + } + } + + //****************************************************************************************** + // Internals + //****************************************************************************************** + + private function _applyOffsetPosition(screenX:Float, screenY:Float):Void { + var childrenCount:Int = childComponents.length; + var currentComponent = getComponentAt(_selectedIndex); + + switch (_transitionMode) { + case TransitionMode.VERTICAL_SLIDE: + var offset:Float = screenY - _currentPosY; + var newTop:Float = currentComponent.top + offset; + if (((newTop >= layout.paddingTop) && (offset > 0 && selectedIndex == 0)) + || ((newTop <= layout.paddingTop) && (offset < 0 && selectedIndex == childrenCount - 1))) { + newTop = layout.paddingTop; + } + + currentComponent.top = newTop; + + if (selectedIndex > 0) { + var previousComponent = getComponentAt(selectedIndex - 1); + var top:Float = currentComponent.top - previousComponent.height - layout.paddingBottom; + var hidden:Bool = (top + previousComponent.height <= 0 || top + previousComponent.height >= height); + if (previousComponent.hidden != hidden) { + previousComponent.includeInLayout = !hidden; + previousComponent.hidden = hidden; + } + + previousComponent.left = layout.paddingTop; + previousComponent.top = top; + } + + if (selectedIndex < childrenCount - 1) { + var nextComponent = getComponentAt(selectedIndex + 1); + var top:Float = currentComponent.top + currentComponent.height + layout.paddingTop; + var hidden:Bool = (top <= 0 || top >= width); + if (nextComponent.hidden != hidden) { + nextComponent.includeInLayout = !hidden; + nextComponent.hidden = hidden; + } + + nextComponent.left = layout.paddingTop; + nextComponent.top = top; + } + case _: + var offset:Float = screenX - _currentPosX; + var newLeft:Float = currentComponent.left + offset; + if (((newLeft >= layout.paddingLeft) && (offset > 0 && selectedIndex == 0)) + || ((newLeft <= layout.paddingLeft) && (offset < 0 && selectedIndex == childrenCount - 1))) { + newLeft = layout.paddingLeft; + } + + currentComponent.left = newLeft; + + if (selectedIndex > 0) { + var previousComponent = getComponentAt(selectedIndex - 1); + var left:Float = currentComponent.left - previousComponent.width - layout.paddingRight; + var hidden:Bool = (left + previousComponent.width <= 0 || left + previousComponent.width >= width); + if (previousComponent.hidden != hidden) { + previousComponent.includeInLayout = !hidden; + previousComponent.hidden = hidden; + } + + previousComponent.left = left; + previousComponent.top = layout.paddingTop; + } + + if (selectedIndex < childrenCount - 1) { + var nextComponent = getComponentAt(selectedIndex + 1); + var left:Float = currentComponent.left + currentComponent.width + layout.paddingLeft; + var hidden:Bool = (left <= 0 || left >= width); + if (nextComponent.hidden != hidden) { + nextComponent.includeInLayout = !hidden; + nextComponent.hidden = hidden; + } + + nextComponent.left = left; + nextComponent.top = layout.paddingTop; + } + } + } +} \ No newline at end of file diff --git a/haxe/ui/constants/TransitionMode.hx b/haxe/ui/constants/TransitionMode.hx new file mode 100644 index 000000000..699f9feae --- /dev/null +++ b/haxe/ui/constants/TransitionMode.hx @@ -0,0 +1,45 @@ +package haxe.ui.constants; + +@:enum +abstract TransitionMode(String) from String to String { + + /** + Without transition. + **/ + var NONE = "none"; + + /** + From left or from right. It depends of new selectedIndex is greater or lower than the old selectedIndex. + **/ + var HORIZONTAL_SLIDE = "horizontal-slide"; + + /** + Always from the left. + **/ + var HORIZONTAL_SLIDE_FROM_LEFT = "horizontal-slide-from-left"; + + /** + Always from the right. + **/ + var HORIZONTAL_SLIDE_FROM_RIGHT = "horizontal-slide-from-right"; + + /** + From top or from bottom. It depends of new selectedIndex is greater or lower than the old selectedIndex. + **/ + var VERTICAL_SLIDE = "vertical-slide"; + + /** + Always from the top. + **/ + var VERTICAL_SLIDE_FROM_TOP = "vertical-slide-from-top"; + + /** + Always from the bottom. + **/ + var VERTICAL_SLIDE_FROM_BOTTOM = "vertical-slide-from-bottom"; + + /** + Opacity transition. + **/ + var FADE = "fade"; +} diff --git a/haxe/ui/containers/Stack.hx b/haxe/ui/containers/Stack.hx index 4e003a18f..41e47838e 100644 --- a/haxe/ui/containers/Stack.hx +++ b/haxe/ui/containers/Stack.hx @@ -1,11 +1,413 @@ package haxe.ui.containers; +import haxe.ui.animation.transition.Transition; +import haxe.ui.animation.transition.TransitionManager; +import haxe.ui.constants.TransitionMode; +import haxe.ui.core.Behaviour; +import haxe.ui.core.Component; +import haxe.ui.core.UIEvent; +import haxe.ui.util.Rectangle; +import haxe.ui.util.Variant; +import haxe.ui.validation.InvalidationFlags; +import haxe.ui.validation.ValidationManager; + /** A `Box` component where only one child is visible at a time **/ @:dox(icon = "/icons/ui-layered-pane.png") class Stack extends Box { + private static inline var NO_SELECTION:Int = -1; + public function new() { super(); } + + private override function createDefaults() { + super.createDefaults(); + defaultBehaviours([ + "transitionMode" => new StackDefaultTransitionModeBehaviour(this), + "selectedIndex" => new StackDefaultSelectedIndexBehaviour(this) + ]); + } + + //****************************************************************************************** + // Overrides + //****************************************************************************************** + public override function addComponent(child:Component):Component { + super.addComponent(child); + if (_selectedIndex == NO_SELECTION && childComponents.length == 1) { + selectedIndex = 0; + } + child.hidden = (childComponents.length - 1 != _selectedIndex); + child.includeInLayout = child.hidden == false; + return child; + } + + public override function addComponentAt(child:Component, index:Int):Component { + super.addComponentAt(child, index); + if (_selectedIndex == NO_SELECTION && childComponents.length == 1) { + selectedIndex = 0; + } + child.hidden = (index != _selectedIndex); + child.includeInLayout = child.hidden == false; + return child; + } + + public override function removeComponent(child:Component, dispose:Bool = true, invalidate:Bool = true):Component { + var index:Int = getComponentIndex(child); + if (index == _selectedIndex) { + selectedIndex = NO_SELECTION; + } + + return super.removeComponent(child); + } + + public override function removeComponentAt(index:Int, dispose:Bool = true, invalidate:Bool = true):Component { + if (index == _selectedIndex) { + selectedIndex = NO_SELECTION; + } + + return super.removeComponentAt(index, dispose, invalidate); + } + + public override function removeAllComponents(dispose:Bool = true) { + selectedIndex = NO_SELECTION; + + super.removeAllComponents(dispose); + } + + private override function onResized() { + updateClip(); + } + + //*********************************************************************************************************** + // Public API + //*********************************************************************************************************** + + private var _selectedIndex:Int = NO_SELECTION; + @:clonable public var selectedIndex(get, set):Int; + private function get_selectedIndex():Int { + return behaviourGet("selectedIndex"); + } + private function set_selectedIndex(value:Int):Int { + behaviourSet("selectedIndex", value); + return value; + } + + public var selectedItem(get, set):Component; + private function get_selectedItem():Component { + if (_selectedIndex == NO_SELECTION) { + return null; + } + + return getComponentAt(_selectedIndex); + } + private function set_selectedItem(value:Component):Component { + selectedIndex = getComponentIndex(value); + return value; + } + + private var _transitionMode:TransitionMode = TransitionMode.NONE; + @:clonable public var transitionMode(get, set):TransitionMode; + private function get_transitionMode():TransitionMode { + return behaviourGet("transitionMode"); + } + private function set_transitionMode(value:TransitionMode):TransitionMode { + behaviourSet("transitionMode", value); + return value; + } + + private var _currentSelection:Component; + + private var _history : List = new List(); + + /** + Go back to the last selected index + **/ + public function back() { + var last = _history.pop(); + if (last == null) { + return; + } + + selectedIndex = last; + } + + public function canGoBack():Bool { + return _history.length > 0; + } + + //*********************************************************************************************************** + // Validation + //*********************************************************************************************************** + + /** + Invalidate the index of this component + **/ + @:dox(group = "Invalidation related properties and methods") + public inline function invalidateIndex() { + invalidate(InvalidationFlags.INDEX); + } + + private override function validateInternal() { + var dataInvalid = isInvalid(InvalidationFlags.DATA); + var indexInvalid = isInvalid(InvalidationFlags.INDEX); + var styleInvalid = isInvalid(InvalidationFlags.STYLE); + var positionInvalid = isInvalid(InvalidationFlags.POSITION); + var displayInvalid = isInvalid(InvalidationFlags.DISPLAY); + var layoutInvalid = isInvalid(InvalidationFlags.LAYOUT) && _layoutLocked == false; + + if (dataInvalid) { + validateData(); + } + + if (styleInvalid) { + validateStyle(); + } + + if (positionInvalid) { + validatePosition(); + } + + if (dataInvalid || indexInvalid) { + var newSelectedItem:Component = selectedItem; + if (newSelectedItem != null) { + newSelectedItem.hidden = false; + newSelectedItem.includeInLayout = true; + } + } + + if (layoutInvalid || indexInvalid) { + displayInvalid = validateLayout() || displayInvalid; + } + + if (dataInvalid || indexInvalid) { + validateIndex(); + } + + if (displayInvalid || styleInvalid) { + ValidationManager.instance.addDisplay(this); //Update the display from all objects at the same time. Avoids UI flashes. + } + } + + private function validateIndex() { + var newSelectedItem:Component = selectedItem; + if(_currentSelection != newSelectedItem) + { + var oldIndex:Int = getComponentIndex(_currentSelection); + animateTo(oldIndex, _selectedIndex); + + _currentSelection = newSelectedItem; + if(_selectedIndex != NO_SELECTION) { + _history.push(_selectedIndex); + } + + dispatch(new UIEvent(UIEvent.CHANGE)); + } + } + + //*********************************************************************************************************** + // Internals + //*********************************************************************************************************** + + private var _currentTransition:Transition; + private function animateTo(fromIndex:Int, toIndex:Int) { + var inComponent:Component = (toIndex != NO_SELECTION) ? getComponentAt(toIndex) : null; + var outComponent:Component = (fromIndex != NO_SELECTION) ? getComponentAt(fromIndex) : null; + + var transitionId:String = null; + var mode:TransitionMode = transitionMode; + if (inComponent == null || outComponent == null || animatable == false) { + mode = TransitionMode.NONE; + } else { + switch (mode) { + case TransitionMode.HORIZONTAL_SLIDE, TransitionMode.VERTICAL_SLIDE, + TransitionMode.HORIZONTAL_SLIDE_FROM_LEFT, TransitionMode.HORIZONTAL_SLIDE_FROM_RIGHT, + TransitionMode.VERTICAL_SLIDE_FROM_TOP, TransitionMode.VERTICAL_SLIDE_FROM_BOTTOM: + transitionId = getClassProperty("transition.slide"); + + case TransitionMode.FADE: + transitionId = getClassProperty("transition.fade"); + + case _: + + } + + if (transitionId == null) { + mode = TransitionMode.NONE; + } + } + + if (inComponent != null) { + inComponent.includeInLayout = false; //Avoid that the layout can override the animation values + inComponent.hidden = false; + } + + if (_currentTransition != null) { + _currentTransition.stop(); + _currentTransition = null; + } + + if (mode != TransitionMode.NONE) { + var inVars:Map = null; + var outVars:Map = null; + + switch (mode) { + case TransitionMode.HORIZONTAL_SLIDE: + inVars = [ + "startLeft" => ((fromIndex < toIndex) ? + outComponent.left + outComponent.width + layout.paddingLeft + : outComponent.left - layout.paddingRight - inComponent.width), + "startTop" => layout.paddingTop, + "endLeft" => layout.paddingLeft + ]; + + outVars = [ + "startLeft" => outComponent.left, + "endLeft" => ((fromIndex < toIndex) ? + -width + layout.paddingLeft + layout.paddingRight + : width) + ]; + + case TransitionMode.HORIZONTAL_SLIDE_FROM_LEFT: + inVars = [ + "startLeft" => outComponent.left - layout.paddingRight - inComponent.width, + "startTop" => layout.paddingTop, + "endLeft" => layout.paddingLeft + ]; + + outVars = [ + "startLeft" => outComponent.left, + "endLeft" => width + ]; + + case TransitionMode.HORIZONTAL_SLIDE_FROM_RIGHT: + inVars = [ + "startLeft" => outComponent.left + outComponent.width + layout.paddingLeft, + "startTop" => layout.paddingTop, + "endLeft" => layout.paddingLeft + ]; + + outVars = [ + "startLeft" => outComponent.left, + "endLeft" => -width + layout.paddingLeft + layout.paddingRight + ]; + + case TransitionMode.VERTICAL_SLIDE: + inVars = [ + "startLeft" => layout.paddingLeft, + "startTop" => ((fromIndex < toIndex) ? + outComponent.top + outComponent.height + layout.paddingTop + : outComponent.top - layout.paddingBottom - inComponent.height), + "endTop" => layout.paddingTop + ]; + + outVars = [ + "startTop" => outComponent.top, + "endTop" => ((fromIndex < toIndex) ? + -height + layout.paddingTop + layout.paddingBottom + : height) + ]; + + case TransitionMode.VERTICAL_SLIDE_FROM_TOP: + inVars = [ + "startLeft" => layout.paddingLeft, + "startTop" => outComponent.top - layout.paddingBottom - inComponent.height, + "endTop" => layout.paddingTop + ]; + + outVars = [ + "startTop" => outComponent.top, + "endTop" => height + ]; + + case TransitionMode.VERTICAL_SLIDE_FROM_BOTTOM: + inVars = [ + "startLeft" => layout.paddingLeft, + "startTop" => outComponent.top + outComponent.height + layout.paddingTop, + "endTop" => layout.paddingTop + ]; + + outVars = [ + "startTop" => outComponent.top, + "endTop" => -height + layout.paddingTop + layout.paddingBottom + ]; + + case TransitionMode.FADE: + + case _: + //TODO - support for custom transition by user + } + + _currentTransition = TransitionManager.instance.run(transitionId, + ["target" => inComponent], inVars, + ["target" => outComponent], outVars, + function() { + _currentTransition = null; + + if (inComponent != null) { + inComponent.includeInLayout = true; + } + + if (outComponent != null) { + outComponent.includeInLayout = false; + outComponent.hidden = true; + } + }); + + } else { + if (inComponent != null) { + inComponent.left = layout.paddingLeft; + inComponent.top = layout.paddingTop; + inComponent.includeInLayout = true; + } + + if (outComponent != null) { + outComponent.includeInLayout = false; + outComponent.hidden = true; + } + } + } + + private function updateClip() { + if(componentClipRect == null || componentClipRect.width != componentWidth || componentClipRect.height != componentHeight) { + componentClipRect = new Rectangle(0, 0, componentWidth, componentHeight); + } + } +} + +//*********************************************************************************************************** +// Default behaviours +//*********************************************************************************************************** +@:dox(hide) +@:access(haxe.ui.containers.Stack) +class StackDefaultTransitionModeBehaviour extends Behaviour { + public override function get():Variant { + var stack:Stack = cast(_component, Stack); + return stack._transitionMode; + } + + public override function set(value:Variant) { + var stack:Stack = cast(_component, Stack); + if (stack._transitionMode != value) { + stack._transitionMode = value; + } + } +} + +@:dox(hide) +@:access(haxe.ui.containers.Stack) +class StackDefaultSelectedIndexBehaviour extends Behaviour { + public override function get():Variant { + var stack:Stack = cast(_component, Stack); + return stack._selectedIndex; + } + + public override function set(value:Variant) { + var stack:Stack = cast(_component, Stack); + if (stack._selectedIndex != value) { + stack._selectedIndex = value; + stack.invalidateIndex(); + } + } } \ No newline at end of file diff --git a/haxe/ui/macros/ModuleMacros.hx b/haxe/ui/macros/ModuleMacros.hx index 6d428ef01..9389d170f 100644 --- a/haxe/ui/macros/ModuleMacros.hx +++ b/haxe/ui/macros/ModuleMacros.hx @@ -105,23 +105,26 @@ class ModuleMacros { // load animations for (a in m.animations) { - code += 'var a:haxe.ui.animation.Animation = new haxe.ui.animation.Animation();\n'; - code += 'a.id = "${a.id}";\n'; - code += 'a.easing = haxe.ui.animation.Animation.easingFromString("${a.ease}");\n'; - for (kf in a.keyFrames) { - code += 'var kf:haxe.ui.animation.AnimationKeyFrame = a.addKeyFrame(${kf.time});\n'; - for (r in kf.componentRefs) { - code += 'var ref:haxe.ui.animation.AnimationComponentRef = kf.addComponentRef("${r.id}");\n'; - for (p in r.properties.keys()) { - code += 'ref.addProperty("${p}", ${r.properties.get(p)});\n'; - } - for (v in r.vars.keys()) { - code += 'ref.addVar("${v}", "${r.vars.get(v)}");\n'; - } - } + code += getAnimationCode(a); + } + + //load transitions + for (t in m.transitions) { + code += 'var t:haxe.ui.animation.transition.Transition = new haxe.ui.animation.transition.Transition();\n'; + code += 't.id = "${t.id}";\n'; + + for (inAnim in t.inAnimations) { + code += getAnimationCode(inAnim); + code += 't.addInAnimation(a);\n'; + } + + for (outAnim in t.outAnimations) { + + code += getAnimationCode(outAnim); + code += 't.addOutAnimation(a);\n'; } - code += 'haxe.ui.animation.AnimationManager.instance.registerAnimation(a.id, a);\n'; + code += 'haxe.ui.animation.transition.TransitionManager.instance.registerTransition(t.id, t);\n'; } for (p in m.preload) { @@ -143,6 +146,28 @@ class ModuleMacros { return Context.parseInlineString(code, Context.currentPos()); } + private static function getAnimationCode(animationEntry:ModuleAnimationEntry):String { + var code:String = 'var a:haxe.ui.animation.Animation = new haxe.ui.animation.Animation();\n'; + code += 'a.id = "${animationEntry.id}";\n'; + code += 'a.easing = haxe.ui.animation.Animation.easingFromString("${animationEntry.ease}");\n'; + for (kf in animationEntry.keyFrames) { + code += 'var kf:haxe.ui.animation.AnimationKeyFrame = a.addKeyFrame(${kf.time});\n'; + for (r in kf.componentRefs) { + code += 'var ref:haxe.ui.animation.AnimationComponentRef = kf.addComponentRef("${r.id}");\n'; + for (p in r.properties.keys()) { + code += 'ref.addProperty("${p}", ${r.properties.get(p)});\n'; + } + for (v in r.vars.keys()) { + code += 'ref.addVar("${v}", "${r.vars.get(v)}");\n'; + } + } + } + + code += 'haxe.ui.animation.AnimationManager.instance.registerAnimation(a.id, a);\n'; + + return code; + } + #if macro private static var _classMapPopulated:Bool = false; public static function populateClassMap() { diff --git a/haxe/ui/module.xml b/haxe/ui/module.xml index fa88d6055..a2924f7e4 100644 --- a/haxe/ui/module.xml +++ b/haxe/ui/module.xml @@ -9,6 +9,7 @@ + @@ -17,6 +18,7 @@ + @@ -67,6 +69,12 @@ + + + + + + @@ -90,6 +98,16 @@ + + + + + + + + + + @@ -122,4 +140,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/haxe/ui/parsers/modules/Module.hx b/haxe/ui/parsers/modules/Module.hx index 569be82d7..c3a0d177c 100644 --- a/haxe/ui/parsers/modules/Module.hx +++ b/haxe/ui/parsers/modules/Module.hx @@ -10,6 +10,7 @@ class Module { public var properties(default, default):Array; public var animations(default, default):Array; public var preload(default, default):Array; + public var transitions(default, default):Array; public function new() { resourceEntries = []; @@ -20,6 +21,7 @@ class Module { properties = []; animations = []; preload = []; + transitions = []; } public function validate() { @@ -66,7 +68,7 @@ class ModuleThemeEntry { class ModuleThemeStyleEntry { public var resource:String; - + public function new() { } } @@ -122,7 +124,18 @@ class ModuleAnimationComponentRefEntry { class ModulePreloadEntry { public var type(default, default):String; public var id(default, default):String; - + public function new() { } } + +class ModuleTransitionEntry { + public var id(default, default):String; + public var inAnimations(default, default):Array; + public var outAnimations(default, default):Array; + + public function new() { + inAnimations = []; + outAnimations = []; + } +} \ No newline at end of file diff --git a/haxe/ui/parsers/modules/XMLParser.hx b/haxe/ui/parsers/modules/XMLParser.hx index 08678a195..a6aa02418 100644 --- a/haxe/ui/parsers/modules/XMLParser.hx +++ b/haxe/ui/parsers/modules/XMLParser.hx @@ -1,5 +1,6 @@ package haxe.ui.parsers.modules; import haxe.ui.parsers.modules.Module.ModuleThemeStyleEntry; +import haxe.ui.parsers.modules.Module.ModuleAnimationEntry; class XMLParser extends ModuleParser { public function new() { @@ -97,55 +98,74 @@ class XMLParser extends ModuleParser { module.properties.push(property); } } else if (nodeName == "animations" && checkCondition(el, defines) == true) { - for (animationNode in el.elementsNamed("animation")) { - if (checkCondition(animationNode, defines) == false) { + parseAnimations(el, defines, module.animations); + } else if (nodeName == "transitions" && checkCondition(el, defines) == true) { + for (transitionNode in el.elementsNamed("transition")) { + if (checkCondition(transitionNode, defines) == false) { continue; } - var animation:Module.ModuleAnimationEntry = new Module.ModuleAnimationEntry(); - animation.id = animationNode.get("id"); - animation.ease = animationNode.get("ease"); - - for (keyFrameNode in animationNode.elementsNamed("keyframe")) { - var keyFrame:Module.ModuleAnimationKeyFrameEntry = new Module.ModuleAnimationKeyFrameEntry(); - if (keyFrameNode.get("time") != null) { - keyFrame.time = Std.parseInt(keyFrameNode.get("time")); - } + var transition:Module.ModuleTransitionEntry = new Module.ModuleTransitionEntry(); + transition.id = transitionNode.get("id"); - for (componentRefNode in keyFrameNode.elements()) { - var componentRef:Module.ModuleAnimationComponentRefEntry = new Module.ModuleAnimationComponentRefEntry(); - componentRef.id = componentRefNode.nodeName; - for (attrName in componentRefNode.attributes()) { - var attrValue = componentRefNode.get(attrName); - if (StringTools.startsWith(attrValue, "{") && StringTools.endsWith(attrValue, "}")) { - attrValue = attrValue.substring(1, attrValue.length - 1); - componentRef.vars.set(attrName, attrValue); - } else { - componentRef.properties.set(attrName, Std.parseFloat(attrValue)); - } - } - - keyFrame.componentRefs.set(componentRef.id, componentRef); - } + for (inAnimationNode in transitionNode.elementsNamed("inAnimations")) { + parseAnimations(inAnimationNode, defines, transition.inAnimations); + } - animation.keyFrames.push(keyFrame); + for (outAnimationNode in transitionNode.elementsNamed("outAnimations")) { + parseAnimations(outAnimationNode, defines, transition.outAnimations); } - module.animations.push(animation); + module.transitions.push(transition); } - } else if (nodeName == "preload" && checkCondition(el, defines) == true) { - for (propertyNode in el.elements()) { - if (checkCondition(propertyNode, defines) == false) { - continue; + } + } + + return module; + } + + private function parseAnimations(node:Xml, defines:Map, result:Array=null):Array + { + if (result == null) { + result = []; + } + + for (animationNode in node.elementsNamed("animation")) { + if (checkCondition(animationNode, defines) == false) { + continue; + } + var animation:Module.ModuleAnimationEntry = new Module.ModuleAnimationEntry(); + animation.id = animationNode.get("id"); + animation.ease = animationNode.get("ease"); + + for (keyFrameNode in animationNode.elementsNamed("keyframe")) { + var keyFrame:Module.ModuleAnimationKeyFrameEntry = new Module.ModuleAnimationKeyFrameEntry(); + if (keyFrameNode.get("time") != null) { + keyFrame.time = Std.parseInt(keyFrameNode.get("time")); + } + + for (componentRefNode in keyFrameNode.elements()) { + var componentRef:Module.ModuleAnimationComponentRefEntry = new Module.ModuleAnimationComponentRefEntry(); + componentRef.id = componentRefNode.nodeName; + for (attrName in componentRefNode.attributes()) { + var attrValue = componentRefNode.get(attrName); + if (StringTools.startsWith(attrValue, "{") && StringTools.endsWith(attrValue, "}")) { + attrValue = attrValue.substring(1, attrValue.length - 1); + componentRef.vars.set(attrName, attrValue); + } else { + componentRef.properties.set(attrName, Std.parseFloat(attrValue)); + } } - var entry:Module.ModulePreloadEntry = new Module.ModulePreloadEntry(); - entry.type = propertyNode.nodeName; - entry.id = propertyNode.get("id"); - module.preload.push(entry); + + keyFrame.componentRefs.set(componentRef.id, componentRef); } + + animation.keyFrames.push(keyFrame); } + + result.push(animation); } - return module; + return result; } private function checkCondition(node:Xml, defines:Map):Bool { @@ -153,7 +173,7 @@ class XMLParser extends ModuleParser { var condition = "haxeui_" + node.get("if"); return defines.exists(condition); } - + return true; } } \ No newline at end of file