You can now make components that can have expressions
This commit is contained in:
parent
21955f625a
commit
6321c4aa8e
2 changed files with 213 additions and 155 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue