diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/CompilerServices/CompilerServices.cs | 389 | ||||
| -rw-r--r-- | src/GraphicObjects/GraphicObject.cs | 338 | ||||
| -rw-r--r-- | src/GraphicObjects/IBindable.cs | 31 | ||||
| -rw-r--r-- | src/GraphicObjects/TextBox.cs | 71 | ||||
| -rw-r--r-- | src/Input/KeyPressEventArgs.cs | 58 | ||||
| -rw-r--r-- | src/Interface.cs | 29 |
6 files changed, 520 insertions, 396 deletions
diff --git a/src/CompilerServices/CompilerServices.cs b/src/CompilerServices/CompilerServices.cs index da5c0fe4..cd491c4d 100644 --- a/src/CompilerServices/CompilerServices.cs +++ b/src/CompilerServices/CompilerServices.cs @@ -50,6 +50,8 @@ namespace Crow string dynMethodId = ""; bool resolved = false; + public bool TwoWayBinding = false; + public string NewDynMethodId { get { if (!string.IsNullOrEmpty (dynMethodId)) @@ -88,19 +90,32 @@ namespace Crow #region CTOR public Binding(){} - public Binding(MemberReference _source, string _expression) + public Binding(MemberReference _target, string _expression) { - Target = _source; + Target = _target; Expression = _expression; } - public Binding(object _source, string _member, string _expression) + public Binding(object _target, string _member, string _expression) { - Target = new MemberReference (_source, _source.GetType().GetMember (_member) [0]); + Target = new MemberReference (_target, _target.GetType().GetMember (_member) [0]); Expression = _expression; } + public Binding(object _target, string _targetMember, object _source, string _sourceMember) + { + Target = new MemberReference (_target, _target.GetType().GetMember (_targetMember) [0]); + Source = new MemberReference (_source, _source.GetType().GetMember (_sourceMember) [0]); + } + public Binding(MemberReference _target, MemberReference _source) + { + Target = _target; + Source = _source; + } #endregion - public bool FindTarget(){ + public bool FindSource(){ + if (Source != null) + return true; + string member = null; //if binding exp = '{}' => binding is done on datasource @@ -112,7 +127,14 @@ namespace Crow return true; } - string[] bindingExp = Expression.Split ('/'); + string expression = Expression; + + if (Expression.StartsWith ("²")) { + expression = expression.Substring (1); + TwoWayBinding = true; + } + + string[] bindingExp = expression.Split ('/'); if (bindingExp.Length == 1) { //datasource binding @@ -164,10 +186,18 @@ namespace Crow return false; } - if (Source.FindMember (member)) + if (Source.FindMember (member)) { + if (TwoWayBinding) { + IBindable source = Source.Instance as IBindable; + if (source == null) + throw new Exception (Target.Instance.ToString() + " does not implement IBindable for 2 way bindings"); + source.Bindings.Add (new Binding (Source, Target)); + } return true; + } Debug.WriteLine ("Binding member not found: " + member); + Source = null; return false; } public void Reset() @@ -186,6 +216,351 @@ namespace Crow public static class CompilerServices { + public static void ResolveBindings(List<Binding> Bindings) + { + if (Bindings == null) + return; + if (Bindings.Count == 0) + return; + #if DEBUG_BINDING + Debug.WriteLine ("Resolve Bindings => " + this.ToString ()); + #endif + //grouped bindings by Instance of Source + Dictionary<object,List<Binding>> resolved = new Dictionary<object, List<Binding>>(); + + foreach (Binding b in Bindings) { + if (b.Resolved) + continue; + if (b.Target.Member.MemberType == MemberTypes.Event) { + if (b.Expression.StartsWith("{")){ + CompilerServices.CompileEventSource(b); + continue; + } + if (!b.FindSource ()) + continue; + //register handler for event + if (b.Source.Method == null) { + Debug.WriteLine ("\tError: Handler Method not found: " + b.ToString()); + continue; + } + try { + MethodInfo addHandler = b.Target.Event.GetAddMethod (); + Delegate del = Delegate.CreateDelegate (b.Target.Event.EventHandlerType, b.Source.Instance, b.Source.Method); + addHandler.Invoke (b.Target.Instance, new object[] { del }); + + #if DEBUG_BINDING + Debug.WriteLine ("\tHandler binded => " + b.ToString()); + #endif + b.Resolved = true; + } catch (Exception ex) { + Debug.WriteLine ("\tERROR: " + ex.ToString()); + } + continue; + } + + if (!b.FindSource ()) + continue; + + List<Binding> bindings = null; + if (!resolved.TryGetValue (b.Source.Instance, out bindings)) { + bindings = new List<Binding> (); + resolved [b.Source.Instance] = bindings; + } + bindings.Add (b); + b.Resolved = true; + } + + MethodInfo stringEquals = typeof(string).GetMethod + ("Equals", new Type[3] {typeof(string), typeof(string), typeof(StringComparison)}); + Type target_Type = Bindings[0].Target.Instance.GetType(); + EventInfo ei = typeof(IValueChange).GetEvent("ValueChanged"); + MethodInfo evtInvoke = ei.EventHandlerType.GetMethod ("Invoke"); + ParameterInfo[] evtParams = evtInvoke.GetParameters (); + Type handlerArgsType = evtParams [1].ParameterType; + Type[] args = {typeof(object), typeof(object),handlerArgsType}; + FieldInfo fiNewValue = typeof(ValueChangeEventArgs).GetField("NewValue"); + FieldInfo fiMbName = typeof(ValueChangeEventArgs).GetField("MemberName"); + + //group;only one dynMethods by target (valuechanged event source) + //changed value name tested in switch + //IEnumerable<Binding[]> groupedByTarget = resolved.GroupBy (g => g.Target.Instance, g => g, (k, g) => g.ToArray ()); + foreach (List<Binding> grouped in resolved.Values) { + int i = 0; + Type source_Type = grouped[0].Source.Instance.GetType(); + + DynamicMethod dm = null; + ILGenerator il = null; + + System.Reflection.Emit.Label[] jumpTable = null; + System.Reflection.Emit.Label endMethod = new System.Reflection.Emit.Label(); + + #region Retrieve EventHandler parameter type + //EventInfo ei = targetType.GetEvent ("ValueChanged"); + //no dynamic update if ValueChanged interface is not implemented + if (source_Type.GetInterfaces().Contains(typeof(IValueChange))){ + dm = new DynamicMethod(grouped[0].NewDynMethodId, + MethodAttributes.Family | MethodAttributes.FamANDAssem | MethodAttributes.NewSlot, + CallingConventions.Standard, + typeof(void), + args, + target_Type,true); + + il = dm.GetILGenerator(256); + + endMethod = il.DefineLabel(); + jumpTable = new System.Reflection.Emit.Label[grouped.Count]; + for (i = 0; i < grouped.Count; i++) + jumpTable [i] = il.DefineLabel (); + il.DeclareLocal(typeof(string)); + il.DeclareLocal(typeof(object)); + + il.Emit(OpCodes.Nop); + il.Emit(OpCodes.Ldarg_0); + //il.Emit(OpCodes.Isinst, sourceType); + //push new value onto stack + il.Emit(OpCodes.Ldarg_2); + il.Emit(OpCodes.Ldfld, fiNewValue); + il.Emit(OpCodes.Stloc_1); + //push name + il.Emit(OpCodes.Ldarg_2); + il.Emit(OpCodes.Ldfld, fiMbName); + il.Emit(OpCodes.Stloc_0); + il.Emit(OpCodes.Ldloc_0); + il.Emit(OpCodes.Brfalse, endMethod); + } + #endregion + + i = 0; + foreach (Binding b in grouped) { + #region initialize target with actual value + object targetValue = null; + if (b.Source.Member != null){ + if (b.Source.Member.MemberType == MemberTypes.Property) + targetValue = b.Source.Property.GetGetMethod ().Invoke (b.Source.Instance, null); + else if (b.Source.Member.MemberType == MemberTypes.Field) + targetValue = b.Source.Field.GetValue (b.Source.Instance); + else if (b.Source.Member.MemberType == MemberTypes.Method){ + MethodInfo mthSrc = b.Source.Method; + if (mthSrc.IsDefined(typeof(ExtensionAttribute), false)) + targetValue = mthSrc.Invoke(null, new object[] {b.Source.Instance}); + else + targetValue = mthSrc.Invoke(b.Source.Instance, null); + }else + throw new Exception ("unandled source member type for binding"); + }else if (string.IsNullOrEmpty(b.Expression)) + targetValue= grouped [0].Source.Instance;//empty binding exp=> bound to target object by default + //TODO: handle other dest type conversions + if (b.Target.Property.PropertyType == typeof(string)){ + if (targetValue == null){ + //set default value + + }else + targetValue = targetValue.ToString (); + } + if (targetValue != null) + b.Target.Property.GetSetMethod ().Invoke + (b.Target.Instance, new object[] { b.Target.Property.PropertyType.Cast(targetValue)}); + else + b.Target.Property.GetSetMethod ().Invoke + (b.Target.Instance, new object[] { targetValue }); + #endregion + + //if no dyn update, skip jump table + if (il == null) + continue; + + il.Emit (OpCodes.Ldloc_0); + if (b.Source.Member != null) + il.Emit (OpCodes.Ldstr, b.Source.Member.Name); + else + il.Emit (OpCodes.Ldstr, b.Expression.Split('/').LastOrDefault()); + il.Emit (OpCodes.Ldc_I4_4);//StringComparison.Ordinal + il.Emit (OpCodes.Callvirt, stringEquals); + il.Emit (OpCodes.Brtrue, jumpTable[i]); + i++; + } + + if (il == null) + continue; + + il.Emit (OpCodes.Br, endMethod); + + i = 0; + foreach (Binding b in grouped) { + + il.MarkLabel (jumpTable [i]); + il.Emit(OpCodes.Ldloc_1); + + //by default, target value type is deducted from source member type to allow + //memberless binding, if targetMember exists, it will be used to determine target + //value type for conversion + Type targetValueType = b.Target.Property.PropertyType; + if (b.Source.Member != null) { + if (b.Source.Member.MemberType == MemberTypes.Property) + targetValueType = b.Source.Property.PropertyType; + else if (b.Source.Member.MemberType == MemberTypes.Field) + targetValueType = b.Source.Field.FieldType; + else + throw new Exception ("unhandle target member type in binding"); + } + + if (b.Target.Property.PropertyType == typeof(string)) { + MemberReference tostring = new MemberReference (b.Target.Instance); + if (!tostring.FindMember ("ToString")) + throw new Exception ("ToString method not found"); + il.Emit (OpCodes.Callvirt, tostring.Method); + }else if (!targetValueType.IsValueType) + il.Emit(OpCodes.Castclass, targetValueType); + else if (b.Target.Property.PropertyType != targetValueType) + il.Emit(OpCodes.Callvirt, CompilerServices.GetConvertMethod( b.Target.Property.PropertyType )); + else + il.Emit(OpCodes.Unbox_Any, b.Target.Property.PropertyType); + + il.Emit(OpCodes.Callvirt, b.Target.Property.GetSetMethod()); + il.Emit (OpCodes.Br, endMethod); + i++; + + } + il.MarkLabel(endMethod); + il.Emit(OpCodes.Pop); + il.Emit(OpCodes.Ret); + + Delegate del = dm.CreateDelegate(ei.EventHandlerType, Bindings[0].Target.Instance); + MethodInfo addHandler = ei.GetAddMethod (); + addHandler.Invoke(grouped [0].Source.Instance, new object[] {del}); + } + } + + /// <summary> + /// Compile events expression in GOML attributes + /// </summary> + /// <param name="es">Event binding details</param> + public static void CompileEventSource(Binding binding) + { + #if DEBUG_BINDING + Debug.WriteLine ("\tCompile Event Source => " + binding.ToString()); + #endif + + Type target_type = binding.Target.Instance.GetType(); + + #region Retrieve EventHandler parameter type + MethodInfo evtInvoke = binding.Target.Event.EventHandlerType.GetMethod ("Invoke"); + ParameterInfo[] evtParams = evtInvoke.GetParameters (); + Type handlerArgsType = evtParams [1].ParameterType; + #endregion + + Type[] args = {typeof(object), typeof(object),handlerArgsType}; + DynamicMethod dm = new DynamicMethod(binding.NewDynMethodId, + typeof(void), + args, + target_type); + + + #region IL generation + ILGenerator il = dm.GetILGenerator(256); + + string src = binding.Expression.Trim(); + + if (! (src.StartsWith("{") || src.EndsWith ("}"))) + throw new Exception (string.Format("GOML:Malformed {0} Event handler: {1}", binding.Target.Member.Name, binding.Expression)); + + src = src.Substring (1, src.Length - 2); + string[] srcLines = src.Split (new char[] { ';' }); + + foreach (string srcLine in srcLines) { + string statement = srcLine.Trim (); + + string[] operandes = statement.Split (new char[] { '=' }); + if (operandes.Length < 2) //not an affectation + { + continue; + } + string lop = operandes [0].Trim (); + string rop = operandes [operandes.Length-1].Trim (); + + #region LEFT OPERANDES + GraphicObject lopObj = binding.Target.Instance as GraphicObject; //default left operand base object is + //the first arg (object sender) of the event handler + + il.Emit(OpCodes.Ldarg_0); //load sender ref onto the stack + + string[] lopParts = lop.Split (new char[] { '.' }); + if (lopParts.Length > 1) {//should search also for member of es.Source + MethodInfo FindByNameMi = typeof(GraphicObject).GetMethod("FindByName"); + for (int j = 0; j < lopParts.Length - 1; j++) { + il.Emit (OpCodes.Ldstr, lopParts[j]); + il.Emit(OpCodes.Callvirt, FindByNameMi); + } + } + + int i = lopParts.Length -1; + + MemberInfo[] lopMbis = lopObj.GetType().GetMember (lopParts[i]); + + if (lopMbis.Length<1) + throw new Exception (string.Format("CROW BINDING: Member not found '{0}'", lop)); + + OpCode lopSetOC; + dynamic lopSetMbi; + Type lopT = null; + switch (lopMbis[0].MemberType) { + case MemberTypes.Property: + PropertyInfo lopPi = target_type.GetProperty (lopParts[i]); + MethodInfo dstMi = lopPi.GetSetMethod (); + lopT = lopPi.PropertyType; + lopSetMbi = dstMi; + lopSetOC = OpCodes.Callvirt; + break; + case MemberTypes.Field: + FieldInfo dstFi = target_type.GetField(lopParts[i]); + lopT = dstFi.FieldType; + lopSetMbi = dstFi; + lopSetOC = OpCodes.Stfld; + break; + default: + throw new Exception (string.Format("GOML:member type not handle: {0}", lopParts[i])); + } + #endregion + + #region RIGHT OPERANDES + if (rop.StartsWith("\'")){ + if (!rop.EndsWith("\'")) + throw new Exception (string.Format + ("GOML:malformed string constant in handler: {0}", rop)); + string strcst = rop.Substring (1, rop.Length - 2); + + il.Emit(OpCodes.Ldstr,strcst); + + }else{ + if (lopT.IsEnum) + throw new NotImplementedException(); + + MethodInfo lopParseMi = lopT.GetMethod("Parse"); + if (lopParseMi == null) + throw new Exception (string.Format + ("GOML:no parse method found in: {0}", lopT.Name)); + il.Emit(OpCodes.Ldstr, rop); + il.Emit(OpCodes.Callvirt, lopParseMi); + il.Emit(OpCodes.Unbox_Any, lopT); + } + + #endregion + + //emit left operand assignment + il.Emit(lopSetOC, lopSetMbi); + } + + il.Emit(OpCodes.Ret); + + #endregion + + Delegate del = dm.CreateDelegate(binding.Target.Event.EventHandlerType, binding.Target.Instance); + MethodInfo addHandler = binding.Target.Event.GetAddMethod (); + addHandler.Invoke(binding.Target.Instance, new object[] {del}); + + binding.Resolved = true; + } + #region conversions internal static MethodInfo GetConvertMethod( Type targetType ) diff --git a/src/GraphicObjects/GraphicObject.cs b/src/GraphicObjects/GraphicObject.cs index cddc918a..c526e909 100644 --- a/src/GraphicObjects/GraphicObject.cs +++ b/src/GraphicObjects/GraphicObject.cs @@ -156,6 +156,7 @@ namespace Crow public event EventHandler<MouseMoveEventArgs> MouseLeave; public event EventHandler<KeyboardKeyEventArgs> KeyDown; public event EventHandler<KeyboardKeyEventArgs> KeyUp; + public event EventHandler<KeyPressEventArgs> KeyPress; public event EventHandler Focused; public event EventHandler Unfocused; public event EventHandler<LayoutingEventArgs> LayoutChanged; @@ -939,6 +940,9 @@ namespace Crow public virtual void onKeyDown(object sender, KeyboardKeyEventArgs e){ KeyDown.Raise (sender, e); } + public virtual void onKeyPress(object sender, KeyPressEventArgs e){ + KeyPress.Raise (sender, e); + } #endregion #region Mouse handling @@ -1055,342 +1059,10 @@ namespace Crow #if DEBUG_BINDING Debug.WriteLine ("Resolve Bindings => " + this.ToString ()); #endif - //grouped bindings by Instance of Source - Dictionary<object,List<Binding>> resolved = new Dictionary<object, List<Binding>>(); - - foreach (Binding b in Bindings) { - if (b.Resolved) - continue; - - if (b.Target.Member.MemberType == MemberTypes.Event) { - if (b.Expression.StartsWith("{")){ - CompileEventSource(b); - continue; - } - if (!b.FindTarget ()) - continue; - //register handler for event - if (b.Source.Method == null) { - Debug.WriteLine ("\tError: Handler Method not found: " + b.ToString()); - continue; - } - try { - MethodInfo addHandler = b.Target.Event.GetAddMethod (); - Delegate del = Delegate.CreateDelegate (b.Target.Event.EventHandlerType, b.Source.Instance, b.Source.Method); - addHandler.Invoke (this, new object[] { del }); - - #if DEBUG_BINDING - Debug.WriteLine ("\tHandler binded => " + b.ToString()); - #endif - b.Resolved = true; - } catch (Exception ex) { - Debug.WriteLine ("\tERROR: " + ex.ToString()); - } - continue; - } - - if (!b.FindTarget ()) - continue; - - List<Binding> bindings = null; - if (!resolved.TryGetValue (b.Source.Instance, out bindings)) { - bindings = new List<Binding> (); - resolved [b.Source.Instance] = bindings; - } - bindings.Add (b); - b.Resolved = true; - - } - - MethodInfo stringEquals = typeof(string).GetMethod - ("Equals", new Type[3] {typeof(string), typeof(string), typeof(StringComparison)}); - Type sourceType = this.GetType(); - EventInfo ei = typeof(IValueChange).GetEvent("ValueChanged"); - MethodInfo evtInvoke = ei.EventHandlerType.GetMethod ("Invoke"); - ParameterInfo[] evtParams = evtInvoke.GetParameters (); - Type handlerArgsType = evtParams [1].ParameterType; - Type[] args = {typeof(object), typeof(object),handlerArgsType}; - FieldInfo fiNewValue = typeof(ValueChangeEventArgs).GetField("NewValue"); - FieldInfo fiMbName = typeof(ValueChangeEventArgs).GetField("MemberName"); - - //group;only one dynMethods by target (valuechanged event source) - //changed value name tested in switch - //IEnumerable<Binding[]> groupedByTarget = resolved.GroupBy (g => g.Target.Instance, g => g, (k, g) => g.ToArray ()); - foreach (List<Binding> grouped in resolved.Values) { - int i = 0; - Type targetType = grouped[0].Source.Instance.GetType(); - - DynamicMethod dm = null; - ILGenerator il = null; - - System.Reflection.Emit.Label[] jumpTable = null; - System.Reflection.Emit.Label endMethod = new System.Reflection.Emit.Label(); - - #region Retrieve EventHandler parameter type - //EventInfo ei = targetType.GetEvent ("ValueChanged"); - //no dynamic update if ValueChanged interface is not implemented - if (targetType.GetInterfaces().Contains(typeof(IValueChange))){ - dm = new DynamicMethod(grouped[0].NewDynMethodId, - MethodAttributes.Family | MethodAttributes.FamANDAssem | MethodAttributes.NewSlot, - CallingConventions.Standard, - typeof(void), - args, - sourceType,true); - - il = dm.GetILGenerator(256); - - endMethod = il.DefineLabel(); - jumpTable = new System.Reflection.Emit.Label[grouped.Count]; - for (i = 0; i < grouped.Count; i++) - jumpTable [i] = il.DefineLabel (); - il.DeclareLocal(typeof(string)); - il.DeclareLocal(typeof(object)); - - il.Emit(OpCodes.Nop); - il.Emit(OpCodes.Ldarg_0); - //il.Emit(OpCodes.Isinst, sourceType); - //push new value onto stack - il.Emit(OpCodes.Ldarg_2); - il.Emit(OpCodes.Ldfld, fiNewValue); - il.Emit(OpCodes.Stloc_1); - //push name - il.Emit(OpCodes.Ldarg_2); - il.Emit(OpCodes.Ldfld, fiMbName); - il.Emit(OpCodes.Stloc_0); - il.Emit(OpCodes.Ldloc_0); - il.Emit(OpCodes.Brfalse, endMethod); - } - #endregion - - i = 0; - foreach (Binding b in grouped) { - #region initialize target with actual value - object targetValue = null; - if (b.Source.Member != null){ - if (b.Source.Member.MemberType == MemberTypes.Property) - targetValue = b.Source.Property.GetGetMethod ().Invoke (b.Source.Instance, null); - else if (b.Source.Member.MemberType == MemberTypes.Field) - targetValue = b.Source.Field.GetValue (b.Source.Instance); - else if (b.Source.Member.MemberType == MemberTypes.Method){ - MethodInfo mthSrc = b.Source.Method; - if (mthSrc.IsDefined(typeof(ExtensionAttribute), false)) - targetValue = mthSrc.Invoke(null, new object[] {b.Source.Instance}); - else - targetValue = mthSrc.Invoke(b.Source.Instance, null); - }else - throw new Exception ("unandled source member type for binding"); - }else if (string.IsNullOrEmpty(b.Expression)) - targetValue= grouped [0].Source.Instance;//empty binding exp=> bound to target object by default - //TODO: handle other dest type conversions - if (b.Target.Property.PropertyType == typeof(string)){ - if (targetValue == null){ - //set default value - - }else - targetValue = targetValue.ToString (); - } - if (targetValue != null) - b.Target.Property.GetSetMethod ().Invoke - (this, new object[] { b.Target.Property.PropertyType.Cast(targetValue)}); - else - b.Target.Property.GetSetMethod ().Invoke - (this, new object[] { targetValue }); - #endregion - - //if no dyn update, skip jump table - if (il == null) - continue; - - il.Emit (OpCodes.Ldloc_0); - if (b.Source.Member != null) - il.Emit (OpCodes.Ldstr, b.Source.Member.Name); - else - il.Emit (OpCodes.Ldstr, b.Expression.Split('/').LastOrDefault()); - il.Emit (OpCodes.Ldc_I4_4);//StringComparison.Ordinal - il.Emit (OpCodes.Callvirt, stringEquals); - il.Emit (OpCodes.Brtrue, jumpTable[i]); - i++; - } - - if (il == null) - continue; - - il.Emit (OpCodes.Br, endMethod); - - i = 0; - foreach (Binding b in grouped) { - - il.MarkLabel (jumpTable [i]); - il.Emit(OpCodes.Ldloc_1); - - //by default, target value type is deducted from source member type to allow - //memberless binding, if targetMember exists, it will be used to determine target - //value type for conversion - Type targetValueType = b.Target.Property.PropertyType; - if (b.Source.Member != null) { - if (b.Source.Member.MemberType == MemberTypes.Property) - targetValueType = b.Source.Property.PropertyType; - else if (b.Source.Member.MemberType == MemberTypes.Field) - targetValueType = b.Source.Field.FieldType; - else - throw new Exception ("unhandle target member type in binding"); - } - - if (b.Target.Property.PropertyType == typeof(string)) { - MemberReference tostring = new MemberReference (b.Target.Instance); - if (!tostring.FindMember ("ToString")) - throw new Exception ("ToString method not found"); - il.Emit (OpCodes.Callvirt, tostring.Method); - }else if (!targetValueType.IsValueType) - il.Emit(OpCodes.Castclass, targetValueType); - else if (b.Target.Property.PropertyType != targetValueType) - il.Emit(OpCodes.Callvirt, CompilerServices.GetConvertMethod( b.Target.Property.PropertyType )); - else - il.Emit(OpCodes.Unbox_Any, b.Target.Property.PropertyType); - il.Emit(OpCodes.Callvirt, b.Target.Property.GetSetMethod()); - il.Emit (OpCodes.Br, endMethod); - i++; - - } - il.MarkLabel(endMethod); - il.Emit(OpCodes.Pop); - il.Emit(OpCodes.Ret); - - Delegate del = dm.CreateDelegate(ei.EventHandlerType, this); - MethodInfo addHandler = ei.GetAddMethod (); - addHandler.Invoke(grouped [0].Source.Instance, new object[] {del}); - } + CompilerServices.ResolveBindings (Bindings); } - /// <summary> - /// Compile events expression in GOML attributes - /// </summary> - /// <param name="es">Event binding details</param> - public void CompileEventSource(Binding binding) - { - #if DEBUG_BINDING - Debug.WriteLine ("\tCompile Event Source => " + binding.ToString()); - #endif - - Type sourceType = this.GetType(); - - #region Retrieve EventHandler parameter type - MethodInfo evtInvoke = binding.Target.Event.EventHandlerType.GetMethod ("Invoke"); - ParameterInfo[] evtParams = evtInvoke.GetParameters (); - Type handlerArgsType = evtParams [1].ParameterType; - #endregion - - Type[] args = {typeof(object), typeof(object),handlerArgsType}; - DynamicMethod dm = new DynamicMethod(binding.NewDynMethodId, - typeof(void), - args, - sourceType); - - #region IL generation - ILGenerator il = dm.GetILGenerator(256); - - string src = binding.Expression.Trim(); - - if (! (src.StartsWith("{") || src.EndsWith ("}"))) - throw new Exception (string.Format("GOML:Malformed {0} Event handler: {1}", binding.Target.Member.Name, binding.Expression)); - - src = src.Substring (1, src.Length - 2); - string[] srcLines = src.Split (new char[] { ';' }); - - foreach (string srcLine in srcLines) { - string statement = srcLine.Trim (); - - string[] operandes = statement.Split (new char[] { '=' }); - if (operandes.Length < 2) //not an affectation - { - continue; - } - string lop = operandes [0].Trim (); - string rop = operandes [operandes.Length-1].Trim (); - - #region LEFT OPERANDES - GraphicObject lopObj = this; //default left operand base object is - //the first arg (object sender) of the event handler - - il.Emit(OpCodes.Ldarg_0); //load sender ref onto the stack - - string[] lopParts = lop.Split (new char[] { '.' }); - if (lopParts.Length > 1) {//should search also for member of es.Source - MethodInfo FindByNameMi = typeof(GraphicObject).GetMethod("FindByName"); - for (int j = 0; j < lopParts.Length - 1; j++) { - il.Emit (OpCodes.Ldstr, lopParts[j]); - il.Emit(OpCodes.Callvirt, FindByNameMi); - } - } - - int i = lopParts.Length -1; - - MemberInfo[] lopMbis = lopObj.GetType().GetMember (lopParts[i]); - - if (lopMbis.Length<1) - throw new Exception (string.Format("CROW BINDING: Member not found '{0}'", lop)); - - OpCode lopSetOC; - dynamic lopSetMbi; - Type lopT = null; - switch (lopMbis[0].MemberType) { - case MemberTypes.Property: - PropertyInfo lopPi = sourceType.GetProperty (lopParts[i]); - MethodInfo dstMi = lopPi.GetSetMethod (); - lopT = lopPi.PropertyType; - lopSetMbi = dstMi; - lopSetOC = OpCodes.Callvirt; - break; - case MemberTypes.Field: - FieldInfo dstFi = sourceType.GetField(lopParts[i]); - lopT = dstFi.FieldType; - lopSetMbi = dstFi; - lopSetOC = OpCodes.Stfld; - break; - default: - throw new Exception (string.Format("GOML:member type not handle: {0}", lopParts[i])); - } - #endregion - - #region RIGHT OPERANDES - if (rop.StartsWith("\'")){ - if (!rop.EndsWith("\'")) - throw new Exception (string.Format - ("GOML:malformed string constant in handler: {0}", rop)); - string strcst = rop.Substring (1, rop.Length - 2); - - il.Emit(OpCodes.Ldstr,strcst); - - }else{ - if (lopT.IsEnum) - throw new NotImplementedException(); - - MethodInfo lopParseMi = lopT.GetMethod("Parse"); - if (lopParseMi == null) - throw new Exception (string.Format - ("GOML:no parse method found in: {0}", lopT.Name)); - il.Emit(OpCodes.Ldstr, rop); - il.Emit(OpCodes.Callvirt, lopParseMi); - il.Emit(OpCodes.Unbox_Any, lopT); - } - - #endregion - - //emit left operand assignment - il.Emit(lopSetOC, lopSetMbi); - } - - il.Emit(OpCodes.Ret); - - #endregion - - Delegate del = dm.CreateDelegate(binding.Target.Event.EventHandlerType,this); - MethodInfo addHandler = binding.Target.Event.GetAddMethod (); - addHandler.Invoke(this, new object[] {del}); - - binding.Resolved = true; - } /// <summary> /// Remove dynamic delegates by ids from dataSource /// and delete ref of this in Shared interface refs diff --git a/src/GraphicObjects/IBindable.cs b/src/GraphicObjects/IBindable.cs new file mode 100644 index 00000000..642c1e9e --- /dev/null +++ b/src/GraphicObjects/IBindable.cs @@ -0,0 +1,31 @@ +// +// IValueChange.cs +// +// Author: +// Jean-Philippe Bruyère <jp.bruyere@hotmail.com> +// +// Copyright (c) 2015 jp +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +using System; +using System.Collections.Generic; + +namespace Crow +{ + public interface IBindable + { + List<Binding> Bindings { get; } + } +} + diff --git a/src/GraphicObjects/TextBox.cs b/src/GraphicObjects/TextBox.cs index 14cc1830..08ec1f69 100644 --- a/src/GraphicObjects/TextBox.cs +++ b/src/GraphicObjects/TextBox.cs @@ -80,9 +80,9 @@ namespace Crow case Key.Back: if (!selectionIsEmpty) { -// Text = Text.Remove(selectionStart, selectionEnd - selectionStart); -// selReleasePos = -1; -// currentCol = selBeginPos; + // Text = Text.Remove(selectionStart, selectionEnd - selectionStart); + // selReleasePos = -1; + // currentCol = selBeginPos; } else this.DeleteChar(); @@ -142,61 +142,26 @@ namespace Crow case Key.RWin: break; case Key.Tab: - this.Insert("\t"); - break; - case Key.KeypadDecimal: - this.Insert ("."); - break; - case Key.Space: - this.Insert(" "); - break; - case Key.KeypadDivide: - case Key.Slash: - this.Insert("/"); - break; - case Key.KeypadMultiply: - this.Insert("*"); - break; - case Key.KeypadMinus: - case Key.Minus: - this.Insert("-"); - break; - case Key.KeypadPlus: - case Key.Plus: - this.Insert("+"); - break; - case Key.ShiftLeft: - case Key.ShiftRight: - case Key.AltLeft: - case Key.AltRight: - break; - case Key.Semicolon: - this.Insert(";"); + this.Insert ("\t"); break; default: - if (!selectionIsEmpty) - { -// Text = Text.Remove(selectionStart, selectionEnd - selectionStart); -// currentCol = selBeginPos; - } - - string k = "?"; - if ((char)key >= 67 && (char)key <= 76) - k = ((int)key - 67).ToString(); - else if ((char)key >= 109 && (char)key <= 118) - k = ((int)key - 109).ToString(); - else if (e.Shift) - k = key.ToString(); - else - k = key.ToString().ToLower(); + break; + } + if (Width < 0) + RegisterForLayouting (LayoutingType.Width); + if (Height < 0) + RegisterForLayouting (LayoutingType.Height); + RegisterForGraphicUpdate(); + } + public override void onKeyPress (object sender, KeyPressEventArgs e) + { + base.onKeyPress (sender, e); - this.Insert (k); + this.Insert (e.KeyChar.ToString()); - SelRelease = -1; - SelBegin = new Point(CurrentColumn, SelBegin.Y); + SelRelease = -1; + SelBegin = new Point(CurrentColumn, SelBegin.Y); - break; - } if (Width < 0) RegisterForLayouting (LayoutingType.Width); if (Height < 0) diff --git a/src/Input/KeyPressEventArgs.cs b/src/Input/KeyPressEventArgs.cs new file mode 100644 index 00000000..73990613 --- /dev/null +++ b/src/Input/KeyPressEventArgs.cs @@ -0,0 +1,58 @@ +// #region License +// // +// // The Open Toolkit Library License +// // +// // Copyright (c) 2006 - 2009 the Open Toolkit library. +// // +// // Permission is hereby granted, free of charge, to any person obtaining a copy +// // of this software and associated documentation files (the "Software"), to deal +// // in the Software without restriction, including without limitation the rights to +// // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +// // the Software, and to permit persons to whom the Software is furnished to do +// // so, subject to the following conditions: +// // +// // The above copyright notice and this permission notice shall be included in all +// // copies or substantial portions of the Software. +// // +// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// // OTHER DEALINGS IN THE SOFTWARE. +// // +// #endregion + +using System; + +namespace Crow +{ + /// <summary> + /// Defines the event arguments for KeyPress events. Instances of this class are cached: + /// KeyPressEventArgs should only be used inside the relevant event, unless manually cloned. + /// </summary> + public class KeyPressEventArgs : EventArgs + { + char key_char; + + /// <summary> + /// Constructs a new instance. + /// </summary> + /// <param name="keyChar">The ASCII character that was typed.</param> + public KeyPressEventArgs(char keyChar) + { + KeyChar = keyChar; + } + + /// <summary> + /// Gets a <see cref="System.Char"/> that defines the ASCII character that was typed. + /// </summary> + public char KeyChar + { + get { return key_char; } + internal set { key_char = value; } + } + } +} diff --git a/src/Interface.cs b/src/Interface.cs index 1084ef7b..4cb94e76 100644 --- a/src/Interface.cs +++ b/src/Interface.cs @@ -647,9 +647,7 @@ namespace Crow hoverWidget.onMouseWheel (this, e); return true; } -// public bool ProcessKeyDown(int Key){ -// -// } + volatile bool mouseRepeatOn; volatile int mouseRepeatCount; Thread mouseRepeatThread; @@ -665,6 +663,31 @@ namespace Crow } #endregion + #region Keyboard + public bool ProcessKeyDown(int Key){ + Keyboard.SetKeyState ((Crow.Key)Key, true); + if (_focusedWidget == null) + return false; + KeyboardKeyEventArgs e = new KeyboardKeyEventArgs((Crow.Key)Key, false, Keyboard); + _focusedWidget.onKeyDown (this, e); + return true; + } + public bool ProcessKeyUp(int Key){ + Keyboard.SetKeyState ((Crow.Key)Key, false); + if (_activeWidget == null) + return false; + //KeyboardKeyEventArgs e = new KeyboardKeyEventArgs((Crow.Key)Key, false, Keyboard); + return true; + } + public bool ProcessKeyPress(char Key){ + if (_focusedWidget == null) + return false; + KeyPressEventArgs e = new KeyPressEventArgs(Key); + _focusedWidget.onKeyPress (this, e); + return true; + } + #endregion + #region ILayoutable implementation public void RegisterClip(Rectangle r){ clipping.AddRectangle (r); |