Jelajahi Sumber

- Refactored CustomiseEditor
- Added AdditionalTables to dashboards.

Kenric Nugteren 4 bulan lalu
induk
melakukan
a6eb76c98d

+ 1 - 1
InABox.Core/CoreTable/CoreTable.cs

@@ -28,7 +28,7 @@ namespace InABox.Core
         public int TotalRecords { get; set; }
         public int Offset { get; set; } = 0;
 
-        public IList<CoreColumn> Columns { get => columns; }
+        public List<CoreColumn> Columns { get => columns; }
         public List<CoreRow> Rows
         {
             get

+ 1 - 1
InABox.Core/CoreTable/ICoreTable.cs

@@ -8,7 +8,7 @@ namespace InABox.Core
 {
     public interface ICoreTable
     {
-        IList<CoreColumn> Columns { get; }
+        List<CoreColumn> Columns { get; }
         List<CoreRow> Rows { get; }
         Dictionary<string, IList<Action<object, object>?>> Setters { get; }
         string TableName { get; }

+ 16 - 0
InABox.Core/Objects/Editors/CoreColumnsListEditor.cs

@@ -0,0 +1,16 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace InABox.Core
+{
+    public class CoreColumnsListEditor : BaseEditor
+    {
+        public Type? Type { get; set; }
+
+        protected override BaseEditor DoClone()
+        {
+            return new CoreColumnsListEditor() { Type = Type };
+        }
+    }
+}

+ 54 - 13
inabox.wpf/Dashboard/DynamicDashboardDataComponent.cs

@@ -1,5 +1,6 @@
 using InABox.Clients;
 using InABox.Core;
+using InABox.Scripting;
 using System;
 using System.Collections.Generic;
 using System.Diagnostics.CodeAnalysis;
@@ -75,10 +76,36 @@ public class DynamicDashboardAdditionalTable : IDynamicDashboardTable
 
     public List<CoreColumn> Columns { get; set; } = new();
 
-    public string? Script { get; set; }
+    private string? _script;
+    public string? Script
+    {
+        get => _script;
+        set
+        {
+            if(_script != value)
+            {
+                _script = value;
+                _scriptDocument = null;
+            }
+        }
+    }
 
     IEnumerable<CoreColumn> IDynamicDashboardTable.CoreColumns => Columns;
 
+    private ScriptDocument? _scriptDocument;
+    private ScriptDocument? ScriptDocument
+    {
+        get
+        {
+            if(_scriptDocument is null && Script is not null)
+            {
+                _scriptDocument = new(Script);
+                _scriptDocument.Compile();
+            }
+            return _scriptDocument;
+        }
+    }
+
     public static string DefaultScript()
     {
         return @"using InABox.Wpf.Dashboard;
@@ -91,6 +118,13 @@ public class Module
     }
 }";
     }
+
+    public void PopulateTable(CoreTable table, DynamicDashboardData data)
+    {
+        if (ScriptDocument is null) return;
+
+        ScriptDocument.Execute(methodname: "PopulateTable", parameters: [table, data]);
+    }
 }
 
 public class DynamicDashboardDataComponent
@@ -104,7 +138,7 @@ public class DynamicDashboardDataComponent
     public IDynamicDashboardTable GetTable(string key)
     {
         return Tables.FirstOrDefault(x => x.Key == key)
-            ?? throw new KeyNotFoundException($"Data query '{key}' does not exist.");
+            ?? throw new KeyNotFoundException($"Data table '{key}' does not exist.");
     }
 
     public bool TryGetTable(string key, [NotNullWhen(true)] out IDynamicDashboardTable? query)
@@ -113,16 +147,15 @@ public class DynamicDashboardDataComponent
         return query != null;
     }
 
-    public IDynamicDashboardDataQuery GetQuery(string key)
+    private void PopulateAdditionalTables(DynamicDashboardData data)
     {
-        return Queries.FirstOrDefault(x => x.Key == key)
-            ?? throw new KeyNotFoundException($"Data query '{key}' does not exist.");
-    }
-
-    public bool TryGetQuery(string key, [NotNullWhen(true)] out IDynamicDashboardDataQuery? query)
-    {
-        query = Queries.FirstOrDefault(x => x.Key == key);
-        return query != null;
+        foreach(var tableDef in AdditionalTables)
+        {
+            var table = new CoreTable();
+            table.Columns.AddRange(tableDef.Columns);
+            tableDef.PopulateTable(table, data);
+            data.Data.Add(tableDef.Key, table);
+        }
     }
 
     /// <summary>
@@ -134,7 +167,10 @@ public class DynamicDashboardDataComponent
         var range = maxRecords.HasValue ? CoreRange.Database(maxRecords.Value) : null;
         var queryDefs = Queries.Select(x => new KeyedQueryDef(x.Key, x.Type, x.Filter, x.Columns, x.SortOrder, range));
         var results = Client.QueryMultiple(queryDefs);
-        return new DynamicDashboardData(results.Results);
+
+        var data = new DynamicDashboardData(results.Results);
+        PopulateAdditionalTables(data);
+        return data;
     }
 
     /// <summary>
@@ -146,7 +182,12 @@ public class DynamicDashboardDataComponent
         var range = maxRecords.HasValue ? CoreRange.Database(maxRecords.Value) : null;
         var queryDefs = Queries.Select(x => new KeyedQueryDef(x.Key, x.Type, x.Filter, x.Columns, x.SortOrder, range));
         var results = await Client.QueryMultipleAsync(queryDefs);
-        return new DynamicDashboardData(results.Results);
+
+        var data = new DynamicDashboardData(results.Results);
+
+        PopulateAdditionalTables(data);
+
+        return data;
     }
 }
 

+ 118 - 0
inabox.wpf/Dashboard/Editor/DynamicDashboardAdditionalTableGrid.cs

@@ -0,0 +1,118 @@
+using InABox.Core;
+using InABox.DynamicGrid;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace InABox.Wpf.Dashboard.Editor;
+
+internal class CoreColumnEditItem : BaseObject
+{
+    [EditorSequence(1)]
+    public string ColumnName { get; set; } = "";
+
+    [EditorSequence(2)]
+    [ComboLookupEditor(typeof(PropertyTypeLookups), Visible = Core.Visible.Default)]
+    public Type DataType { get; set; } = typeof(string);
+
+    internal class PropertyTypeLookups : LookupGenerator<object>
+    {
+        private static IEnumerable<Type> Types => [
+            typeof(string),
+            typeof(int),
+            typeof(bool),
+            typeof(DateTime),
+            typeof(TimeSpan),
+            typeof(double),
+            ];
+
+        public PropertyTypeLookups(object[] items) : base(items)
+        {
+            foreach(var type in Types)
+            {
+                AddValue(type, type.Name);
+            }
+        }
+    }
+}
+
+internal class DynamicDashboardAdditionalTableEditItem : BaseObject
+{
+    [EditorSequence(1)]
+    public string Name { get; set; } = "";
+
+    [EditorSequence(2)]
+    [ListEditor]
+    public List<CoreColumnEditItem> Columns { get; set; } = new();
+
+    [EditorSequence(3)]
+    [ScriptEditor]
+    public string Script { get; set; } = "";
+
+    public DynamicDashboardAdditionalTableEditItem()
+    {
+    }
+
+    public DynamicDashboardAdditionalTableEditItem(DynamicDashboardAdditionalTable table)
+    {
+        Name = table.Key;
+        Columns = table.Columns.ToList(x => new CoreColumnEditItem
+        {
+            DataType = x.DataType,
+            ColumnName = x.ColumnName
+        });
+        Script = table.Script ?? "";
+    }
+
+    public DynamicDashboardAdditionalTable ToTable()
+    {
+        return new DynamicDashboardAdditionalTable
+        {
+            Key = Name,
+            Columns = Columns.ToList(x => new CoreColumn
+            {
+                ColumnName = x.ColumnName,
+                DataType = x.DataType
+            }),
+            Script = Script.IsNullOrWhiteSpace() ? null : Script
+        };
+    }
+}
+
+internal class DynamicDashboardAdditionalTableGrid : DynamicItemsListGrid<DynamicDashboardAdditionalTableEditItem>
+{
+    protected override void DoReconfigure(DynamicGridOptions options)
+    {
+        base.DoReconfigure(options);
+
+        options.AddRows = true;
+        options.EditRows = true;
+        options.DeleteRows = true;
+    }
+
+    protected override void CustomiseEditor(IDynamicEditorForm form, DynamicDashboardAdditionalTableEditItem[] items, DynamicGridColumn column, BaseEditor editor)
+    {
+        base.CustomiseEditor(form, items, column, editor);
+
+        var item = items[0];
+
+        if(column.ColumnName == nameof(DynamicDashboardAdditionalTableEditItem.Script) && editor is ScriptEditor scriptEditor)
+        {
+            scriptEditor.Type = ScriptEditorType.TemplateEditor;
+            scriptEditor.OnEditorClicked += () =>
+            {
+                var script = item.Script.NotWhiteSpaceOr()
+                    ?? DynamicDashboardAdditionalTable.DefaultScript();
+
+                var editor = new ScriptEditorWindow(script, SyntaxLanguage.CSharp);
+                if (editor.ShowDialog() == true)
+                {
+                    form.SetEditorValue(column.ColumnName, editor.Script);
+                    item.Script = editor.Script;
+                }
+            };
+        }
+    }
+}

+ 2 - 2
inabox.wpf/Dashboard/Editor/DynamicDashboardDataQueryGrid.cs

@@ -149,9 +149,9 @@ internal class DynamicDashboardDataQueryGrid : DynamicItemsListGrid<DynamicDashb
     private readonly Column<DynamicDashboardDataQueryEditItem> SortOrderColumn = new(x => x.SortOrder);
     private readonly Column<DynamicDashboardDataQueryEditItem> TypeColumn = new(x => x.Type);
 
-    protected override void CustomiseEditor(DynamicDashboardDataQueryEditItem[] items, DynamicGridColumn column, BaseEditor editor)
+    protected override void CustomiseEditor(IDynamicEditorForm form, DynamicDashboardDataQueryEditItem[] items, DynamicGridColumn column, BaseEditor editor)
     {
-        base.CustomiseEditor(items, column, editor);
+        base.CustomiseEditor(form, items, column, editor);
 
         var item = items.First();
 

+ 6 - 1
inabox.wpf/Dashboard/Editor/DynamicDashboardEditor.xaml

@@ -18,6 +18,7 @@
             
             <!-- Presenter Editor -->
             <RowDefinition Height="Auto"/>
+            <RowDefinition Height="Auto"/>
             <RowDefinition Height="*"/>
         </Grid.RowDefinitions>
         <Grid.ColumnDefinitions>
@@ -63,12 +64,16 @@
                    Margin="5"/>
 
         <ContentControl x:Name="PresentationEditorControl"
-                        Grid.Row="4" Grid.RowSpan="2" Grid.ColumnSpan="2"
+                        Grid.Row="4" Grid.RowSpan="3" Grid.ColumnSpan="2"
                         Margin="0,0,0,0"
                         MinHeight="100"/>
         <Button x:Name="SelectData" Content="Data"
                 Grid.Row="4" Grid.Column="2"
                 Padding="5" Margin="5,0,0,0"
                 Click="SelectData_Click"/>
+        <Button x:Name="SelectScripts" Content="Scripts"
+                Grid.Row="5" Grid.Column="2"
+                Padding="5" Margin="5,5,0,0"
+                Click="SelectScripts_Click"/>
     </Grid>
 </UserControl>

+ 19 - 0
inabox.wpf/Dashboard/Editor/DynamicDashboardEditor.xaml.cs

@@ -121,6 +121,25 @@ public partial class DynamicDashboardEditor : UserControl, INotifyPropertyChange
             RefreshData();
         }
     }
