From c3851053afdb227c4d84c1ba2b31625b8166a59b Mon Sep 17 00:00:00 2001 From: =?utf8?q?Jean-Philippe=20Bruy=C3=A8re?= Date: Thu, 10 Oct 2019 07:24:38 +0200 Subject: [PATCH] new ColorPicker, 2 levels DataSource bindings with struct and property handling, First click bug solved --- Crow/Crow.csproj | 15 +- Crow/Default.style | 17 ++ Crow/Templates/ColorPicker.template | 80 ++++--- Crow/Templates/ColorPicker2.template | 44 ++++ Crow/Templates/EnumSelector.template | 5 + Crow/Templates/GroupBox.template | 2 +- Crow/Templates/Spinner.template | 2 +- Crow/src/CompilerServices/CompilerServices.cs | 11 +- Crow/src/Instantiator.cs | 94 ++++++-- Crow/src/Interface.cs | 2 +- Crow/src/Widgets/CheckBox.cs | 26 +-- Crow/src/Widgets/ColorPicker.cs | 211 +----------------- Crow/src/Widgets/ColorPicker2.cs | 206 +++++++++++++++++ Crow/src/Widgets/EnumSelector.cs | 31 ++- Crow/src/Widgets/GenericStack.cs | 26 +-- Crow/src/Widgets/Group.cs | 17 +- Crow/src/Widgets/Label.cs | 33 +-- Crow/src/Widgets/PrivateContainer.cs | 26 +-- Crow/src/Widgets/TemplatedGroup.cs | 26 +-- Crow/src/Widgets/Widget.cs | 82 ++++--- Samples/ShowCase/ShowCase.csproj | 4 + Samples/ShowCase/main.cs | 28 +-- Samples/common/ui/Interfaces/Divers/0.crow | 4 +- .../common/ui/Interfaces/basicTests/7.crow | 144 ------------ 24 files changed, 520 insertions(+), 616 deletions(-) create mode 100755 Crow/Templates/ColorPicker2.template create mode 100755 Crow/Templates/EnumSelector.template create mode 100644 Crow/src/Widgets/ColorPicker2.cs delete mode 100755 Samples/common/ui/Interfaces/basicTests/7.crow diff --git a/Crow/Crow.csproj b/Crow/Crow.csproj index 8e8934e9..3519cc7f 100644 --- a/Crow/Crow.csproj +++ b/Crow/Crow.csproj @@ -5,7 +5,7 @@ Crow - 0.8.4.1 + 0.8.6 C.R.O.W. is a widget toolkit and rendering engine entirely developed in C# with templates, styles, compositing, and bindings. false false @@ -20,21 +20,14 @@ https://opensource.org/licenses/MIT https://jpbruyere.github.io/Crow/images/crow.png Copyright 2013-2019 - xcb backend - DESIGN_MODE + + DESIGN_MODE full - _DEBUG_DISPOSE;TRACE;_DEBUG_BINDING;DESIGN_MODE;_DEBUG_CLIP_RECTANGLE;_DEBUG_FOCUS;_DEBUG_DRAGNDROP;NET471;NETFRAMEWORK;NET461;DEBUG;NETSTANDARD;NETSTANDARD2_0 + _DEBUG_DISPOSE;TRACE;_DEBUG_BINDING;DESIGN_MODE;_DEBUG_CLIP_RECTANGLE;DEBUG_FOCUS;_DEBUG_DRAGNDROP;NET471;NET461;NETFRAMEWORK;NET472;DEBUG;NETSTANDARD;NETSTANDARD2_0 true - diff --git a/Crow/Default.style b/Crow/Default.style index 4108a1fb..8865efc0 100644 --- a/Crow/Default.style +++ b/Crow/Default.style @@ -226,4 +226,21 @@ EnumSelector { Orientation = "Vertical"; Height = "Fit"; Spacing = "0"; +} +labColor { + Margin="2"; + Width="Fit"; +} +labColorV { + Margin="1"; + Width="Fit"; +} +ColorSlider { + Focusable="true"; + Height="12"; + Margin="0"; +} +ColorPicker { + Height="Fit"; + MinimumSize="200,100"; } \ No newline at end of file diff --git a/Crow/Templates/ColorPicker.template b/Crow/Templates/ColorPicker.template index b4355517..10976523 100755 --- a/Crow/Templates/ColorPicker.template +++ b/Crow/Templates/ColorPicker.template @@ -1,44 +1,42 @@  - - - - - - - - - + + + + + + + + + + + + + + + + + + diff --git a/Crow/Templates/ColorPicker2.template b/Crow/Templates/ColorPicker2.template new file mode 100755 index 00000000..b4355517 --- /dev/null +++ b/Crow/Templates/ColorPicker2.template @@ -0,0 +1,44 @@ + + + + + + + + + + + + diff --git a/Crow/Templates/EnumSelector.template b/Crow/Templates/EnumSelector.template new file mode 100755 index 00000000..cc4e239d --- /dev/null +++ b/Crow/Templates/EnumSelector.template @@ -0,0 +1,5 @@ + + + + + diff --git a/Crow/Templates/GroupBox.template b/Crow/Templates/GroupBox.template index 0540b552..f10b1c4c 100755 --- a/Crow/Templates/GroupBox.template +++ b/Crow/Templates/GroupBox.template @@ -3,7 +3,7 @@ - + diff --git a/Crow/Templates/Spinner.template b/Crow/Templates/Spinner.template index 613b6229..1bac0d1d 100755 --- a/Crow/Templates/Spinner.template +++ b/Crow/Templates/Spinner.template @@ -5,7 +5,7 @@ - + diff --git a/Crow/src/CompilerServices/CompilerServices.cs b/Crow/src/CompilerServices/CompilerServices.cs index d1751920..88498951 100644 --- a/Crow/src/CompilerServices/CompilerServices.cs +++ b/Crow/src/CompilerServices/CompilerServices.cs @@ -322,10 +322,7 @@ namespace Crow.IML return; } - if (miDest.MemberType == MemberTypes.Property) - destType =(miDest as PropertyInfo).PropertyType; - else if (miDest.MemberType == MemberTypes.Field) - destType =(miDest as FieldInfo).FieldType; + destType = CompilerServices.GetMemberInfoType (miDest); try { if (value != null) { @@ -1050,6 +1047,12 @@ namespace Crow.IML il.Emit (OpCodes.Ldfld, fi); } + + //helper to get member info underlying type. + internal static Type GetMemberInfoType (MemberInfo mi) => + mi.MemberType == MemberTypes.Field ? (mi as FieldInfo).FieldType : + mi.MemberType == MemberTypes.Property ? (mi as PropertyInfo).PropertyType : + mi.MemberType == MemberTypes.Method ? (mi as MethodInfo).ReturnType : null; } } diff --git a/Crow/src/Instantiator.cs b/Crow/src/Instantiator.cs index 3268d2e6..1dcb989a 100644 --- a/Crow/src/Instantiator.cs +++ b/Crow/src/Instantiator.cs @@ -1160,7 +1160,7 @@ namespace Crow.IML { typeof (void), CompilerServices.argsBoundValueChange, true); - il = dm.GetILGenerator (32); + il = dm.GetILGenerator (64); System.Reflection.Emit.Label endMethod = il.DefineLabel (); @@ -1173,7 +1173,7 @@ namespace Crow.IML { il.Emit (OpCodes.Ldfld, CompilerServices.fiVCMbName); //test if it's the expected one - il.Emit (OpCodes.Ldstr, bindingDef.TargetMember); + il.Emit (OpCodes.Ldstr, bindingDef.HasUnresolvedTargetName ? $"{bindingDef.TargetName}.{bindingDef.TargetMember}" : bindingDef.TargetMember); il.Emit (OpCodes.Ldc_I4_4);//StringComparison.Ordinal il.Emit (OpCodes.Call, CompilerServices.stringEquals); il.Emit (OpCodes.Brfalse, endMethod); @@ -1218,6 +1218,7 @@ namespace Crow.IML { il.DeclareLocal (typeof(object));//used for checking propery less bindings il.DeclareLocal (typeof(MemberInfo));//used for checking propery less bindings il.DeclareLocal (typeof (object));//new datasource store, save one field access + il.DeclareLocal (typeof (MemberInfo));//used for binding with datasource.object.member (2 levels) System.Reflection.Emit.Label cancel = il.DefineLabel (); System.Reflection.Emit.Label newDSIsNull = il.DefineLabel (); System.Reflection.Emit.Label cancelInit = il.DefineLabel (); @@ -1238,9 +1239,19 @@ namespace Crow.IML { il.Emit (OpCodes.Brfalse, newDSIsNull);//new ds is null } -#region fetch initial Value + #region fetch initial Value if (!string.IsNullOrEmpty(bindingDef.TargetMember)){ - il.Emit (OpCodes.Ldloc_2); + il.Emit (OpCodes.Ldloc_2); + if (bindingDef.HasUnresolvedTargetName) { + il.Emit (OpCodes.Ldstr, bindingDef.TargetName);//load parent object + il.Emit (OpCodes.Call, CompilerServices.miGetMembIinfoWithRefx); + il.Emit (OpCodes.Stloc_3); + il.Emit (OpCodes.Ldloc_3); + il.Emit (OpCodes.Brfalse, cancelInit);//may be propertyLessBinding + il.Emit (OpCodes.Ldloc_2);//load datasource + il.Emit (OpCodes.Ldloc_3);//load first memberInfo + il.Emit (OpCodes.Call, CompilerServices.miGetValWithRefx);//get first member level + } il.Emit (OpCodes.Ldstr, bindingDef.TargetMember);//load member name il.Emit (OpCodes.Call, CompilerServices.miGetMembIinfoWithRefx); il.Emit (OpCodes.Stloc_1);//save memberInfo @@ -1251,12 +1262,16 @@ namespace Crow.IML { il.Emit (OpCodes.Ldarg_1);//load source of dataSourceChanged which is the dest instance il.Emit (OpCodes.Ldloc_2);//load new datasource if (!string.IsNullOrEmpty(bindingDef.TargetMember)){ + if (bindingDef.HasUnresolvedTargetName) { + il.Emit (OpCodes.Ldloc_3); + il.Emit (OpCodes.Call, CompilerServices.miGetValWithRefx);//get first member level + } il.Emit (OpCodes.Ldloc_1);//push mi for value fetching il.Emit (OpCodes.Call, CompilerServices.miGetValWithRefx); } CompilerServices.emitConvert (il, piSource.PropertyType); il.Emit (OpCodes.Callvirt, piSource.GetSetMethod ()); -#endregion + #endregion if (!string.IsNullOrEmpty(bindingDef.TargetMember)){ il.MarkLabel(cancelInit); @@ -1268,7 +1283,7 @@ namespace Crow.IML { il.Emit(OpCodes.Ldarg_0);//load ref to this instanciator onto the stack il.Emit (OpCodes.Ldarg_1);//load datasource change source il.Emit (OpCodes.Ldloc_2);//load new datasource - il.Emit(OpCodes.Ldc_I4, dmVC);//load index of dynmathod + il.Emit(OpCodes.Ldc_I4, dmVC);//load index of dynMethod il.Emit (OpCodes.Call, CompilerServices.miDSChangeEmitHelper); il.MarkLabel (cancel); @@ -1277,7 +1292,8 @@ namespace Crow.IML { il.Emit (OpCodes.Ldarg_1);//arg1: dataSourceChange source, the origine of the binding il.Emit (OpCodes.Ldstr, bindingDef.SourceMember);//arg2: orig member il.Emit (OpCodes.Ldloc_2);//arg3: new datasource - il.Emit (OpCodes.Ldstr, bindingDef.TargetMember);//arg4: dest member + il.Emit (OpCodes.Ldstr, bindingDef.HasUnresolvedTargetName ? + $"{bindingDef.TargetName}.{bindingDef.TargetMember}" : bindingDef.TargetMember);//arg4: dest member il.Emit (OpCodes.Call, CompilerServices.miDSReverseBinding); } @@ -1297,6 +1313,16 @@ namespace Crow.IML { Debug.WriteLine("\tDataSource Changed: " + dm.Name); #endif } + + static void emitSetValue (ILGenerator il, MemberInfo mi) + { + if (mi.MemberType == MemberTypes.Field) + il.Emit (OpCodes.Stfld, mi as FieldInfo); + else if (mi.MemberType == MemberTypes.Property) + il.Emit (OpCodes.Callvirt, (mi as PropertyInfo).GetSetMethod ()); + else + throw new NotImplementedException (); + } /// /// Two way binding for datasource, graphicObj=>dataSource link, datasource value has priority /// and will be set as init for source property (in emitDataSourceBindings func) @@ -1309,12 +1335,18 @@ namespace Crow.IML { Type tOrig = orig.GetType (); Type tDest = dest.GetType (); PropertyInfo piOrig = tOrig.GetProperty (origMember); - PropertyInfo piDest = tDest.GetProperty (destMember); - - if (piDest == null) { - Debug.WriteLine ("Member '{0}' not found in new DataSource '{1}' of '{2}'", destMember, dest, orig); - return; + List miDests = new List (); + Type curType = tDest; + foreach (string m in destMember.Split('.')) { + MemberInfo miDest = curType.GetMember (m).FirstOrDefault (); + if (miDest == null) { + Debug.WriteLine ($"Member '{destMember}' not found in new DataSource '{dest}' of '{orig}'"); + return; + } + miDests.Add (miDest); + curType = CompilerServices.GetMemberInfoType (miDest); } + #if DEBUG_BINDING Debug.WriteLine ("DS Reverse binding: Member '{0}' found in new DS '{1}' of '{2}'", destMember, dest, orig); #endif @@ -1322,7 +1354,7 @@ namespace Crow.IML { #region ValueChanged emit DynamicMethod dm = new DynamicMethod ("dyn_valueChanged" + NewId, typeof (void), CompilerServices.argsBoundValueChange, true); - ILGenerator il = dm.GetILGenerator (256); + ILGenerator il = dm.GetILGenerator (64); System.Reflection.Emit.Label endMethod = il.DefineLabel (); @@ -1340,20 +1372,44 @@ namespace Crow.IML { //set destination member with valueChanged new value //load destination ref il.Emit (OpCodes.Ldarg_0); + for (int i = 0; i < miDests.Count - 1; i++) { + if (miDests [i].MemberType == MemberTypes.Field) + il.Emit (OpCodes.Ldflda, miDests [i] as FieldInfo); + else if (miDests [i].MemberType == MemberTypes.Property) { + PropertyInfo pi = miDests [i] as PropertyInfo; + if (pi.PropertyType.IsValueType) { + il.Emit (OpCodes.Dup);//dup parent for calling property set afterward + il.Emit (OpCodes.Callvirt, pi.GetGetMethod ()); + il.Emit (OpCodes.Box, pi.PropertyType); + il.Emit (OpCodes.Dup);//dup boxed valueType, should unbox before setting parent + } else + il.Emit (OpCodes.Callvirt, pi.GetGetMethod ()); + } else + throw new NotImplementedException (); + } + //load new value onto the stack il.Emit (OpCodes.Ldarg_2); il.Emit (OpCodes.Ldfld, CompilerServices.fiVCNewValue); - //if (piOrig.PropertyType != piDest.PropertyType) - CompilerServices.emitConvert (il, piOrig.PropertyType, piDest.PropertyType); + CompilerServices.emitConvert (il, piOrig.PropertyType, curType); - il.Emit (OpCodes.Callvirt, piDest.GetSetMethod ()); + emitSetValue (il, miDests.Last ()); + for (int i = miDests.Count -2; i >= 0; i--) { + if (miDests [i].MemberType != MemberTypes.Property) + continue; + PropertyInfo pi = miDests [i] as PropertyInfo; + if (!pi.PropertyType.IsValueType) + continue; + il.Emit (OpCodes.Ldobj, pi.PropertyType); + il.Emit (OpCodes.Callvirt, pi.GetSetMethod ());//updating parent + } il.MarkLabel (endMethod); il.Emit (OpCodes.Ret); -#endregion - - orig.ValueChanged += (EventHandler)dm.CreateDelegate (typeof(EventHandler), dest); + #endregion + EventHandler tmp = (EventHandler)dm.CreateDelegate (typeof (EventHandler), dest); + orig.ValueChanged += tmp; } #endregion diff --git a/Crow/src/Interface.cs b/Crow/src/Interface.cs index 2ef26fce..cb836a9f 100644 --- a/Crow/src/Interface.cs +++ b/Crow/src/Interface.cs @@ -977,7 +977,7 @@ namespace Crow MouseCursor cursor = MouseCursor.Arrow; public MouseState Mouse; - Stopwatch lastMouseDown = new Stopwatch (), mouseRepeatTimer = new Stopwatch (); + Stopwatch lastMouseDown = Stopwatch.StartNew (), mouseRepeatTimer = new Stopwatch (); bool doubleClickTriggered; //next mouse up will trigger a double click //int mouseRepeatCount; MouseButtonEventArgs lastMouseDownEvent; diff --git a/Crow/src/Widgets/CheckBox.cs b/Crow/src/Widgets/CheckBox.cs index 7dc2b073..7a6e5d45 100644 --- a/Crow/src/Widgets/CheckBox.cs +++ b/Crow/src/Widgets/CheckBox.cs @@ -1,28 +1,6 @@ -// -// CheckBox.cs +// Copyright (c) 2013-2019 Bruyère Jean-Philippe jp_bruyere@hotmail.com // -// Author: -// Jean-Philippe Bruyère -// -// Copyright (c) 2013-2017 Jean-Philippe Bruyère -// -// 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. +// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) using System; using System.ComponentModel; diff --git a/Crow/src/Widgets/ColorPicker.cs b/Crow/src/Widgets/ColorPicker.cs index 92837717..72f77dea 100644 --- a/Crow/src/Widgets/ColorPicker.cs +++ b/Crow/src/Widgets/ColorPicker.cs @@ -1,28 +1,6 @@ -// -// ColorPicker.cs +// Copyright (c) 2013-2019 Bruyère Jean-Philippe jp_bruyere@hotmail.com // -// Author: -// Jean-Philippe Bruyère -// -// Copyright (c) 2013-2017 Jean-Philippe Bruyère -// -// 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. +// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) using System; using System.Xml.Serialization; @@ -40,189 +18,18 @@ namespace Crow public ColorPicker (Interface iface) : base(iface){} #endregion - const double div = 255.0; - const double colDiv = 1.0 / div; + Color currentColor; - Color curColor; - double h,s,v; - - - public virtual double R { - get { return Math.Round(curColor.R * div); } - set { - if (R == value) - return; - curColor.R = value * colDiv; - NotifyValueChanged ("R", R); - hsvFromRGB (); - notifyCurColorHasChanged (); - } - } - - public virtual double G { - get { return Math.Round(curColor.G * div); } - set { - if (G == value) - return; - curColor.G = value * colDiv; - NotifyValueChanged ("G", G); - notifyCurColorHasChanged (); - hsvFromRGB (); - } - } - - public virtual double B { - get { return Math.Round(curColor.B * div); } - set { - if (B == value) - return; - curColor.B = value * colDiv; - NotifyValueChanged ("B", B); - notifyCurColorHasChanged (); - hsvFromRGB (); - } - } - - public virtual double A { - get { return Math.Round(curColor.A * div); } - set { - if (A == value) - return; - curColor.A = value * colDiv; - NotifyValueChanged ("A", A); - notifyCurColorHasChanged (); - hsvFromRGB (); - } - } - - public virtual double H { - get { return Math.Round (h, 3); } - set { - if (H == value) - return; - h = value; - NotifyValueChanged ("H", H); - rgbFromHSV (); - } - } - - public virtual double S { - get { return Math.Round (s, 2); } + [DefaultValue("Black")] + public virtual Color CurrentColor { + get => currentColor; set { - if (s == value) + if (currentColor == value) return; - s = value; - NotifyValueChanged ("S", S); - rgbFromHSV (); + currentColor = value; + NotifyValueChanged ("CurrentColor", currentColor); } } - - public virtual double V { - get { return Math.Round (v, 2); } - set { - if (v == value) - return; - v = value; - NotifyValueChanged ("V", V); - rgbFromHSV (); - } - } - - - public virtual Fill SelectedColor { - get { return new SolidColor(curColor); } - set { - if (value == null) - curColor = Color.White; - else if (value is SolidColor) { - Color c = (value as SolidColor).color; - if (curColor == c) - return; - curColor = c; - } - notifyCurColorHasChanged (); - notifyRGBAHasChanged (); - hsvFromRGB (); - } - } - - public virtual Color SelectedRawColor { - get { return curColor; } - set { - if (curColor == value) - return; - curColor = value; - notifyCurColorHasChanged (); - notifyRGBAHasChanged (); - hsvFromRGB (); - } - } - void notifyCurColorHasChanged(){ - NotifyValueChanged ("SelectedColor", SelectedColor); - NotifyValueChanged ("SelectedRawColor", curColor); - string n = curColor.ToString (); - if (char.IsLetter(n[0])) - NotifyValueChanged ("SelectedColorName", n); - else - NotifyValueChanged ("SelectedColorName", "-"); - string tmp = ((int)Math.Round (R)).ToString ("X2") + - ((int)Math.Round (G)).ToString ("X2") + - ((int)Math.Round (B)).ToString ("X2"); - if (curColor.A < 1.0) - tmp += ((int)Math.Round (A)).ToString ("X2"); - NotifyValueChanged ("HexColor", tmp); - } - void notifyRGBAHasChanged(){ - NotifyValueChanged ("R", R); - NotifyValueChanged ("G", G); - NotifyValueChanged ("B", B); - NotifyValueChanged ("A", A); - } - void notifyHSVHasChanged(){ - NotifyValueChanged ("H", H); - NotifyValueChanged ("S", S); - NotifyValueChanged ("V", V); - } - void hsvFromRGB(){ - Color c = curColor; - c.ResetName (); - double min = Math.Min (c.R, Math.Min (c.G, c.B)); //Min. value of RGB - double max = Math.Max (c.R, Math.Max (c.G, c.B)); //Max. value of RGB - double diff = max - min; //Delta RGB value - - v = max; - - if ( diff == 0 )//This is a gray, no chroma... - { - h = 0; - s = 0; - }else{//Chromatic data... - s = diff / max; - - double diffR = (((max - c.R) / 6.0) + (diff / 2.0)) / diff; - double diffG = (((max - c.G) / 6.0) + (diff / 2.0)) / diff; - double diffB = (((max - c.B) / 6.0) + (diff / 2.0)) / diff; - - if (c.R == max) - h = diffB - diffG; - else if (c.G == max) - h = (1.0 / 3.0) + diffR - diffB; - else if (c.B == max) - h = (2.0 / 3.0) + diffG - diffR; - - if (h < 0) - h += 1; - if (h > 1) - h -= 1; - - } - notifyHSVHasChanged (); - } - void rgbFromHSV(){ - curColor = Color.FromHSV (h, v, s, curColor.A); - notifyCurColorHasChanged (); - notifyRGBAHasChanged (); - } } } diff --git a/Crow/src/Widgets/ColorPicker2.cs b/Crow/src/Widgets/ColorPicker2.cs new file mode 100644 index 00000000..f5b457c1 --- /dev/null +++ b/Crow/src/Widgets/ColorPicker2.cs @@ -0,0 +1,206 @@ +// Copyright (c) 2013-2019 Bruyère Jean-Philippe jp_bruyere@hotmail.com +// +// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) + +using System; +using System.Xml.Serialization; +using System.ComponentModel; + +namespace Crow +{ + /// + /// templated color selector control + /// + public class ColorPicker2 : TemplatedControl + { + #region CTOR + protected ColorPicker2() : base(){} + public ColorPicker2 (Interface iface) : base(iface){} + #endregion + + const double div = 255.0; + const double colDiv = 1.0 / div; + + Color curColor; + double h,s,v; + + + public virtual double R { + get { return Math.Round(curColor.R * div); } + set { + if (R == value) + return; + curColor.R = value * colDiv; + NotifyValueChanged ("R", R); + hsvFromRGB (); + notifyCurColorHasChanged (); + } + } + + public virtual double G { + get { return Math.Round(curColor.G * div); } + set { + if (G == value) + return; + curColor.G = value * colDiv; + NotifyValueChanged ("G", G); + notifyCurColorHasChanged (); + hsvFromRGB (); + } + } + + public virtual double B { + get { return Math.Round(curColor.B * div); } + set { + if (B == value) + return; + curColor.B = value * colDiv; + NotifyValueChanged ("B", B); + notifyCurColorHasChanged (); + hsvFromRGB (); + } + } + + public virtual double A { + get { return Math.Round(curColor.A * div); } + set { + if (A == value) + return; + curColor.A = value * colDiv; + NotifyValueChanged ("A", A); + notifyCurColorHasChanged (); + hsvFromRGB (); + } + } + + public virtual double H { + get { return Math.Round (h, 3); } + set { + if (H == value) + return; + h = value; + NotifyValueChanged ("H", H); + rgbFromHSV (); + } + } + + public virtual double S { + get { return Math.Round (s, 2); } + set { + if (s == value) + return; + s = value; + NotifyValueChanged ("S", S); + rgbFromHSV (); + } + } + + public virtual double V { + get { return Math.Round (v, 2); } + set { + if (v == value) + return; + v = value; + NotifyValueChanged ("V", V); + rgbFromHSV (); + } + } + + + public virtual Fill SelectedColor { + get { return new SolidColor(curColor); } + set { + if (value == null) + curColor = Color.White; + else if (value is SolidColor) { + Color c = (value as SolidColor).color; + if (curColor == c) + return; + curColor = c; + } + notifyCurColorHasChanged (); + notifyRGBAHasChanged (); + hsvFromRGB (); + } + } + + public virtual Color SelectedRawColor { + get { return curColor; } + set { + if (curColor == value) + return; + curColor = value; + notifyCurColorHasChanged (); + notifyRGBAHasChanged (); + hsvFromRGB (); + } + } + void notifyCurColorHasChanged(){ + NotifyValueChanged ("SelectedColor", SelectedColor); + NotifyValueChanged ("SelectedRawColor", curColor); + string n = curColor.ToString (); + if (char.IsLetter(n[0])) + NotifyValueChanged ("SelectedColorName", n); + else + NotifyValueChanged ("SelectedColorName", "-"); + string tmp = ((int)Math.Round (R)).ToString ("X2") + + ((int)Math.Round (G)).ToString ("X2") + + ((int)Math.Round (B)).ToString ("X2"); + if (curColor.A < 1.0) + tmp += ((int)Math.Round (A)).ToString ("X2"); + NotifyValueChanged ("HexColor", tmp); + } + void notifyRGBAHasChanged(){ + NotifyValueChanged ("R", R); + NotifyValueChanged ("G", G); + NotifyValueChanged ("B", B); + NotifyValueChanged ("A", A); + } + void notifyHSVHasChanged(){ + NotifyValueChanged ("H", H); + NotifyValueChanged ("S", S); + NotifyValueChanged ("V", V); + } + void hsvFromRGB(){ + Color c = curColor; + c.ResetName (); + double min = Math.Min (c.R, Math.Min (c.G, c.B)); //Min. value of RGB + double max = Math.Max (c.R, Math.Max (c.G, c.B)); //Max. value of RGB + double diff = max - min; //Delta RGB value + + v = max; + + if ( diff == 0 )//This is a gray, no chroma... + { + h = 0; + s = 0; + }else{//Chromatic data... + s = diff / max; + + double diffR = (((max - c.R) / 6.0) + (diff / 2.0)) / diff; + double diffG = (((max - c.G) / 6.0) + (diff / 2.0)) / diff; + double diffB = (((max - c.B) / 6.0) + (diff / 2.0)) / diff; + + if (c.R == max) + h = diffB - diffG; + else if (c.G == max) + h = (1.0 / 3.0) + diffR - diffB; + else if (c.B == max) + h = (2.0 / 3.0) + diffG - diffR; + + if (h < 0) + h += 1; + if (h > 1) + h -= 1; + + } + notifyHSVHasChanged (); + } + void rgbFromHSV(){ + curColor = Color.FromHSV (h, v, s, curColor.A); + notifyCurColorHasChanged (); + notifyRGBAHasChanged (); + } + } +} + diff --git a/Crow/src/Widgets/EnumSelector.cs b/Crow/src/Widgets/EnumSelector.cs index 932e90e2..576eb953 100644 --- a/Crow/src/Widgets/EnumSelector.cs +++ b/Crow/src/Widgets/EnumSelector.cs @@ -8,15 +8,26 @@ using System.ComponentModel; namespace Crow { /// - /// Convenient widget for selecting value from enum + /// Convenient widget for selecting value among enum values. This is a templated control + /// expecting a 'Group' widget named 'Content' inside the template to handle the enum values display. /// - public class EnumSelector : GenericStack + public class EnumSelector : TemplatedControl { #region CTOR protected EnumSelector () : base(){} public EnumSelector (Interface iface) : base(iface){} #endregion + Group enumValueContainer; + + protected override void loadTemplate (Widget template = null) + { + base.loadTemplate (template); + enumValueContainer = this.child.FindByName ("Content") as Group; + if (enumValueContainer == null) + throw new Exception("EnumSelector template MUST contain a 'Group' named 'Content'"); + } + #region private fields Enum enumValue; Type enumType; @@ -37,20 +48,22 @@ namespace Crow if (enumValue != null) { if (enumType != enumValue.GetType ()) { - ClearChildren (); + enumValueContainer.ClearChildren (); enumType = enumValue.GetType (); foreach (string en in enumType.GetEnumNames ()) { RadioButton rb = new RadioButton (IFace); rb.Caption = en; - if (enumValue.ToString() == en) + rb.Fit = true; + rb.LogicalParent = this; + if (enumValue.ToString () == en) rb.IsChecked = true; - rb.Checked += (sender, e) => (((RadioButton)sender).Parent as EnumSelector).EnumValue = (Enum)Enum.Parse (enumType, (sender as RadioButton).Caption); - AddChild (rb); - RegisterForLayouting (LayoutingType.All); + rb.Checked += (sender, e) => (((RadioButton)sender).LogicalParent as EnumSelector).EnumValue = (Enum)Enum.Parse (enumType, (sender as RadioButton).Caption); + enumValueContainer.AddChild (rb); } + } - } else - ClearChildren (); + } else + enumValueContainer.ClearChildren (); NotifyValueChanged ("EnumValue", enumValue); RegisterForRedraw (); diff --git a/Crow/src/Widgets/GenericStack.cs b/Crow/src/Widgets/GenericStack.cs index 51897487..7d1facf0 100644 --- a/Crow/src/Widgets/GenericStack.cs +++ b/Crow/src/Widgets/GenericStack.cs @@ -1,28 +1,6 @@ -// -// GenericStack.cs +// Copyright (c) 2013-2019 Bruyère Jean-Philippe jp_bruyere@hotmail.com // -// Author: -// Jean-Philippe Bruyère -// -// Copyright (c) 2013-2017 Jean-Philippe Bruyère -// -// 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. +// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) using System.ComponentModel; using System.Diagnostics; diff --git a/Crow/src/Widgets/Group.cs b/Crow/src/Widgets/Group.cs index a0ae35b2..a992e5af 100644 --- a/Crow/src/Widgets/Group.cs +++ b/Crow/src/Widgets/Group.cs @@ -91,6 +91,13 @@ namespace Crow set { _multiSelect = value; } } public virtual void AddChild(Widget g){ +#if DEBUG + if (disposed) { + Console.WriteLine ($"AddChild ({g}) in disposed Widget: {this}\n{System.Environment.StackTrace}"); + return; + } +#endif + childrenRWLock.EnterWriteLock(); g.Parent = this; @@ -132,6 +139,12 @@ namespace Crow child.Dispose (); } public virtual void InsertChild (int idx, Widget g) { +#if DEBUG + if (disposed) { + Console.WriteLine ($"InsertChild ({idx},{g}) in disposed Widget: {this}\n{System.Environment.StackTrace}"); + return; + } +#endif childrenRWLock.EnterWriteLock (); g.Parent = this; @@ -164,7 +177,7 @@ namespace Crow resetChildrenMaxSize (); - this.RegisterForLayouting (LayoutingType.Sizing); + RegisterForLayouting (LayoutingType.Sizing); ChildrenCleared.Raise (this, new EventArgs ()); } public override void OnDataSourceChanged (object sender, DataSourceChangeEventArgs e) @@ -463,8 +476,10 @@ namespace Crow protected override void Dispose (bool disposing) { if (disposing) { + childrenRWLock.EnterReadLock (); foreach (Widget c in children) c.Dispose (); + childrenRWLock.ExitReadLock (); } base.Dispose (disposing); } diff --git a/Crow/src/Widgets/Label.cs b/Crow/src/Widgets/Label.cs index f597f5d7..a03a0bdc 100644 --- a/Crow/src/Widgets/Label.cs +++ b/Crow/src/Widgets/Label.cs @@ -1,41 +1,16 @@ -// -// Label.cs +// Copyright (c) 2013-2019 Bruyère Jean-Philippe jp_bruyere@hotmail.com // -// Author: -// Jean-Philippe Bruyère -// -// Copyright (c) 2013-2017 Jean-Philippe Bruyère -// -// 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. +// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) using System; using System.Collections.Generic; using System.Linq; -using System.Diagnostics; using Crow.Cairo; using System.Text.RegularExpressions; -using System.Xml.Serialization; using System.ComponentModel; -namespace Crow -{ - public class Label : Widget +namespace Crow { + public class Label : Widget { #region CTOR protected Label () : base(){} diff --git a/Crow/src/Widgets/PrivateContainer.cs b/Crow/src/Widgets/PrivateContainer.cs index 7909a6d9..9f2dbdc5 100644 --- a/Crow/src/Widgets/PrivateContainer.cs +++ b/Crow/src/Widgets/PrivateContainer.cs @@ -1,28 +1,6 @@ -// -// PrivateContainer.cs +// Copyright (c) 2013-2019 Bruyère Jean-Philippe // -// Author: -// Jean-Philippe Bruyère -// -// Copyright (c) 2013-2017 Jean-Philippe Bruyère -// -// 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. +// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) using System; using System.Xml.Serialization; diff --git a/Crow/src/Widgets/TemplatedGroup.cs b/Crow/src/Widgets/TemplatedGroup.cs index da87b91e..42e4dc06 100644 --- a/Crow/src/Widgets/TemplatedGroup.cs +++ b/Crow/src/Widgets/TemplatedGroup.cs @@ -1,28 +1,6 @@ -// -// TemplatedGroup.cs +// Copyright (c) 2013-2019 Bruyère Jean-Philippe jp_bruyere@hotmail.com // -// Author: -// Jean-Philippe Bruyère -// -// Copyright (c) 2013-2017 Jean-Philippe Bruyère -// -// 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. +// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) using System; using System.Collections; diff --git a/Crow/src/Widgets/Widget.cs b/Crow/src/Widgets/Widget.cs index d6c3a968..7fdec5a1 100644 --- a/Crow/src/Widgets/Widget.cs +++ b/Crow/src/Widgets/Widget.cs @@ -1,28 +1,6 @@ -// -// GraphicObject.cs +// Copyright (c) 2013-2019 Bruyère Jean-Philippe jp_bruyere@hotmail.com // -// Author: -// Jean-Philippe Bruyère -// -// Copyright (c) 2013-2017 Jean-Philippe Bruyère -// -// 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. +// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) using System; using System.Collections.Generic; @@ -163,13 +141,13 @@ namespace Crow GC.SuppressFinalize(this); } ~Widget(){ - Console.WriteLine(this.ToString() + " not disposed by user"); + Debug.WriteLine(this.ToString() + " not disposed by user"); Dispose(false); } protected virtual void Dispose(bool disposing){ if (disposed){ #if DEBUG_DISPOSE - Console.WriteLine ("Trying to dispose already disposed obj: {0}", this.ToString()); + Debug.WriteLine ("Trying to dispose already disposed obj: {0}", this.ToString()); #endif return; } @@ -1408,6 +1386,10 @@ namespace Crow /// /// Clip rectangle public virtual void RegisterClip(Rectangle clip){ + if (disposed) { + Debug.WriteLine ($"Trying to register clip for disposed Widget: {this}\n{System.Environment.StackTrace}"); + return; + } #if DEBUG_LOG DbgEvent dbgEvt = DebugLog.AddEvent(DbgEvtType.GORegisterClip, this); #endif @@ -1417,6 +1399,10 @@ namespace Crow r.Width -= r.Right - cb.Right; if (r.Bottom > cb.Bottom) r.Height -= r.Bottom - cb.Bottom; + if (r.Width < 0 || r.Height < 0) { + Debug.WriteLine ($"Invalid clip: {clip}:{r} hnd:{this}");//\n{Environment.StackTrace}"); + return; + } if (cacheEnabled && !IsDirty) Clipping.UnionRectangle (r); if (Parent == null) @@ -1433,6 +1419,12 @@ namespace Crow [MethodImpl(MethodImplOptions.AggressiveInlining)] public void RegisterForGraphicUpdate () { +#if DEBUG + if (disposed) { + Console.WriteLine ($"RegisterForGraphicUpdate for disposed Widget: {this}\n{System.Environment.StackTrace}"); + return; + } +#endif IsDirty = true; if (Width.IsFit || Height.IsFit) RegisterForLayouting (LayoutingType.Sizing); @@ -1443,6 +1435,13 @@ namespace Crow [MethodImpl(MethodImplOptions.AggressiveInlining)] public void RegisterForRedraw () { +#if DEBUG + if (disposed) { + Console.WriteLine ($"RegisterForRedraw for disposed Widget: {this}\n{System.Environment.StackTrace}"); + return; + } +#endif + IsDirty = true; if (RegisteredLayoutings == LayoutingType.None) IFace.EnqueueForRepaint (this); @@ -1461,6 +1460,12 @@ namespace Crow } public virtual bool ArrangeChildren { get { return false; } } public virtual void RegisterForLayouting(LayoutingType layoutType){ +#if DEBUG + if (disposed) { + Console.WriteLine ($"RegisterForLayouting({layoutType}) for disposed Widget: {this}\n{System.Environment.StackTrace}"); + return; + } +#endif if (Parent == null) return; lock (IFace.LayoutMutex) { @@ -1744,12 +1749,29 @@ namespace Crow /// of the widget public virtual void Paint (ref Context ctx) { - #if DEBUG_LOG +#if DEBUG_LOG DebugLog.AddEvent(DbgEvtType.GOPaint, this); - #endif +#endif //TODO:this test should not be necessary - if (Slot.Height < 0 || Slot.Width < 0 || parent == null) - return; + + if (disposed || Slot.Height < 0 || Slot.Width < 0 || parent == null){ +#if DEBUG + Console.ForegroundColor = ConsoleColor.Red; + if (disposed) + Console.WriteLine ($"Paint disposed widget: {this}"); + Console.ForegroundColor = ConsoleColor.DarkRed; + if (Slot.Height < 0 || Slot.Width < 0) + Console.WriteLine ($"Paint slot invalid ({Slot}): {this}"); + Console.ForegroundColor = ConsoleColor.DarkMagenta; + if (parent == null) + Console.WriteLine ($"Paint with parent == null: {this}"); + Console.ForegroundColor = ConsoleColor.Magenta; + if (!isVisible) + Console.WriteLine ($"Paint invisible widget: {this}"); + Console.ForegroundColor = ConsoleColor.Gray; +#endif + return; + } lock (this) { if (cacheEnabled) { if (Slot.Width > Interface.MaxCacheSize || Slot.Height > Interface.MaxCacheSize) diff --git a/Samples/ShowCase/ShowCase.csproj b/Samples/ShowCase/ShowCase.csproj index 97dd9690..60dbd9ec 100644 --- a/Samples/ShowCase/ShowCase.csproj +++ b/Samples/ShowCase/ShowCase.csproj @@ -3,6 +3,7 @@ net471 Exe false + 0.8.0 @@ -27,4 +28,7 @@ Interfaces\%(RecursiveDir)%(Filename)%(Extension) + + + diff --git a/Samples/ShowCase/main.cs b/Samples/ShowCase/main.cs index a2b263fe..09f86f76 100644 --- a/Samples/ShowCase/main.cs +++ b/Samples/ShowCase/main.cs @@ -1,28 +1,6 @@ +// Copyright (c) 2013-2019 Bruyère Jean-Philippe // -// UIEditor.cs -// -// Author: -// Jean-Philippe Bruyère -// -// Copyright (c) 2013-2017 Jean-Philippe Bruyère -// -// 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. +// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) using System; using Crow; @@ -193,7 +171,7 @@ namespace tests } get { return List2; } } - IList testList = Color.ColorDic.Values.ToList (); + IList testList = Color.ColorDic.Values.OrderBy(c=>c.Hue).ThenBy(c=>c.Value).ToList (); public IList TestList { set { testList = value; diff --git a/Samples/common/ui/Interfaces/Divers/0.crow b/Samples/common/ui/Interfaces/Divers/0.crow index e7cf3e2f..d083946c 100755 --- a/Samples/common/ui/Interfaces/Divers/0.crow +++ b/Samples/common/ui/Interfaces/Divers/0.crow @@ -140,9 +140,9 @@ --> - + -