Browse Source

Expression colours; fix to setters needing to be cleared;

Kenric Nugteren 2 years ago
parent
commit
6490945dac

+ 117 - 16
InABox.Core/DigitalForms/Layouts/DFLayout.cs

@@ -1,5 +1,7 @@
 using System;
 using System.Collections.Generic;
+using System.Drawing;
+using System.Globalization;
 using System.Linq;
 using System.Text;
 using Expressive;
@@ -21,6 +23,11 @@ namespace InABox.Core
         object? GetFieldData(string fieldName, string dataField);
 
         void SetFieldValue(string field, object? value);
+
+        /// <summary>
+        /// Set the background colour for a field.
+        /// </summary>
+        void SetFieldColour(string field, Color? colour = null);
     }
 
     public class DFLayout
@@ -32,7 +39,8 @@ namespace InABox.Core
             Elements = new List<DFLayoutControl>();
 
             Expressions = new Dictionary<string, CoreExpression>();
-            VariableReferences = new Dictionary<string, List<string>>();
+            ColourExpressions = new Dictionary<string, CoreExpression>();
+            VariableReferences = new Dictionary<string, List<Tuple<ReferenceType, string>>>();
         }
 
         public List<string> ColumnWidths { get; }
@@ -41,8 +49,15 @@ namespace InABox.Core
 
         public List<DFLayoutControl> Elements { get; }
 
+        private enum ReferenceType
+        {
+            Value,
+            Colour
+        }
+
         private Dictionary<string, CoreExpression> Expressions;
-        private Dictionary<string, List<string>> VariableReferences;
+        private Dictionary<string, CoreExpression> ColourExpressions;
+        private Dictionary<string, List<Tuple<ReferenceType, string>>> VariableReferences;
         public IDFRenderer? Renderer;
 
         public string SaveLayout()
@@ -136,17 +151,17 @@ namespace InABox.Core
                 RowHeights.AddRange(new[] { "Auto" });
         }
 
-        private void AddVariableReference(string reference, string fieldName)
+        private void AddVariableReference(string reference, string fieldName, ReferenceType referenceType)
         {
             if (reference.Contains('.'))
                 reference = reference.Split('.')[0];
 
             if(!VariableReferences.TryGetValue(reference, out var refs))
             {
-                refs = new List<string>();
+                refs = new List<Tuple<ReferenceType, string>>();
                 VariableReferences[reference] = refs;
             }
-            refs.Add(fieldName);
+            refs.Add(new Tuple<ReferenceType, string>(referenceType, fieldName));
         }
 
         private object? GetFieldValue(string field)
@@ -162,7 +177,7 @@ namespace InABox.Core
             }
         }
 
-        private void EvaluateExpression(string name)
+        private void EvaluateValueExpression(string name)
         {
             var expression = Expressions[name];
             var values = new Dictionary<string, object?>();
@@ -185,18 +200,92 @@ namespace InABox.Core
             }
         }
 