+
+    private void SelectScripts_Click(object sender, RoutedEventArgs e)
+    {
+        var grid = new DynamicDashboardAdditionalTableGrid();
+        grid.Items = DataComponent.AdditionalTables.ToList(x => new DynamicDashboardAdditionalTableEditItem(x));
+        grid.Refresh(true, true);
+        var dlg = new DynamicContentDialog(grid)
+        {
+            WindowStartupLocation = WindowStartupLocation.CenterScreen,
+            Title = "Edit Additional Scripted Tables",
+            CanSave = true
+        };
+        if(dlg.ShowDialog() == true)
+        {
+            DataComponent.AdditionalTables = grid.Items.Select(x => x.ToTable()).ToList();
+            RefreshData();
+        }
+    }
+
     private void RefreshData()
     {
         if(_presenter is not null)

+ 92 - 82
inabox.wpf/Dashboard/Presenters/DynamicDashboardGridPresenter.cs

@@ -19,7 +19,7 @@ public class DynamicDashboardGridPresenterProperties : BaseObject
     /// <summary>
     /// The key of the query that will provide the data for this presenter.
     /// </summary>
-    public string Query { get; set; } = "";
+    public string Table { get; set; } = "";
 
     public DynamicGridColumns? Columns { get; set; }
 }
@@ -44,7 +44,7 @@ public class DynamicDashboardGridPresenter : IDynamicDashboardDataPresenter<Dyna
 
     public bool IsPreview { get; set; }
 
-    private IDynamicDashboardGridPresenterGrid? Grid;
+    private CoreTableGrid? Grid;
     private readonly ContentControl Content = new();
 
     private DynamicDashboardData? _data;
@@ -57,12 +57,12 @@ public class DynamicDashboardGridPresenter : IDynamicDashboardDataPresenter<Dyna
 
     private void UpdateData()
     {
-        if (Properties.Query.IsNullOrWhiteSpace() || !DataComponent.TryGetQuery(Properties.Query, out var query))
+        if (Properties.Table.IsNullOrWhiteSpace() || !DataComponent.TryGetTable(Properties.Table, out var table))
         {
-            Properties.Query = DataComponent.Queries.FirstOrDefault()?.Key ?? "";
+            Properties.Table = DataComponent.Tables.FirstOrDefault()?.Key ?? "";
         }
 
-        if (!DataComponent.TryGetQuery(Properties.Query, out query))
+        if (!DataComponent.TryGetTable(Properties.Table, out table))
         {
             var border = new Border
             {
@@ -73,12 +73,10 @@ public class DynamicDashboardGridPresenter : IDynamicDashboardDataPresenter<Dyna
             return;
         }
 
-        Grid = (Activator.CreateInstance(typeof(DynamicDashboardGridPresenterGrid<>).MakeGenericType(query.Type)) as IDynamicDashboardGridPresenterGrid)!;
-        Grid.OnGenerateColumns += Grid_OnGenerateColumns;
-        Grid.OnSaveColumns += Grid_OnSaveColumns;
-        Grid.GetAvailableColumns += Grid_GetAvailableColumns;
-        Grid.OnLoadColumnsMenu += Grid_OnLoadColumnsMenu;
-
+        Grid = new DataGrid(this)
+        {
+            ColumnSchema = new DataGridColumnSchema(this)
+        };
         Grid.Refresh(true, false);
 
         Content.Content = Grid as FrameworkElement;
@@ -88,13 +86,14 @@ public class DynamicDashboardGridPresenter : IDynamicDashboardDataPresenter<Dyna
     {
         if (!IsPreview) return;
 
-        if(DataComponent.Queries.Count > 1)
+        var tableItem = menu.AddItem("Select Table", null, null);
+        foreach(var table in DataComponent.Tables)
         {
-            var queryItem = menu.AddItem("Select Query", null, null);
-            foreach(var query in DataComponent.Queries)
-            {
-                queryItem.AddCheckMenuItem(query.Key, query.Key, SelectQuery_Click, isChecked: Properties.Query == query.Key);
-            }
+            tableItem.AddCheckMenuItem(table.Key, table.Key, SelectTable_Click, isChecked: Properties.Table == table.Key);
+        }
+        if(tableItem.Items.Count <= 1)
+        {
+            menu.Items.Remove(tableItem);
         }
         menu.AddItem("Select Data", null, SelectData_Click);
     }
@@ -118,11 +117,11 @@ public class DynamicDashboardGridPresenter : IDynamicDashboardDataPresenter<Dyna
         }
     }
 
