You can now make components that can have expressions

This commit is contained in:
Lars Brubaker 2022-03-21 15:41:57 -07:00
parent 21955f625a
commit 6321c4aa8e
2 changed files with 213 additions and 155 deletions

View file

@ -30,6 +30,7 @@ either expressed or implied, of the FreeBSD Project.
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Threading;
using MatterHackers.Agg.UI;
using MatterHackers.DataConverters3D;
using MatterHackers.DataConverters3D.UndoCommands;
@ -132,7 +133,89 @@ namespace MatterHackers.MatterControl.DesignTools
Invalidate(InvalidateType.Children);
}
public override void Cancel(UndoBuffer undoBuffer)
public (string cellId, string cellData) DecodeContent(int editorIndex)
{
var cellData2 = SurfacedEditors[editorIndex].Substring(1);
var cellId2 = cellData2.ToLower();
// check if it has embededdata
var separator = cellData2.IndexOf(',');
if (separator != -1)
{
cellId2 = cellData2.Substring(0, separator).ToLower();
cellData2 = cellData2.Substring(separator + 1);
}
else
{
var firtSheet = this.Descendants<SheetObject3D>().FirstOrDefault();
if (firtSheet != null)
{
// We don't have any cache of the cell content, get the current content
double.TryParse(firtSheet.SheetData.EvaluateExpression(cellId2), out double value);
cellData2 = value.ToString();
}
}
return (cellId2, cellData2);
}
private void RecalculateSheet()
{
// if there are editors that reference cells
for (int i=0; i<SurfacedEditors.Count; i++)
{
var (cellId, cellData) = this.DecodeContent(i);
if (cellData.StartsWith("="))
{
var expression = new DoubleOrExpression(cellData);
var firtSheet = this.Descendants<SheetObject3D>().FirstOrDefault();
if (firtSheet != null)
{
var cell = firtSheet.SheetData[cellId];
if (cell != null)
{
cell.Expression = expression.Value(this).ToString();
}
}
}
}
if (SurfacedEditors.Any(se => se.StartsWith("!"))
&& !this.RebuildLocked)
{
var firtSheet = this.Descendants<SheetObject3D>().FirstOrDefault();
var componentLock = this.RebuildLock();
firtSheet.SheetData.Recalculate();
UiThread.RunOnIdle(() =>
{
// wait until the sheet is done rebuilding (or 30 seconds)
var startTime = UiThread.CurrentTimerMs;
while (firtSheet.RebuildLocked
&& startTime + 30000 < UiThread.CurrentTimerMs)
{
Thread.Sleep(1);
}
componentLock.Dispose();
});
}
}
public override void OnInvalidate(InvalidateArgs invalidateType)
{
switch(invalidateType.InvalidateType)
{
case InvalidateType.SheetUpdated:
RecalculateSheet();
break;
}
base.OnInvalidate(invalidateType);
}
public override void Cancel(UndoBuffer undoBuffer)
{
// Make any hiden children visible
// on any invalidate ensure that the visibility setting are correct for embedded sheet objects

View file

@ -348,159 +348,10 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
// put in the normal editor
if (selectedItem is ComponentObject3D componentObject
&& componentObject.Finalized)
{
var context = new PPEContext();
PublicPropertyEditor.AddUnlockLinkIfRequired(selectedItem, editorPanel, theme);
foreach (var selector in componentObject.SurfacedEditors)
{
// if it is a reference to a sheet cell
if (selector.StartsWith("!"))
{
var firtSheet = componentObject.Descendants<SheetObject3D>().FirstOrDefault();
if (firtSheet != null)
{
(string cellId, string cellData) DecodeContent()
{
var cellData2 = selector.Substring(1);
var cellId2 = cellData2.ToLower();
// check if it has embededdata
var separator = cellData2.IndexOf(',');
if (separator != -1)
{
cellId2 = cellData2.Substring(0, separator).ToLower();
cellData2 = cellData2.Substring(separator);
}
else
{
// We don't have any cache of the cell content, get the current content
double.TryParse(firtSheet.SheetData.EvaluateExpression(cellId2), out double value);
cellData2 = value.ToString();
}
return (cellId2, cellData2);
}
var (cellId, cellData) = DecodeContent();
var cell = firtSheet.SheetData[cellId];
if (cell != null)
{
// create an expresion editor
var field = new ExpressionField(theme)
{
Name = cellId + " Field"
};
field.Initialize(0);
if (cellData.Contains("="))
{
field.SetValue(cellData, false);
}
else // make sure it is formatted
{
double.TryParse(cellData, out double value);
var format = "0." + new string('#', 5);
field.SetValue(value.ToString(format), false);
}
field.ClearUndoHistory();
void RecalculateSheet()
{
var componentLock = componentObject.RebuildLock();
firtSheet.SheetData.Recalculate();
UiThread.RunOnIdle(() =>
{
// wait until the sheet is done rebuilding (or 30 seconds)
var startTime = UiThread.CurrentTimerMs;
while (firtSheet.RebuildLocked
&& startTime + 30000 < UiThread.CurrentTimerMs)
{
Thread.Sleep(1);
}
componentLock.Dispose();
});
}
var doOrUndoing = false;
field.ValueChanged += (s, e) =>
{
if (!doOrUndoing)
{
var oldValue = DecodeContent().cellData;
var newValue = field.Value;
undoBuffer.AddAndDo(new UndoRedoActions(() =>
{
doOrUndoing = true;
cell.Expression = oldValue;
RecalculateSheet();
doOrUndoing = false;
},
() =>
{
doOrUndoing = true;
cell.Expression = newValue;
RecalculateSheet();
doOrUndoing = false;
}));
}
};
var row = new SettingsRow(cell.Name == null ? cellId : cell.Name, null, field.Content, theme);
editorPanel.AddChild(row);
}
}
}
else // parse it as a path to an object
{
// Get the named property via reflection
// Selector example: '$.Children<CylinderObject3D>'
var match = pathGetter.Select(componentObject, selector).ToList();
//// - Add editor row for each
foreach (var instance in match)
{
if (instance is IObject3D object3D)
{
if (ApplicationController.Instance.Extensions.GetEditorsForType(object3D.GetType())?.FirstOrDefault() is IObject3DEditor editor)
{
ShowObjectEditor((editor, object3D, object3D.Name), selectedItem);
}
}
else if (JsonPathContext.ReflectionValueSystem.LastMemberValue is ReflectionTarget reflectionTarget)
{
if (reflectionTarget.Source is IObject3D editedChild)
{
context.item = editedChild;
}
else
{
context.item = item;
}
var editableProperty = new EditableProperty(reflectionTarget.PropertyInfo, reflectionTarget.Source);
var editor = PublicPropertyEditor.CreatePropertyEditor(rows, editableProperty, undoBuffer, context, theme);
if (editor != null)
{
editorPanel.AddChild(editor);
}
// Init with custom 'UpdateControls' hooks
(context.item as IPropertyGridModifier)?.UpdateControls(new PublicPropertyChange(context, "Update_Button"));
}
}
}
}
// Enforce panel padding
foreach (var sectionWidget in editorPanel.Descendants<SectionWidget>())
{
sectionWidget.Margin = 0;
}
}
else
{
AddComponentEditor(selectedItem, undoBuffer, rows, componentObject);
}
else
{
if (item != null
&& ApplicationController.Instance.Extensions.GetEditorsForType(item.GetType())?.FirstOrDefault() is IObject3DEditor editor)
@ -510,7 +361,131 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
}
}
private class OperationButton : TextButton
private void AddComponentEditor(IObject3D selectedItem, UndoBuffer undoBuffer, SafeList<SettingsRow> rows, ComponentObject3D componentObject)
{
var context = new PPEContext();
PublicPropertyEditor.AddUnlockLinkIfRequired(selectedItem, editorPanel, theme);
var editorList = componentObject.SurfacedEditors;
for (var editorIndex = 0; editorIndex < editorList.Count; editorIndex++)
{
// if it is a reference to a sheet cell
if (editorList[editorIndex].StartsWith("!"))
{
AddSheetCellEditor(undoBuffer, componentObject, editorList, editorIndex);
}
else // parse it as a path to an object
{
// Get the named property via reflection
// Selector example: '$.Children<CylinderObject3D>'
var match = pathGetter.Select(componentObject, editorList[editorIndex]).ToList();
//// - Add editor row for each
foreach (var instance in match)
{
if (instance is IObject3D object3D)
{
if (ApplicationController.Instance.Extensions.GetEditorsForType(object3D.GetType())?.FirstOrDefault() is IObject3DEditor editor)
{
ShowObjectEditor((editor, object3D, object3D.Name), selectedItem);
}
}
else if (JsonPathContext.ReflectionValueSystem.LastMemberValue is ReflectionTarget reflectionTarget)
{
if (reflectionTarget.Source is IObject3D editedChild)
{
context.item = editedChild;
}
else
{
context.item = item;
}
var editableProperty = new EditableProperty(reflectionTarget.PropertyInfo, reflectionTarget.Source);
var editor = PublicPropertyEditor.CreatePropertyEditor(rows, editableProperty, undoBuffer, context, theme);
if (editor != null)
{
editorPanel.AddChild(editor);
}
// Init with custom 'UpdateControls' hooks
(context.item as IPropertyGridModifier)?.UpdateControls(new PublicPropertyChange(context, "Update_Button"));
}
}
}
}
// Enforce panel padding
foreach (var sectionWidget in editorPanel.Descendants<SectionWidget>())
{
sectionWidget.Margin = 0;
}
}
private void AddSheetCellEditor(UndoBuffer undoBuffer, ComponentObject3D componentObject, List<string> editorList, int editorIndex)
{
var firtSheet = componentObject.Descendants<SheetObject3D>().FirstOrDefault();
if (firtSheet != null)
{
var (cellId, cellData) = componentObject.DecodeContent(editorIndex);
var cell = firtSheet.SheetData[cellId];
if (cell != null)
{
// create an expresion editor
var field = new ExpressionField(theme)
{
Name = cellId + " Field"
};
field.Initialize(0);
if (cellData.Contains("="))
{
field.SetValue(cellData, false);
}
else // make sure it is formatted
{
double.TryParse(cellData, out double value);
var format = "0." + new string('#', 5);
field.SetValue(value.ToString(format), false);
}
field.ClearUndoHistory();
var doOrUndoing = false;
field.ValueChanged += (s, e) =>
{
if (!doOrUndoing)
{
var oldValue = componentObject.DecodeContent(editorIndex).cellData;
var newValue = field.Value;
undoBuffer.AddAndDo(new UndoRedoActions(() =>
{
doOrUndoing = true;
editorList[editorIndex] = "!" + cellId + "," + oldValue;
var expression = new DoubleOrExpression(oldValue);
cell.Expression = expression.Value(componentObject).ToString();
componentObject.Invalidate(InvalidateType.SheetUpdated);
doOrUndoing = false;
},
() =>
{
doOrUndoing = true;
editorList[editorIndex] = "!" + cellId + "," + newValue;
var expression = new DoubleOrExpression(newValue);
cell.Expression = expression.Value(componentObject).ToString();
componentObject.Invalidate(InvalidateType.SheetUpdated);
doOrUndoing = false;
}));
}
};
var row = new SettingsRow(cell.Name == null ? cellId : cell.Name, null, field.Content, theme);
editorPanel.AddChild(row);
}
}
}
private class OperationButton : TextButton
{
private readonly SceneOperation sceneOperation;
private readonly ISceneContext sceneContext;