-        public void LoadVariable(DigitalFormVariable variable, DFLayoutField field)
+        private Color? ConvertObjectToColour(object? colour)
         {
-            var properties = variable.LoadProperties(field);
-            if (!string.IsNullOrWhiteSpace(properties?.Expression))
+            if(colour is string str)
             {
-                var expression = new CoreExpression(properties.Expression);
-                foreach (var reference in expression.ReferencedVariables)
+                if (str.StartsWith('#'))
+                {
+                    var trimmed = str.TrimStart('#');
+                    try
+                    {
+                        if (trimmed.Length == 6)
+                        {
+                            return Color.FromArgb(
+                                Int32.Parse(trimmed[..2], NumberStyles.HexNumber),
+                                Int32.Parse(trimmed.Substring(2, 2), NumberStyles.HexNumber),
+                                Int32.Parse(trimmed.Substring(4, 2), NumberStyles.HexNumber));
+                        }
+                        else if (trimmed.Length == 8)
+                        {
+                            return Color.FromArgb(Int32.Parse(trimmed, NumberStyles.HexNumber));
+                        }
+                        else
+                        {
+                            return null;
+                        }
+                    }
+                    catch (Exception e)
+                    {
+                        Logger.Send(LogType.Error, "", $"Error parsing Colour Expression colour '{str}': {e.Message}");
+                        return null;
+                    }
+                }
+                else if(Enum.TryParse<KnownColor>(str, out var result))
                 {
-                    AddVariableReference(reference, field.Name);
+                    return Color.FromKnownColor(result);
                 }
-                Expressions[field.Name] = expression;
+                return null;
+            }
+            return null;
+        }
+
+        private void EvaluateColourExpression(string name)
+        {
+            var expression = ColourExpressions[name];
+            var values = new Dictionary<string, object?>();
+            foreach (var field in expression.ReferencedVariables)
+            {
+                values[field] = GetFieldValue(field);
+            }
+
+            try
+            {
+                var colour = expression?.Evaluate(values);
+                Renderer?.SetFieldColour(name, ConvertObjectToColour(colour));
+            }
+            catch (Exception e)
+            {
+                Logger.Send(LogType.Error, ClientFactory.UserID, $"Error in Expression field '{name}': {CoreUtils.FormatException(e)}");
+            }
+        }
+
+        private void LoadExpression(string fieldName, string? expressionStr, ReferenceType referenceType)
+        {
+            if (string.IsNullOrWhiteSpace(expressionStr))
+                return;
+
+            var expression = new CoreExpression(expressionStr);
+            foreach (var reference in expression.ReferencedVariables)
+            {
+                AddVariableReference(reference, fieldName, referenceType);
             }
+            switch (referenceType)
+            {
+                case ReferenceType.Value:
+                    Expressions[fieldName] = expression;
+                    break;
+                case ReferenceType.Colour:
+                    ColourExpressions[fieldName] = expression;
+                    break;
+            }
+        }
+
+        public void LoadVariable(DigitalFormVariable variable, DFLayoutField field)
+        {
+            var properties = variable.LoadProperties(field);
+            LoadExpression(field.Name, properties?.Expression, ReferenceType.Value);
+            LoadExpression(field.Name, properties?.ColourExpression, ReferenceType.Colour);
         }
 
         public void LoadVariables(IEnumerable<DigitalFormVariable> variables)
@@ -223,9 +312,17 @@ namespace InABox.Core
         public void ChangeField(string fieldName)
         {
             if (!VariableReferences.TryGetValue(fieldName, out var refs)) return;
-            foreach(var reference in refs)
+            foreach(var (refType, refName) in refs)
             {
-                EvaluateExpression(reference);
+                switch (refType)
+                {
+                    case ReferenceType.Value:
+                        EvaluateValueExpression(refName);
+                        break;
+                    case ReferenceType.Colour:
+                        EvaluateColourExpression(refName);
+                        break;
+                }
             }
         }
 
@@ -233,7 +330,11 @@ namespace InABox.Core
         {
             foreach(var name in Expressions.Keys)
             {
-                EvaluateExpression(name);
+                EvaluateValueExpression(name);
+            }
+            foreach(var name in ColourExpressions.Keys)
+            {
+                EvaluateColourExpression(name);
             }
         }
 

+ 11 - 0
InABox.Core/DigitalForms/Layouts/Fields/DFLayoutFieldProperties.cs

@@ -35,6 +35,15 @@ namespace InABox.Core
         [EditorSequence(7)]
         public string Expression { get; set; }
 
+        /// <summary>
+        /// An expression that sets the field to have a background colour of its result.
+        /// The result of the expression should be either a hex code like #rrggbb or #aarrggbb, or the string name of a <see cref="System.Drawing.KnownColor"/>,
+        /// like 'Yellow'.
+        /// </summary>
+        [TextBoxEditor(ToolTip = "Evalutes to either a hex code (#RRGGBB or #AARRGGBB) or a colour name (like 'Red' or 'Yellow'), which sets the background colour for this variable.")]
+        [EditorSequence(7)]
+        public string ColourExpression { get; set; }
+
         public abstract object? ParseValue(object? value);
 
         public abstract string FormatValue(object? value);
@@ -52,6 +61,7 @@ namespace InABox.Core
             Secure = GetProperty("Secure", false);
             Retain = GetProperty("Retain", false);
             Expression = GetProperty("Expression", "");
+            ColourExpression = GetProperty("ColourExpression", "");
         }
 
         protected override void SaveProperties()