-    private void SelectQuery_Click(string key, bool isChecked)
+    private void SelectTable_Click(string key, bool isChecked)
     {
         if (!isChecked) return;
 
-        Properties.Query = key;
+        Properties.Table = key;
         Properties.Columns = null;
 
         UpdateData();
@@ -132,22 +131,6 @@ public class DynamicDashboardGridPresenter : IDynamicDashboardDataPresenter<Dyna
         }
     }
 
-    private void Grid_OnLoadColumnsMenu(ContextMenu menu)
-    {
-        menu.AddSeparatorIfNeeded();
-        CustomiseMenu(menu);
-        menu.RemoveUnnecessarySeparators();
-    }
-
-    private void Grid_GetAvailableColumns(GetAvailableColumnsEventArgs args)
-    {
-        if (!DataComponent.TryGetQuery(Properties.Query, out var query))
-        {
-            return;
-        }
-        args.Columns = args.Columns.Where(x => query.Columns.Contains(x.ColumnName));
-    }
-
     private void Border_ContextMenuOpening(object sender, ContextMenuEventArgs e)
     {
         var menu = new ContextMenu();
@@ -159,48 +142,22 @@ public class DynamicDashboardGridPresenter : IDynamicDashboardDataPresenter<Dyna
         e.Handled = true;
     }
 
-    private void Grid_OnSaveColumns(object sender, SaveColumnsEventArgs args)
-    {
-        Properties.Columns = args.Columns.Count > 0 ? args.Columns : null;
-    }
-
-    private void Grid_OnGenerateColumns(object sender, GenerateColumnsEventArgs args)
-    {
-        if(Properties.Columns is not null)
-        {
-            args.Columns.Clear();
-            foreach(var column in Properties.Columns)
-            {
-                args.Columns.Add(column.Copy());
-            }
-        }
-        else if (DataComponent.TryGetQuery(Properties.Query, out var query) && Grid is not null)
-        {
-            args.Columns.Clear();
-            args.Columns.AddRange(Grid.ExtractColumns(query.Columns));
-        }
-    }
-
     public void Refresh(DynamicDashboardData data)
     {
         _data = data;
 
-        if (!DataComponent.TryGetQuery(Properties.Query, out var query) || Grid is null)
+        if (!DataComponent.TryGetTable(Properties.Table, out var _table) || Grid is null)
         {
             return;
         }
 
-        if(data.TryGetData(Properties.Query, out var table))
+        if(data.TryGetData(Properties.Table, out var table))
         {
-            Grid.Items.Clear();
-            foreach(var obj in table.ToObjects(query.Type))
-            {
-                Grid.Items.Add(obj);
-            }
+            Grid.Table = table;
         }
         else
         {
-            Grid.Items.Clear();
+            Grid.Table.Rows.Clear();
         }
         Grid.Refresh(false, true);
     }
@@ -208,28 +165,81 @@ public class DynamicDashboardGridPresenter : IDynamicDashboardDataPresenter<Dyna
     public void Shutdown(CancelEventArgs? cancel)
     {
     }
