From 36a0bd0b6a643dacc4709aab453fa4d9bb992d8b Mon Sep 17 00:00:00 2001 From: Pablo Martin Date: Sun, 29 Jan 2017 19:27:30 +0100 Subject: [PATCH 01/11] Transition system + Stack container --- haxe/ui/animation/transition/Transition.hx | 80 +++++ .../animation/transition/TransitionManager.hx | 87 ++++++ haxe/ui/constants/TransitionMode.hx | 45 +++ haxe/ui/containers/Stack.hx | 277 ++++++++++++++++++ haxe/ui/macros/ModuleMacros.hx | 55 +++- haxe/ui/module.xml | 62 ++++ haxe/ui/parsers/modules/Module.hx | 13 + haxe/ui/parsers/modules/XMLParser.hx | 81 +++-- 8 files changed, 658 insertions(+), 42 deletions(-) create mode 100644 haxe/ui/animation/transition/Transition.hx create mode 100644 haxe/ui/animation/transition/TransitionManager.hx create mode 100644 haxe/ui/constants/TransitionMode.hx diff --git a/haxe/ui/animation/transition/Transition.hx b/haxe/ui/animation/transition/Transition.hx new file mode 100644 index 000000000..bee9557a1 --- /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 = onComplete == null ? null : 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/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 933838ee2..8ac063035 100644 --- a/haxe/ui/containers/Stack.hx +++ b/haxe/ui/containers/Stack.hx @@ -1,6 +1,14 @@ 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.IClonable; +import haxe.ui.core.UIEvent; +import haxe.ui.util.Rectangle; +import haxe.ui.util.Variant; /** A `Box` component where only one child is visible at a time @@ -10,4 +18,273 @@ class Stack extends Box implements IClonable { 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); + child.hidden = (childComponents.length - 1 != _selectedIndex); + child.includeInLayout = child.hidden == false; + return child; + } + + //TODO --> https://github.com/haxeui/haxeui-core/pull/93 + /*public override function addComponentAt(child:Component, index:Int):Component { + super.addComponentAt(child, index); + child.hidden = (index != _selectedIndex); + return child; + }*/ + + public override function removeComponent(child:Component, dispose:Bool = true, invalidate:Bool = true):Component { + var index:Int = getComponentIndex(child); + if (index == _selectedIndex) + selectedIndex = -1; + + return super.removeComponent(child); + } + + public override function removeAllComponents(dispose:Bool = true) { + selectedIndex = -1; + + super.removeAllComponents(dispose); + } + + private override function onResized() { + updateClip(); + } + + //*********************************************************************************************************** + // Public API + //*********************************************************************************************************** + + private var _selectedIndex:Int = -1; + @:clonable public var selectedIndex(get, set):Int; + private function get_selectedIndex():Int { + return _selectedIndex; + } + private function set_selectedIndex(value:Int):Int { + if (_selectedIndex == value) { + return value; + } + + if(_selectedIndex != -1) { + _history.push(_selectedIndex); + } + + behaviourSet("selectedIndex", value); + _selectedIndex = value; + + dispatch(new UIEvent(UIEvent.CHANGE)); + + return value; + } + + private var _transitionMode:TransitionMode = TransitionMode.NONE; + @:clonable public var transitionMode(get, set):TransitionMode; + private function get_transitionMode():TransitionMode { + return _transitionMode; + } + private function set_transitionMode(value:TransitionMode):TransitionMode { + if (_transitionMode == value) { + return value; + } + + _transitionMode = value; + behaviourSet("transitionMode", value); + + return value; + } + + 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; + } + + //*********************************************************************************************************** + // Internals + //*********************************************************************************************************** + + private var _currentTransition:Transition; + private function animateTo(index:Int) { + var inComponent:Component = (index != -1) ? getComponentAt(index) : null; + var outComponent:Component = (_selectedIndex != -1) ? getComponentAt(_selectedIndex) : 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 = true; + 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" => ((_selectedIndex < index) ? width : -width), + "endLeft" => paddingLeft + ]; + + outVars = [ + "startLeft" => outComponent.left, + "endLeft" => ((_selectedIndex < index) ? -width : width) + ]; + + case TransitionMode.HORIZONTAL_SLIDE_FROM_LEFT: + inVars = [ + "startLeft" => -width, + "endLeft" => paddingLeft + ]; + + outVars = [ + "startLeft" => outComponent.left, + "endLeft" => width + ]; + + case TransitionMode.HORIZONTAL_SLIDE_FROM_RIGHT: + inVars = [ + "startLeft" => width, + "endLeft" => paddingLeft + ]; + + outVars = [ + "startLeft" => outComponent.left, + "endLeft" => -width + ]; + + case TransitionMode.VERTICAL_SLIDE: + inVars = [ + "startTop" => ((_selectedIndex < index) ? height : -height), + "endTop" => paddingTop + ]; + + outVars = [ + "startTop" => outComponent.top, + "endTop" => ((_selectedIndex < index) ? -height : height) + ]; + + case TransitionMode.VERTICAL_SLIDE_FROM_TOP: + inVars = [ + "startTop" => -height, + "endTop" => paddingTop + ]; + + outVars = [ + "startTop" => outComponent.top, + "endTop" => height + ]; + + case TransitionMode.VERTICAL_SLIDE_FROM_BOTTOM: + inVars = [ + "startTop" => height, + "endTop" => paddingTop + ]; + + outVars = [ + "startTop" => outComponent.top, + "endTop" => -height + ]; + + case TransitionMode.FADE: + + case _: + //TODO - support for custom transition by user + } + + _currentTransition = TransitionManager.instance.run(transitionId, + ["target" => inComponent], inVars, + ["target" => outComponent], outVars, + function() { + outComponent.includeInLayout = false; + outComponent.hidden = true; + }); + + } else { + if (inComponent != null) { + inComponent.left = paddingLeft; + inComponent.top = paddingTop; + } + + 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 set(value:Variant) { + + } +} + +@:dox(hide) +@:access(haxe.ui.containers.Stack) +class StackDefaultSelectedIndexBehaviour extends Behaviour { + public override function set(value:Variant) { + var stack:Stack = cast _component; + stack.animateTo(value); + } } \ No newline at end of file diff --git a/haxe/ui/macros/ModuleMacros.hx b/haxe/ui/macros/ModuleMacros.hx index 804439eb7..6459e5c0a 100644 --- a/haxe/ui/macros/ModuleMacros.hx +++ b/haxe/ui/macros/ModuleMacros.hx @@ -138,23 +138,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'; } } @@ -172,6 +175,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 a79d18bfc..da37aa639 100644 --- a/haxe/ui/module.xml +++ b/haxe/ui/module.xml @@ -63,6 +63,9 @@ + + + @@ -86,6 +89,16 @@ + + + + + + + + + + @@ -118,4 +131,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/haxe/ui/parsers/modules/Module.hx b/haxe/ui/parsers/modules/Module.hx index 5756267e1..8f4ba042b 100644 --- a/haxe/ui/parsers/modules/Module.hx +++ b/haxe/ui/parsers/modules/Module.hx @@ -9,6 +9,7 @@ class Module { public var plugins(default, default):Array; public var properties(default, default):Array; public var animations(default, default):Array; + public var transitions(default, default):Array; public function new() { resourceEntries = []; @@ -18,6 +19,7 @@ class Module { plugins = []; properties = []; animations = []; + transitions = []; } public function validate() { @@ -111,3 +113,14 @@ class ModuleAnimationComponentRefEntry { vars = new Map(); } } + +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 8f10876fe..6f1b21506 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.ModuleAnimationEntry; class XMLParser extends ModuleParser { public function new() { super(); @@ -76,37 +77,21 @@ class XMLParser extends ModuleParser { module.properties.push(property); } } else if (nodeName == "animations") { - for (animationNode in el.elementsNamed("animation")) { - 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")); - } + parseAnimations(el, module.animations); + } else if (nodeName == "transitions") { + for (transitionNode in el.elementsNamed("transition")) { + 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, transition.inAnimations); + } - animation.keyFrames.push(keyFrame); + for (outAnimationNode in transitionNode.elementsNamed("outAnimations")) { + parseAnimations(outAnimationNode, transition.outAnimations); } - module.animations.push(animation); + module.transitions.push(transition); } } } @@ -114,6 +99,48 @@ class XMLParser extends ModuleParser { return module; } + private function parseAnimations(node:Xml, result:Array=null):Array + { + if (result == null) { + result = []; + } + + for (animationNode in node.elementsNamed("animation")) { + 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)); + } + } + + keyFrame.componentRefs.set(componentRef.id, componentRef); + } + + animation.keyFrames.push(keyFrame); + } + + result.push(animation); + } + + return result; + } + private function buildCondition(parentNode:Xml, node:Xml):String { var condition:String = parentNode.get("condition"); if (parentNode.get("if") != null) { From 9ed6bd7785989d150009356c28b6db5e38e01927 Mon Sep 17 00:00:00 2001 From: Pablo Martin Date: Mon, 30 Jan 2017 15:02:41 +0100 Subject: [PATCH 02/11] Improve positions. --- haxe/ui/containers/Stack.hx | 43 +++++++++++++++++++++++++------------ 1 file changed, 29 insertions(+), 14 deletions(-) diff --git a/haxe/ui/containers/Stack.hx b/haxe/ui/containers/Stack.hx index 8ac063035..589b9ba78 100644 --- a/haxe/ui/containers/Stack.hx +++ b/haxe/ui/containers/Stack.hx @@ -127,9 +127,9 @@ class Stack extends Box implements IClonable { //*********************************************************************************************************** private var _currentTransition:Transition; - private function animateTo(index:Int) { - var inComponent:Component = (index != -1) ? getComponentAt(index) : null; - var outComponent:Component = (_selectedIndex != -1) ? getComponentAt(_selectedIndex) : null; + private function animateTo(fromIndex:Int, toIndex:Int) { + var inComponent:Component = (toIndex != -1) ? getComponentAt(toIndex) : null; + var outComponent:Component = (fromIndex != -1) ? getComponentAt(fromIndex) : null; var transitionId:String = null; var mode:TransitionMode = transitionMode; @@ -171,18 +171,24 @@ class Stack extends Box implements IClonable { switch (mode) { case TransitionMode.HORIZONTAL_SLIDE: inVars = [ - "startLeft" => ((_selectedIndex < index) ? width : -width), + "startLeft" => ((fromIndex < toIndex) ? + outComponent.left + outComponent.width + paddingLeft + : outComponent.left - paddingRight - inComponent.width), + "startTop" => paddingTop, "endLeft" => paddingLeft ]; outVars = [ "startLeft" => outComponent.left, - "endLeft" => ((_selectedIndex < index) ? -width : width) + "endLeft" => ((fromIndex < toIndex) ? + -width + paddingLeft + paddingRight + : width) ]; case TransitionMode.HORIZONTAL_SLIDE_FROM_LEFT: inVars = [ - "startLeft" => -width, + "startLeft" => outComponent.left - paddingRight - inComponent.width, + "startTop" => paddingTop, "endLeft" => paddingLeft ]; @@ -193,29 +199,36 @@ class Stack extends Box implements IClonable { case TransitionMode.HORIZONTAL_SLIDE_FROM_RIGHT: inVars = [ - "startLeft" => width, + "startLeft" => outComponent.left + outComponent.width + paddingLeft, + "startTop" => paddingTop, "endLeft" => paddingLeft ]; outVars = [ "startLeft" => outComponent.left, - "endLeft" => -width + "endLeft" => -width + paddingLeft + paddingRight ]; case TransitionMode.VERTICAL_SLIDE: inVars = [ - "startTop" => ((_selectedIndex < index) ? height : -height), + "startLeft" => paddingLeft, + "startTop" => ((fromIndex < toIndex) ? + outComponent.top + outComponent.height + paddingTop + : outComponent.top - paddingBottom - inComponent.height), "endTop" => paddingTop ]; outVars = [ "startTop" => outComponent.top, - "endTop" => ((_selectedIndex < index) ? -height : height) + "endTop" => ((fromIndex < toIndex) ? + -height + paddingTop + paddingBottom + : height) ]; case TransitionMode.VERTICAL_SLIDE_FROM_TOP: inVars = [ - "startTop" => -height, + "startLeft" => paddingLeft, + "startTop" => outComponent.top - paddingBottom - inComponent.height, "endTop" => paddingTop ]; @@ -226,13 +239,14 @@ class Stack extends Box implements IClonable { case TransitionMode.VERTICAL_SLIDE_FROM_BOTTOM: inVars = [ - "startTop" => height, + "startLeft" => paddingLeft, + "startTop" => outComponent.top + outComponent.height + paddingTop, "endTop" => paddingTop ]; outVars = [ "startTop" => outComponent.top, - "endTop" => -height + "endTop" => -height + paddingTop + paddingBottom ]; case TransitionMode.FADE: @@ -245,6 +259,7 @@ class Stack extends Box implements IClonable { ["target" => inComponent], inVars, ["target" => outComponent], outVars, function() { + _currentTransition = null; outComponent.includeInLayout = false; outComponent.hidden = true; }); @@ -285,6 +300,6 @@ class StackDefaultTransitionModeBehaviour extends Behaviour { class StackDefaultSelectedIndexBehaviour extends Behaviour { public override function set(value:Variant) { var stack:Stack = cast _component; - stack.animateTo(value); + stack.animateTo(stack._selectedIndex, value); } } \ No newline at end of file From 42dcd66581ed8f54b2d50a44b7f6ba0ab8ae85d5 Mon Sep 17 00:00:00 2001 From: Pablo Martin Date: Mon, 30 Jan 2017 15:03:01 +0100 Subject: [PATCH 03/11] Clean. --- haxe/ui/animation/transition/Transition.hx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/haxe/ui/animation/transition/Transition.hx b/haxe/ui/animation/transition/Transition.hx index bee9557a1..f11a248aa 100644 --- a/haxe/ui/animation/transition/Transition.hx +++ b/haxe/ui/animation/transition/Transition.hx @@ -35,7 +35,7 @@ class Transition { if (onComplete != null) { var total = inAnimations.length + outAnimations.length; var current = 0; - animationCallback = onComplete == null ? null : function() { + animationCallback = function() { if (++current >= total) { onComplete(); } From deaf497c86e5801f1bd3c77f800a91259a4daf08 Mon Sep 17 00:00:00 2001 From: Pablo Martin Date: Mon, 30 Jan 2017 15:03:30 +0100 Subject: [PATCH 04/11] Change default animation for slide. --- haxe/ui/module.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/haxe/ui/module.xml b/haxe/ui/module.xml index da37aa639..d9e309f5a 100644 --- a/haxe/ui/module.xml +++ b/haxe/ui/module.xml @@ -135,7 +135,7 @@ - + @@ -146,7 +146,7 @@ - + From 931c32a38d3844e226225592029bdcd698db5001 Mon Sep 17 00:00:00 2001 From: Pablo Martin Date: Mon, 30 Jan 2017 15:23:08 +0100 Subject: [PATCH 05/11] New ImageGallery component based in the Stack container. --- haxe/ui/components/complex/ImageGallery.hx | 237 +++++++++++++++++++++ haxe/ui/module.xml | 5 + 2 files changed, 242 insertions(+) create mode 100644 haxe/ui/components/complex/ImageGallery.hx diff --git a/haxe/ui/components/complex/ImageGallery.hx b/haxe/ui/components/complex/ImageGallery.hx new file mode 100644 index 000000000..3465a993a --- /dev/null +++ b/haxe/ui/components/complex/ImageGallery.hx @@ -0,0 +1,237 @@ +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; +import haxe.ui.util.MathUtil; + +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 _startPosX:Float; + private var _startPosY:Float; + private var _currentPosX:Float; + private var _currentPosY:Float; + private var _drag:Bool; //TODO - it isn't necessary, but something is wrong with _onMouseUp. It can be called twice (race condition?) and still _drag is ignored + + private function _onMouseDown(e:MouseEvent):Void { + Screen.instance.registerEvent(MouseEvent.MOUSE_MOVE, _onMouseMove); + Screen.instance.registerEvent(MouseEvent.MOUSE_UP, _onMouseUp); + + _startPosX = _currentPosX = e.screenX; + _startPosY = _currentPosY = e.screenY; + + if (_currentTransition != null) { + _currentTransition.stop(); + _currentTransition = null; + } + + _drag = true; + } + + 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 (_drag == false) { + return; + } + + _drag = 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 != paddingTop) { + var currentIndex:Int = _selectedIndex; + _selectedIndex = currentComponent.top > 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 != paddingLeft) { + var currentIndex:Int = _selectedIndex; + _selectedIndex = currentComponent.left > paddingLeft ? _selectedIndex - 1 : _selectedIndex + 1; + selectedIndex = currentIndex; + } + } + + if (MathUtil.distance(_startPosX, _startPosY, _currentPosX, _currentPosY) < 1 * Toolkit.pixelsPerRem * Toolkit.scale) { + dispatch(new MouseEvent(MouseEvent.CLICK)); + } + } + + //****************************************************************************************** + // 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 >= paddingTop) && (offset > 0 && selectedIndex == 0)) + || ((newTop <= paddingTop) && (offset < 0 && selectedIndex == childrenCount - 1))) { + newTop = paddingTop; + } + + currentComponent.top = newTop; + + if (selectedIndex > 0) { + var previousComponent = getComponentAt(selectedIndex - 1); + var top:Float = currentComponent.top - previousComponent.height - paddingBottom; + var hidden:Bool = (top + previousComponent.height <= 0 || top + previousComponent.height >= height); + if (previousComponent.hidden != hidden) { + previousComponent.includeInLayout = !hidden; + previousComponent.hidden = hidden; + } + + previousComponent.left = paddingTop; + previousComponent.top = top; + } + + if (selectedIndex < childrenCount - 1) { + var nextComponent = getComponentAt(selectedIndex + 1); + var top:Float = currentComponent.top + currentComponent.height + paddingTop; + var hidden:Bool = (top <= 0 || top >= width); + if (nextComponent.hidden != hidden) { + nextComponent.includeInLayout = !hidden; + nextComponent.hidden = hidden; + } + + nextComponent.left = paddingTop; + nextComponent.top = top; + } + case _: + var offset:Float = screenX - _currentPosX; + var newLeft:Float = currentComponent.left + offset; + if (((newLeft >= paddingLeft) && (offset > 0 && selectedIndex == 0)) + || ((newLeft <= paddingLeft) && (offset < 0 && selectedIndex == childrenCount - 1))) { + newLeft = paddingLeft; + } + + currentComponent.left = newLeft; + + if (selectedIndex > 0) { + var previousComponent = getComponentAt(selectedIndex - 1); + var left:Float = currentComponent.left - previousComponent.width - 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 = paddingTop; + } + + if (selectedIndex < childrenCount - 1) { + var nextComponent = getComponentAt(selectedIndex + 1); + var left:Float = currentComponent.left + currentComponent.width + paddingLeft; + var hidden:Bool = (left <= 0 || left >= width); + if (nextComponent.hidden != hidden) { + nextComponent.includeInLayout = !hidden; + nextComponent.hidden = hidden; + } + + nextComponent.left = left; + nextComponent.top = paddingTop; + } + } + } +} \ No newline at end of file diff --git a/haxe/ui/module.xml b/haxe/ui/module.xml index d9e309f5a..caac07c8f 100644 --- a/haxe/ui/module.xml +++ b/haxe/ui/module.xml @@ -9,6 +9,7 @@ + @@ -17,6 +18,7 @@ + @@ -66,6 +68,9 @@ + + + From d37d82aad9ded28a993e01d3be524cdc46b97546 Mon Sep 17 00:00:00 2001 From: Pablo Martin Date: Mon, 30 Jan 2017 19:24:34 +0100 Subject: [PATCH 06/11] Some fixes (padding could have null value). --- haxe/ui/components/complex/ImageGallery.hx | 50 +++++++++++---------- haxe/ui/containers/Stack.hx | 52 +++++++++++----------- 2 files changed, 53 insertions(+), 49 deletions(-) diff --git a/haxe/ui/components/complex/ImageGallery.hx b/haxe/ui/components/complex/ImageGallery.hx index 3465a993a..c8049c087 100644 --- a/haxe/ui/components/complex/ImageGallery.hx +++ b/haxe/ui/components/complex/ImageGallery.hx @@ -84,9 +84,15 @@ class ImageGallery extends Stack { private var _startPosY:Float; private var _currentPosX:Float; private var _currentPosY:Float; - private var _drag:Bool; //TODO - it isn't necessary, but something is wrong with _onMouseUp. It can be called twice (race condition?) and still _drag is ignored + 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); @@ -97,8 +103,6 @@ class ImageGallery extends Stack { _currentTransition.stop(); _currentTransition = null; } - - _drag = true; } private function _onMouseMove(e:MouseEvent):Void { @@ -112,11 +116,11 @@ class ImageGallery extends Stack { } private function _onMouseUp(e:MouseEvent):Void { - if (_drag == false) { + if (_dragging == false) { return; } - _drag = false; + _dragging = false; Screen.instance.unregisterEvent(MouseEvent.MOUSE_MOVE, _onMouseMove); Screen.instance.unregisterEvent(MouseEvent.MOUSE_UP, _onMouseUp); @@ -129,9 +133,9 @@ class ImageGallery extends Stack { selectedIndex -= 1; } else if (currentComponent.top < -height * percentToChange / 100) { selectedIndex += 1; - } else if (currentComponent.top != paddingTop) { + } else if (currentComponent.top != layout.paddingTop) { var currentIndex:Int = _selectedIndex; - _selectedIndex = currentComponent.top > paddingTop ? _selectedIndex - 1 : _selectedIndex + 1; + _selectedIndex = currentComponent.top > layout.paddingTop ? _selectedIndex - 1 : _selectedIndex + 1; selectedIndex = currentIndex; } @@ -141,9 +145,9 @@ class ImageGallery extends Stack { selectedIndex -= 1; } else if (currentComponent.left < -width * percentToChange / 100) { selectedIndex += 1; - } else if (currentComponent.left != paddingLeft) { + } else if (currentComponent.left != layout.paddingLeft) { var currentIndex:Int = _selectedIndex; - _selectedIndex = currentComponent.left > paddingLeft ? _selectedIndex - 1 : _selectedIndex + 1; + _selectedIndex = currentComponent.left > layout.paddingLeft ? _selectedIndex - 1 : _selectedIndex + 1; selectedIndex = currentIndex; } } @@ -165,51 +169,51 @@ class ImageGallery extends Stack { case TransitionMode.VERTICAL_SLIDE: var offset:Float = screenY - _currentPosY; var newTop:Float = currentComponent.top + offset; - if (((newTop >= paddingTop) && (offset > 0 && selectedIndex == 0)) - || ((newTop <= paddingTop) && (offset < 0 && selectedIndex == childrenCount - 1))) { - newTop = paddingTop; + 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 - paddingBottom; + 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 = paddingTop; + previousComponent.left = layout.paddingTop; previousComponent.top = top; } if (selectedIndex < childrenCount - 1) { var nextComponent = getComponentAt(selectedIndex + 1); - var top:Float = currentComponent.top + currentComponent.height + paddingTop; + 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 = paddingTop; + nextComponent.left = layout.paddingTop; nextComponent.top = top; } case _: var offset:Float = screenX - _currentPosX; var newLeft:Float = currentComponent.left + offset; - if (((newLeft >= paddingLeft) && (offset > 0 && selectedIndex == 0)) - || ((newLeft <= paddingLeft) && (offset < 0 && selectedIndex == childrenCount - 1))) { - newLeft = paddingLeft; + 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 - paddingRight; + 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; @@ -217,12 +221,12 @@ class ImageGallery extends Stack { } previousComponent.left = left; - previousComponent.top = paddingTop; + previousComponent.top = layout.paddingTop; } if (selectedIndex < childrenCount - 1) { var nextComponent = getComponentAt(selectedIndex + 1); - var left:Float = currentComponent.left + currentComponent.width + paddingLeft; + var left:Float = currentComponent.left + currentComponent.width + layout.paddingLeft; var hidden:Bool = (left <= 0 || left >= width); if (nextComponent.hidden != hidden) { nextComponent.includeInLayout = !hidden; @@ -230,7 +234,7 @@ class ImageGallery extends Stack { } nextComponent.left = left; - nextComponent.top = paddingTop; + nextComponent.top = layout.paddingTop; } } } diff --git a/haxe/ui/containers/Stack.hx b/haxe/ui/containers/Stack.hx index 589b9ba78..555710fbb 100644 --- a/haxe/ui/containers/Stack.hx +++ b/haxe/ui/containers/Stack.hx @@ -172,24 +172,24 @@ class Stack extends Box implements IClonable { case TransitionMode.HORIZONTAL_SLIDE: inVars = [ "startLeft" => ((fromIndex < toIndex) ? - outComponent.left + outComponent.width + paddingLeft - : outComponent.left - paddingRight - inComponent.width), - "startTop" => paddingTop, - "endLeft" => paddingLeft + 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 + paddingLeft + paddingRight + -width + layout.paddingLeft + layout.paddingRight : width) ]; case TransitionMode.HORIZONTAL_SLIDE_FROM_LEFT: inVars = [ - "startLeft" => outComponent.left - paddingRight - inComponent.width, - "startTop" => paddingTop, - "endLeft" => paddingLeft + "startLeft" => outComponent.left - layout.paddingRight - inComponent.width, + "startTop" => layout.paddingTop, + "endLeft" => layout.paddingLeft ]; outVars = [ @@ -199,37 +199,37 @@ class Stack extends Box implements IClonable { case TransitionMode.HORIZONTAL_SLIDE_FROM_RIGHT: inVars = [ - "startLeft" => outComponent.left + outComponent.width + paddingLeft, - "startTop" => paddingTop, - "endLeft" => paddingLeft + "startLeft" => outComponent.left + outComponent.width + layout.paddingLeft, + "startTop" => layout.paddingTop, + "endLeft" => layout.paddingLeft ]; outVars = [ "startLeft" => outComponent.left, - "endLeft" => -width + paddingLeft + paddingRight + "endLeft" => -width + layout.paddingLeft + layout.paddingRight ]; case TransitionMode.VERTICAL_SLIDE: inVars = [ - "startLeft" => paddingLeft, + "startLeft" => layout.paddingLeft, "startTop" => ((fromIndex < toIndex) ? - outComponent.top + outComponent.height + paddingTop - : outComponent.top - paddingBottom - inComponent.height), - "endTop" => paddingTop + outComponent.top + outComponent.height + layout.paddingTop + : outComponent.top - layout.paddingBottom - inComponent.height), + "endTop" => layout.paddingTop ]; outVars = [ "startTop" => outComponent.top, "endTop" => ((fromIndex < toIndex) ? - -height + paddingTop + paddingBottom + -height + layout.paddingTop + layout.paddingBottom : height) ]; case TransitionMode.VERTICAL_SLIDE_FROM_TOP: inVars = [ - "startLeft" => paddingLeft, - "startTop" => outComponent.top - paddingBottom - inComponent.height, - "endTop" => paddingTop + "startLeft" => layout.paddingLeft, + "startTop" => outComponent.top - layout.paddingBottom - inComponent.height, + "endTop" => layout.paddingTop ]; outVars = [ @@ -239,14 +239,14 @@ class Stack extends Box implements IClonable { case TransitionMode.VERTICAL_SLIDE_FROM_BOTTOM: inVars = [ - "startLeft" => paddingLeft, - "startTop" => outComponent.top + outComponent.height + paddingTop, - "endTop" => paddingTop + "startLeft" => layout.paddingLeft, + "startTop" => outComponent.top + outComponent.height + layout.paddingTop, + "endTop" => layout.paddingTop ]; outVars = [ "startTop" => outComponent.top, - "endTop" => -height + paddingTop + paddingBottom + "endTop" => -height + layout.paddingTop + layout.paddingBottom ]; case TransitionMode.FADE: @@ -266,8 +266,8 @@ class Stack extends Box implements IClonable { } else { if (inComponent != null) { - inComponent.left = paddingLeft; - inComponent.top = paddingTop; + inComponent.left = layout.paddingLeft; + inComponent.top = layout.paddingTop; } if (outComponent != null) { From c8210af5808a859ef944ca5b460162d68680137d Mon Sep 17 00:00:00 2001 From: Pablo Martin Date: Mon, 30 Jan 2017 19:34:39 +0100 Subject: [PATCH 07/11] Remove click event. --- haxe/ui/components/complex/ImageGallery.hx | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/haxe/ui/components/complex/ImageGallery.hx b/haxe/ui/components/complex/ImageGallery.hx index c8049c087..ad6d7ee22 100644 --- a/haxe/ui/components/complex/ImageGallery.hx +++ b/haxe/ui/components/complex/ImageGallery.hx @@ -6,7 +6,6 @@ import haxe.ui.core.Component; import haxe.ui.core.MouseEvent; import haxe.ui.core.Screen; import haxe.ui.layouts.AbsoluteLayout; -import haxe.ui.util.MathUtil; class ImageGallery extends Stack { public function new() { @@ -80,8 +79,6 @@ class ImageGallery extends Stack { // Events //****************************************************************************************** - private var _startPosX:Float; - private var _startPosY:Float; private var _currentPosX:Float; private var _currentPosY:Float; private var _dragging:Bool = false; @@ -96,8 +93,8 @@ class ImageGallery extends Stack { Screen.instance.registerEvent(MouseEvent.MOUSE_MOVE, _onMouseMove); Screen.instance.registerEvent(MouseEvent.MOUSE_UP, _onMouseUp); - _startPosX = _currentPosX = e.screenX; - _startPosY = _currentPosY = e.screenY; + _currentPosX = e.screenX; + _currentPosY = e.screenY; if (_currentTransition != null) { _currentTransition.stop(); @@ -151,10 +148,6 @@ class ImageGallery extends Stack { selectedIndex = currentIndex; } } - - if (MathUtil.distance(_startPosX, _startPosY, _currentPosX, _currentPosY) < 1 * Toolkit.pixelsPerRem * Toolkit.scale) { - dispatch(new MouseEvent(MouseEvent.CLICK)); - } } //****************************************************************************************** From b50ec02d615e769e2f425f9665b386aaec010907 Mon Sep 17 00:00:00 2001 From: Pablo Martin Date: Wed, 1 Feb 2017 14:36:03 +0100 Subject: [PATCH 08/11] Auto select the first index when the first component is added. --- haxe/ui/containers/Stack.hx | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/haxe/ui/containers/Stack.hx b/haxe/ui/containers/Stack.hx index 555710fbb..a3c2bc4a6 100644 --- a/haxe/ui/containers/Stack.hx +++ b/haxe/ui/containers/Stack.hx @@ -32,6 +32,9 @@ class Stack extends Box implements IClonable { //****************************************************************************************** public override function addComponent(child:Component):Component { super.addComponent(child); + if (_selectedIndex == -1 && childComponents.length == 1) { + selectedIndex = 0; + } child.hidden = (childComponents.length - 1 != _selectedIndex); child.includeInLayout = child.hidden == false; return child; @@ -40,14 +43,18 @@ class Stack extends Box implements IClonable { //TODO --> https://github.com/haxeui/haxeui-core/pull/93 /*public override function addComponentAt(child:Component, index:Int):Component { super.addComponentAt(child, index); + if (_selectedIndex == -1 && childComponents.length == 1) { + selectedIndex = 0; + } child.hidden = (index != _selectedIndex); return child; }*/ public override function removeComponent(child:Component, dispose:Bool = true, invalidate:Bool = true):Component { var index:Int = getComponentIndex(child); - if (index == _selectedIndex) + if (index == _selectedIndex) { selectedIndex = -1; + } return super.removeComponent(child); } From 33d88ce1047b81ca371cc33e79fb5bf5332a0db9 Mon Sep 17 00:00:00 2001 From: Pablo Martin Date: Sun, 18 Feb 2018 23:37:59 +0100 Subject: [PATCH 09/11] Validation for Stack component. --- haxe/ui/containers/Stack.hx | 149 ++++++++++++++++++++++++++++-------- 1 file changed, 117 insertions(+), 32 deletions(-) diff --git a/haxe/ui/containers/Stack.hx b/haxe/ui/containers/Stack.hx index 1c3a1a822..681ae902a 100644 --- a/haxe/ui/containers/Stack.hx +++ b/haxe/ui/containers/Stack.hx @@ -6,14 +6,17 @@ 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(); } @@ -31,7 +34,7 @@ class Stack extends Box { //****************************************************************************************** public override function addComponent(child:Component):Component { super.addComponent(child); - if (_selectedIndex == -1 && childComponents.length == 1) { + if (_selectedIndex == NO_SELECTION && childComponents.length == 1) { selectedIndex = 0; } child.hidden = (childComponents.length - 1 != _selectedIndex); @@ -39,27 +42,35 @@ class Stack extends Box { return child; } - //TODO --> https://github.com/haxeui/haxeui-core/pull/93 - /*public override function addComponentAt(child:Component, index:Int):Component { + public override function addComponentAt(child:Component, index:Int):Component { super.addComponentAt(child, index); - if (_selectedIndex == -1 && childComponents.length == 1) { + 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 = -1; + 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 = -1; + selectedIndex = NO_SELECTION; super.removeAllComponents(dispose); } @@ -72,44 +83,41 @@ class Stack extends Box { // Public API //*********************************************************************************************************** - private var _selectedIndex:Int = -1; + private var _selectedIndex:Int = NO_SELECTION; @:clonable public var selectedIndex(get, set):Int; private function get_selectedIndex():Int { - return _selectedIndex; + return behaviourGet("selectedIndex"); } private function set_selectedIndex(value:Int):Int { - if (_selectedIndex == value) { - return value; - } - - if(_selectedIndex != -1) { - _history.push(_selectedIndex); - } - behaviourSet("selectedIndex", value); - _selectedIndex = value; + return value; + } - dispatch(new UIEvent(UIEvent.CHANGE)); + 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 _transitionMode; + return behaviourGet("transitionMode"); } private function set_transitionMode(value:TransitionMode):TransitionMode { - if (_transitionMode == value) { - return value; - } - - _transitionMode = value; behaviourSet("transitionMode", value); - return value; } + private var _currentSelection:Component; + private var _history : List = new List(); /** @@ -128,14 +136,75 @@ class Stack extends Box { 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 (dataInvalid || indexInvalid) { + validateIndex(); + } + + if (styleInvalid) { + validateStyle(); + } + + if (positionInvalid) { + validatePosition(); + } + + if (layoutInvalid) { + displayInvalid = validateLayout() || displayInvalid; + } + + 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 != -1) ? getComponentAt(toIndex) : null; - var outComponent:Component = (fromIndex != -1) ? getComponentAt(fromIndex) : null; + 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; @@ -296,16 +365,32 @@ class Stack extends Box { @:dox(hide) @:access(haxe.ui.containers.Stack) class StackDefaultTransitionModeBehaviour extends Behaviour { - public override function set(value:Variant) { + 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.animateTo(stack._selectedIndex, value); + var stack:Stack = cast(_component, Stack); + if (stack._selectedIndex != value) { + stack._selectedIndex = value; + stack.invalidateIndex(); + } } } \ No newline at end of file From 066f6acaac0a429ec084e9deba95df9b347d0432 Mon Sep 17 00:00:00 2001 From: Pablo Martin Date: Sun, 18 Feb 2018 23:52:43 +0100 Subject: [PATCH 10/11] Import fix. --- haxe/ui/containers/Stack.hx | 1 + 1 file changed, 1 insertion(+) diff --git a/haxe/ui/containers/Stack.hx b/haxe/ui/containers/Stack.hx index 681ae902a..4248f10ef 100644 --- a/haxe/ui/containers/Stack.hx +++ b/haxe/ui/containers/Stack.hx @@ -6,6 +6,7 @@ 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; From 5f71f66e44e81d451c4e0f230a343327439ff26d Mon Sep 17 00:00:00 2001 From: Pablo Martin Date: Mon, 19 Feb 2018 00:55:58 +0100 Subject: [PATCH 11/11] Animation fix. --- haxe/ui/containers/Stack.hx | 32 ++++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/haxe/ui/containers/Stack.hx b/haxe/ui/containers/Stack.hx index 4248f10ef..41e47838e 100644 --- a/haxe/ui/containers/Stack.hx +++ b/haxe/ui/containers/Stack.hx @@ -161,10 +161,6 @@ class Stack extends Box { validateData(); } - if (dataInvalid || indexInvalid) { - validateIndex(); - } - if (styleInvalid) { validateStyle(); } @@ -173,10 +169,22 @@ class Stack extends Box { validatePosition(); } - if (layoutInvalid) { + 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. } @@ -231,7 +239,7 @@ class Stack extends Box { } if (inComponent != null) { - inComponent.includeInLayout = true; + inComponent.includeInLayout = false; //Avoid that the layout can override the animation values inComponent.hidden = false; } @@ -336,14 +344,22 @@ class Stack extends Box { ["target" => outComponent], outVars, function() { _currentTransition = null; - outComponent.includeInLayout = false; - outComponent.hidden = true; + + 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) {