@@ -62,6 +72,7 @@ namespace InABox.Core
             SetProperty("Secure", Secure);
             SetProperty("Retain", Retain);
             SetProperty("Expression", Expression);
+            SetProperty("ColourExpression", ColourExpression);
         }
 
         private class PropertyLookupGenerator : LookupGenerator<object>

+ 1 - 0
InABox.DynamicGrid/DynamicGrid.cs

@@ -1879,6 +1879,7 @@ namespace InABox.DynamicGrid
         private void ProcessData(bool reloadcolumns, bool reloaddata)
         {
             Data.Columns.Clear();
+            Data.Setters.Clear();
             if (MasterData != null)
                 foreach (var column in MasterData.Columns)
                     Data.Columns.Add(column);

+ 1 - 1
InABox.DynamicGrid/DynamicGridUtils.cs

@@ -374,7 +374,7 @@ namespace InABox.DynamicGrid
                 else
                     try
                     {
-                        object value;
+                        object? value;
                         var getter = prop.Getter();
                         if (getter != null)
                             value = getter.Invoke(item);

+ 28 - 4
InABox.DynamicGrid/FormDesigner/DynamicFormDesignGrid.cs

@@ -23,6 +23,7 @@ using InABox.DynamicGrid.Properties;
 using InABox.WPF;
 using Microsoft.Win32;
 using Syncfusion.Windows.Shared;
+using Brush = System.Windows.Media.Brush;
 using Color = System.Drawing.Color;
 using Image = System.Windows.Controls.Image;
 using Point = System.Windows.Point;
@@ -90,6 +91,7 @@ namespace InABox.DynamicGrid
         private readonly Dictionary<Type, IDynamicGrid> elementgrids = new();
 
         private readonly Dictionary<DFLayoutControl, FrameworkElement> elementmap = new();
+        private readonly Dictionary<DFLayoutControl, Border> borderMap = new();
         private readonly SolidColorBrush FieldBrush = new(Colors.LightYellow);
 
 
@@ -912,14 +914,25 @@ namespace InABox.DynamicGrid
             return control;
         }
 
-        private void RenderElement(DFLayoutControl item, int offset, bool horzstar, bool vertstar)
+        private Brush GetItemBackgroundBrush(DFLayoutControl item, Color? colour = null)
         {
-            var border = CreateBorder(item.Row, item.Column, item.RowSpan, item.ColumnSpan, offset, horzstar, vertstar);
+            if(colour != null)
+            {
+                var colourValue = colour.Value;
+                return new SolidColorBrush(new System.Windows.Media.Color { R = colourValue.R, G = colourValue.G, B = colourValue.B, A = colourValue.A });
+            }
 
             if (item is DFLayoutField field)
-                border.Background = field.GetPropertyValue<bool>("Required") ? RequiredFieldBrush : FieldBrush;
+                return field.GetPropertyValue<bool>("Required") ? RequiredFieldBrush : FieldBrush;
             else
-                border.Background = IsDesigning ? FieldBrush : BackgroundBrush;
+                return IsDesigning ? FieldBrush : BackgroundBrush;
+        }
+
+        private void RenderElement(DFLayoutControl item, int offset, bool horzstar, bool vertstar)
+        {
+            var border = CreateBorder(item.Row, item.Column, item.RowSpan, item.ColumnSpan, offset, horzstar, vertstar);
+
+            border.Background = GetItemBackgroundBrush(item);
 
             if (!ShowBorders)
                 border.Padding = new Thickness(
@@ -938,6 +951,7 @@ namespace InABox.DynamicGrid
             {
                 CreateElementContextMenu(border, item);
             }
+            borderMap[item] = border;
 
             if (element != null)
             {
@@ -1231,6 +1245,16 @@ namespace InABox.DynamicGrid
             return GetFieldData(field, dataField);
         }
 
+        public void SetFieldColour(string fieldName, Color? colour)
+        {
+            var field = form.Elements.Where(x => (x as DFLayoutField)?.Name == fieldName).FirstOrDefault() as DFLayoutField;
+            if (field is null || !borderMap.TryGetValue(field, out var border))
+            {
+                return;
+            }
+            border.Background = GetItemBackgroundBrush(field, colour);
+        }
+
         /// <summary>
         /// Load values into editor; Must be called after <see cref="Initialize"/>.
         /// </summary>