-}
-
-internal interface IDynamicDashboardGridPresenterGrid : IDynamicItemsListGrid
-{
-    public event Action<ContextMenu>? OnLoadColumnsMenu;
-}
-internal class DynamicDashboardGridPresenterGrid<T> : DynamicItemsListGrid<T>, IDynamicDashboardGridPresenterGrid
-    where T : BaseObject, new()
-{
-    public event Action<ContextMenu>? OnLoadColumnsMenu;
 
-    protected override void DoReconfigure(DynamicGridOptions options)
+    private class DataGridColumnSchema(DynamicDashboardGridPresenter presenter) : IDynamicGridColumnSchema
     {
-        base.DoReconfigure(options);
+        public IEnumerable<string> ColumnNames
+        {
+            get
+            {
+                if (presenter.DataComponent.TryGetTable(presenter.Properties.Table, out var table))
+                {
+                    return table.CoreColumns.Where(x => EditorUtils.GetEditor(x.DataType) is not null).Select(x => x.ColumnName);
+                }
+                else
+                {
+                    return [];
+                }
+            }
+        }
 
-        options.Clear();
-        options.SelectColumns = true;
+        public DynamicGridColumn GetColumn(string column)
+        {
+            var table = presenter.DataComponent.GetTable(presenter.Properties.Table);
+            var coreCol = table.CoreColumns.First(x => x.ColumnName == column);
+            return DynamicGridColumn.FromCoreColumn(coreCol)!;
+        }
+
+        public string? GetComment(string column)
+        {
+            return null;
+        }
+
+        public bool IsVisible(string column)
+        {
+            return true;
+        }
     }
 
-    protected override void LoadColumnsMenu(ContextMenu menu)
+    private class DataGrid(DynamicDashboardGridPresenter presenter) : CoreTableGrid
     {
-        base.LoadColumnsMenu(menu);
-        OnLoadColumnsMenu?.Invoke(menu);
+        protected override void DoReconfigure(DynamicGridOptions options)
+        {
+            base.DoReconfigure(options);
+            options.SelectColumns = true;
+        }
+
+        public override DynamicGridColumns GenerateColumns()
+        {
+            if(presenter.Properties.Columns is not null)
+            {
+                var columns = new DynamicGridColumns();
+                columns.AddRange(presenter.Properties.Columns.Select(x => x.Copy()));
+                return columns;
+            }
+            else
+            {
+                return base.GenerateColumns();
+            }
+        }
+
+        protected override void LoadColumnsMenu(ContextMenu menu)
+        {
+            base.LoadColumnsMenu(menu);
+            menu.AddSeparatorIfNeeded();
+            presenter.CustomiseMenu(menu);
+            menu.RemoveUnnecessarySeparators();
+        }
+
+        protected override void SaveColumns(DynamicGridColumns columns)
+        {
+            base.SaveColumns(columns);
+            presenter.Properties.Columns = columns.Count > 0 ? columns : null;
+        }
+
+        protected override DynamicGridColumns LoadColumns()
+        {
+            return presenter.Properties.Columns ?? base.LoadColumns();
+        }
     }
 }

+ 2 - 2
inabox.wpf/DynamicGrid/Grids/DynamicGrid.cs

@@ -504,11 +504,11 @@ public abstract class DynamicGrid<T> : BaseDynamicGrid, IDynamicGridUIComponentP
 
     private void DoCustomiseEditor(IDynamicEditorForm sender, object[] items, DynamicGridColumn column, BaseEditor editor)
     {
-        CustomiseEditor((T[])items, column, editor);
+        CustomiseEditor(sender, (T[])items, column, editor);
         OnCustomiseEditor?.Invoke(sender, (T[])items, column, editor);
     }
 
-    protected virtual void CustomiseEditor(T[] items, DynamicGridColumn column, BaseEditor editor)
+    protected virtual void CustomiseEditor(IDynamicEditorForm form, T[] items, DynamicGridColumn column, BaseEditor editor)
     {
     }