diff --git a/src/XTMF2.GUI/App.axaml.cs b/src/XTMF2.GUI/App.axaml.cs
index ac32b4e..28b9ed9 100644
--- a/src/XTMF2.GUI/App.axaml.cs
+++ b/src/XTMF2.GUI/App.axaml.cs
@@ -47,8 +47,6 @@ public override void OnFrameworkInitializationCompleted()
// Load the XTMF Runtime asynchronously
_ = Task.Run(async () =>
{
- // Artificial delay for testing (0.5 seconds)
- await Task.Delay(500);
try
{
// Create the XTMF Runtime
diff --git a/src/XTMF2.GUI/Controls/ModelSystemCanvas.cs b/src/XTMF2.GUI/Controls/ModelSystemCanvas.cs
index 09bf991..325b4be 100644
--- a/src/XTMF2.GUI/Controls/ModelSystemCanvas.cs
+++ b/src/XTMF2.GUI/Controls/ModelSystemCanvas.cs
@@ -997,7 +997,7 @@ private void TryApplyZoomText()
///
/// Scrolls the host when the pointer is within
- /// pixels of any viewport edge during an element drag.
+ /// pixels of any viewport edge during a drag.
/// The scroll delta is proportional to how far inside the zone the cursor sits,
/// reaching at the very edge.
/// Also starts/stops the continuous based on whether
@@ -1035,6 +1035,8 @@ private void TryAutoScrollForDrag(Point svPos)
sv.Offset = new Vector(
Math.Max(0, sv.Offset.X + dx),
Math.Max(0, sv.Offset.Y + dy));
+ RefreshPendingLinkCurrentPos(svPos);
+ RefreshSelectionRectCurrentPos(svPos);
if (!_autoScrollTimer.IsEnabled)
_autoScrollTimer.Start();
}
@@ -1052,7 +1054,7 @@ private void TryAutoScrollForDrag(Point svPos)
///
private void OnAutoScrollTick(object? sender, EventArgs e)
{
- if (_dragging is null)
+ if (_dragging is null && _linkOrigin is null && _selRectStart is null)
{
_autoScrollTimer.Stop();
return;
@@ -1060,6 +1062,32 @@ private void OnAutoScrollTick(object? sender, EventArgs e)
TryAutoScrollForDrag(_lastSvPos);
}
+ private void RefreshPendingLinkCurrentPos(Point svPos)
+ {
+ if (_linkOrigin is null) return;
+
+ var sv = GetScrollViewer();
+ if (sv is null) return;
+
+ _linkCurrentPos = new Point(
+ (sv.Offset.X + svPos.X) / _scale,
+ (sv.Offset.Y + svPos.Y) / _scale);
+ InvalidateVisual();
+ }
+
+ private void RefreshSelectionRectCurrentPos(Point svPos)
+ {
+ if (_selRectStart is null) return;
+
+ var sv = GetScrollViewer();
+ if (sv is null) return;
+
+ _selRectCurrent = new Point(
+ (sv.Offset.X + svPos.X) / _scale,
+ (sv.Offset.Y + svPos.Y) / _scale);
+ InvalidateVisual();
+ }
+
private void OnZoomTextBoxKeyDown(object? sender, KeyEventArgs e)
{
if (e.Key is Key.Enter or Key.Return)
diff --git a/src/XTMF2.GUI/Controls/ModelSystemCanvas/ModelSystemCanvas.ContextMenu.cs b/src/XTMF2.GUI/Controls/ModelSystemCanvas/ModelSystemCanvas.ContextMenu.cs
index 94a8a8a..792dbe9 100644
--- a/src/XTMF2.GUI/Controls/ModelSystemCanvas/ModelSystemCanvas.ContextMenu.cs
+++ b/src/XTMF2.GUI/Controls/ModelSystemCanvas/ModelSystemCanvas.ContextMenu.cs
@@ -19,7 +19,9 @@ You should have received a copy of the GNU General Public License
using System;
using System.Collections.Generic;
using System.Linq;
+using Avalonia;
using Avalonia.Controls;
+using Avalonia.Layout;
using Avalonia.Styling;
using XTMF2.GUI.ViewModels;
using XTMF2.ModelSystemConstruct;
@@ -101,6 +103,34 @@ private void ClearElementMultiSelectionOnly()
_ => null,
};
+ private static Grid CreateShortcutMenuHeader(string description, string shortcut)
+ {
+ var grid = new Grid
+ {
+ ColumnDefinitions = new ColumnDefinitions("*,Auto"),
+ MinWidth = 280
+ };
+
+ var descriptionText = new TextBlock
+ {
+ Text = description
+ };
+
+ var shortcutText = new TextBlock
+ {
+ Text = shortcut,
+ Margin = new Thickness(24, 0, 0, 0),
+ HorizontalAlignment = HorizontalAlignment.Right,
+ Opacity = 0.65
+ };
+
+ Grid.SetColumn(descriptionText, 0);
+ Grid.SetColumn(shortcutText, 1);
+ grid.Children.Add(descriptionText);
+ grid.Children.Add(shortcutText);
+ return grid;
+ }
+
private List GetDisableTargetsForClickedLink(LinkViewModel clickedLink)
{
if (_multiLinkSelection.Count <= 1 || !_multiLinkSelection.Contains(clickedLink.UnderlyingLink))
@@ -304,19 +334,19 @@ private void ShowContextMenu(ICanvasElement? element, LinkViewModel? link)
addStartItem.Click += (_, _) => _vm.AddStartAt(spawnPt.X, spawnPt.Y);
bgMenu.Items.Add(addStartItem);
- var addModuleItem = new MenuItem { Header = "Add Module…" };
+ var addModuleItem = new MenuItem { Header = CreateShortcutMenuHeader("Add Module…", "Ctrl+M") };
addModuleItem.Click += (_, _) => _ = _vm.AddModuleAtAsync(spawnPt.X, spawnPt.Y);
bgMenu.Items.Add(addModuleItem);
- var addCommentItem = new MenuItem { Header = "Add Comment" };
+ var addCommentItem = new MenuItem { Header = CreateShortcutMenuHeader("Add Comment", "Ctrl+N") };
addCommentItem.Click += (_, _) => _vm.AddCommentBlockAt(spawnPt.X, spawnPt.Y);
bgMenu.Items.Add(addCommentItem);
- var addFtItem = new MenuItem { Header = "Add Function Template…" };
+ var addFtItem = new MenuItem { Header = CreateShortcutMenuHeader("Add Function Template…", "Ctrl+T") };
addFtItem.Click += (_, _) => _ = _vm.AddFunctionTemplateAtAsync(spawnPt.X, spawnPt.Y);
bgMenu.Items.Add(addFtItem);
- var addFiItem = new MenuItem { Header = "Add Function Instance…" };
+ var addFiItem = new MenuItem { Header = CreateShortcutMenuHeader("Add Function Instance…", "Ctrl+I") };
addFiItem.Click += (_, _) => _ = _vm.AddFunctionInstanceAtAsync(spawnPt.X, spawnPt.Y);
bgMenu.Items.Add(addFiItem);
@@ -330,7 +360,7 @@ private void ShowContextMenu(ICanvasElement? element, LinkViewModel? link)
// ── Paste (always available; reads system clipboard at click-time) ────
bgMenu.Items.Add(new Separator());
- var pasteItem = new MenuItem { Header = "Paste\tCtrl+V" };
+ var pasteItem = new MenuItem { Header = CreateShortcutMenuHeader("Paste", "Ctrl+V") };
pasteItem.Click += (_, _) => _ = PasteElementsAsync(spawnPt.X, spawnPt.Y);
bgMenu.Items.Add(pasteItem);
@@ -855,12 +885,12 @@ private void ShowContextMenu(ICanvasElement? element, LinkViewModel? link)
el is NodeViewModel envm && !envm.IsParameterNode
|| el is FunctionInstanceViewModel);
- string extractHeader = extractCount > 1
- ? $"Extract {extractCount} Elements to Function Template…\tCtrl+Shift+M"
- : "Extract to Function Template…\tCtrl+Shift+M";
+ string extractDescription = extractCount > 1
+ ? $"Extract {extractCount} Elements to Function Template…"
+ : "Extract to Function Template…";
var capturedExtractElements = selElements;
- var extractItem = new MenuItem { Header = extractHeader };
+ var extractItem = new MenuItem { Header = CreateShortcutMenuHeader(extractDescription, "Ctrl+Shift+M") };
extractItem.Click += (_, _) => _ = vm.ExtractSelectionToFunctionTemplateAsync(capturedExtractElements);
menu.Items.Add(new Separator());
menu.Items.Add(extractItem);
@@ -1071,9 +1101,9 @@ el is NodeViewModel envm && !envm.IsParameterNode
_ => false,
})
: 1;
- string copyHeader = copyCount > 1 ? $"Copy {copyCount} Elements\tCtrl+C" : "Copy\tCtrl+C";
+ string copyDescription = copyCount > 1 ? $"Copy {copyCount} Elements" : "Copy";
- var copyItem = new MenuItem { Header = copyHeader };
+ var copyItem = new MenuItem { Header = CreateShortcutMenuHeader(copyDescription, "Ctrl+C") };
copyItem.Click += (_, _) =>
{
// Narrow selection to just this element when it isn't already multi-selected.
diff --git a/src/XTMF2.GUI/Controls/ModelSystemCanvas/ModelSystemCanvas.CopyPaste.cs b/src/XTMF2.GUI/Controls/ModelSystemCanvas/ModelSystemCanvas.CopyPaste.cs
index ea8c451..bd78a57 100644
--- a/src/XTMF2.GUI/Controls/ModelSystemCanvas/ModelSystemCanvas.CopyPaste.cs
+++ b/src/XTMF2.GUI/Controls/ModelSystemCanvas/ModelSystemCanvas.CopyPaste.cs
@@ -72,7 +72,8 @@ private CanvasElementDto BuildNodeDto(NodeViewModel nvm)
TypeName: node.Type?.AssemblyQualifiedName,
ParameterValue: paramValue,
IsScriptedParam: isScriptedParam,
- InlinedChildren: inlined);
+ InlinedChildren: inlined,
+ OriginalId: node.Id);
}
///
@@ -159,6 +160,7 @@ private async Task CopySelectedElementsAsync()
break;
case FunctionInstanceViewModel fi:
+ nodeToDtoIndex[fi.UnderlyingInstance] = dtos.Count;
_vm.TryExportFunctionTemplateSnapshot(fi.UnderlyingInstance.Template, out var instanceTemplateSnapshot);
dto = new CanvasElementDto(
CanvasElementKind.FunctionInstance,
@@ -166,7 +168,8 @@ private async Task CopySelectedElementsAsync()
(float)fi.Width, (float)fi.Height,
Name: fi.Name,
TemplateName: fi.TemplateName,
- EmbeddedTemplateSnapshot: instanceTemplateSnapshot);
+ EmbeddedTemplateSnapshot: instanceTemplateSnapshot,
+ OriginalId: fi.UnderlyingInstance.Id);
break;
case GhostNodeViewModel ghost:
@@ -209,22 +212,22 @@ private async Task CopySelectedElementsAsync()
}
}
- // Second pass: detect links where both origin and destination are in the copied set
- // and record them as cross-node links on the origin's DTO.
- if (nodeToDtoIndex.Count > 1 && _vm is not null)
+ // Second pass: record outgoing links from copied node-like origins. On paste,
+ // destinations are resolved first to pasted nodes by GUID, then to existing nodes
+ // in the target model system by the same original GUID.
+ if (nodeToDtoIndex.Count > 0 && _vm is not null)
{
foreach (var lvm in _vm.Links)
{
- if (lvm.Origin is not NodeViewModel originNvm) continue;
- if (!nodeToDtoIndex.TryGetValue(originNvm.UnderlyingNode, out var originIdx)) continue;
+ if (!TryGetCopyableNode(lvm.Origin, out var originNode, out _)) continue;
+ if (!nodeToDtoIndex.TryGetValue(originNode, out var originIdx)) continue;
- if (lvm.Destination is not NodeViewModel destNvm) continue;
- if (destNvm.IsInlined) continue;
- if (!nodeToDtoIndex.ContainsKey(destNvm.UnderlyingNode)) continue;
+ if (!TryGetLinkDestinationForRenderedBranch(lvm, out var destNode)) continue;
+ bool destIsInlined = lvm.Destination is NodeViewModel { IsInlined: true };
+ if (destIsInlined) continue;
- // Both ends are in the selection — record a cross-link.
var hookName = lvm.UnderlyingLink.OriginHook.Name;
- var destName = destNvm.UnderlyingNode.Name;
+ var destName = destNode.Name;
var origDto = dtos[originIdx];
var crossLinks = origDto.CrossLinks ?? new System.Collections.Generic.List();
if (origDto.CrossLinks is null)
@@ -232,7 +235,7 @@ private async Task CopySelectedElementsAsync()
origDto = origDto with { CrossLinks = crossLinks };
dtos[originIdx] = origDto;
}
- crossLinks.Add(new CrossNodeLinkDto(hookName, destName));
+ crossLinks.Add(new CrossNodeLinkDto(hookName, destName, destNode.Id));
}
}
@@ -261,4 +264,39 @@ private async Task PasteElementsAsync(double anchorX, double anchorY)
await _vm.PasteElementsAsync(payload, anchorX, anchorY);
}
+ private static bool TryGetCopyableNode(ICanvasElement? element, out Node node, out bool isInlined)
+ {
+ switch (element)
+ {
+ case NodeViewModel nvm:
+ node = nvm.UnderlyingNode;
+ isInlined = nvm.IsInlined;
+ return true;
+ case FunctionInstanceViewModel fivm:
+ node = fivm.UnderlyingInstance;
+ isInlined = false;
+ return true;
+ default:
+ node = null!;
+ isInlined = false;
+ return false;
+ }
+ }
+
+ private static bool TryGetLinkDestinationForRenderedBranch(LinkViewModel link, out Node destination)
+ {
+ switch (link.UnderlyingLink)
+ {
+ case SingleLink singleLink:
+ destination = singleLink.Destination;
+ return true;
+ case MultiLink multiLink when link.DestinationIndex >= 0 && link.DestinationIndex < multiLink.Destinations.Count:
+ destination = multiLink.Destinations[link.DestinationIndex];
+ return true;
+ default:
+ destination = null!;
+ return false;
+ }
+ }
+
}
diff --git a/src/XTMF2.GUI/Controls/ModelSystemCanvas/ModelSystemCanvas.Input.cs b/src/XTMF2.GUI/Controls/ModelSystemCanvas/ModelSystemCanvas.Input.cs
index 7ff1941..21c9878 100644
--- a/src/XTMF2.GUI/Controls/ModelSystemCanvas/ModelSystemCanvas.Input.cs
+++ b/src/XTMF2.GUI/Controls/ModelSystemCanvas/ModelSystemCanvas.Input.cs
@@ -77,6 +77,10 @@ protected override void OnKeyDown(KeyEventArgs e)
ClearMultiSelection();
e.Handled = true;
}
+ else if (TryHandleAddShortcut(e))
+ {
+ e.Handled = true;
+ }
else if ((e.Key is Key.Return or Key.Enter) && (e.KeyModifiers & KeyModifiers.Control) != 0)
{
if (_vm.SelectedElement is FunctionTemplateViewModel ftvm)
@@ -182,7 +186,7 @@ or FunctionTemplateViewModel or FunctionInstanceViewModel
}
}
else if (e.Key == Key.Up
- && _editingParamNode is null
+ && !IsParameterOrCommentEditing
&& (e.KeyModifiers & (KeyModifiers.Control | KeyModifiers.Alt | KeyModifiers.Shift)) == 0)
{
// Arrow Up: navigate to nearest element above current selection.
@@ -190,20 +194,24 @@ or FunctionTemplateViewModel or FunctionInstanceViewModel
e.Handled = true;
}
else if (e.Key == Key.Down
- && _editingParamNode is null
+ && !IsParameterOrCommentEditing
&& (e.KeyModifiers & (KeyModifiers.Control | KeyModifiers.Alt | KeyModifiers.Shift)) == 0)
{
// Arrow Down: navigate to nearest element below current selection.
NavigateToNextElement(NavigationDirection.Down);
e.Handled = true;
}
- else if (e.Key == Key.Left && (e.KeyModifiers & (KeyModifiers.Control | KeyModifiers.Alt | KeyModifiers.Shift)) == 0)
+ else if (e.Key == Key.Left
+ && !IsParameterOrCommentEditing
+ && (e.KeyModifiers & (KeyModifiers.Control | KeyModifiers.Alt | KeyModifiers.Shift)) == 0)
{
// Arrow Left: navigate to nearest element to the left of current selection.
NavigateToNextElement(NavigationDirection.Left);
e.Handled = true;
}
- else if (e.Key == Key.Right && (e.KeyModifiers & (KeyModifiers.Control | KeyModifiers.Alt | KeyModifiers.Shift)) == 0)
+ else if (e.Key == Key.Right
+ && !IsParameterOrCommentEditing
+ && (e.KeyModifiers & (KeyModifiers.Control | KeyModifiers.Alt | KeyModifiers.Shift)) == 0)
{
// Arrow Right: navigate to nearest element to the right of current selection.
NavigateToNextElement(NavigationDirection.Right);
@@ -236,6 +244,53 @@ or FunctionTemplateViewModel or FunctionInstanceViewModel
}
base.OnKeyDown(e);
}
+
+ private bool IsParameterOrCommentEditing =>
+ _editingParamNode is not null
+ || _editingCommentBlock is not null
+ || _editingCommentHeaderBlock is not null;
+
+ private bool TryHandleAddShortcut(KeyEventArgs e)
+ {
+ if (_vm is null
+ || _editingParamNode is not null
+ || _editingNameElement is not null
+ || _editingCommentBlock is not null
+ || _editingCommentHeaderBlock is not null
+ || (e.KeyModifiers & (KeyModifiers.Control | KeyModifiers.Alt | KeyModifiers.Shift)) != KeyModifiers.Control)
+ {
+ return false;
+ }
+
+ var spawnPt = GetKeyboardSpawnPoint();
+ switch (e.Key)
+ {
+ case Key.M:
+ _ = _vm.AddModuleAtAsync(spawnPt.X, spawnPt.Y);
+ return true;
+ case Key.I:
+ _ = _vm.AddFunctionInstanceAtAsync(spawnPt.X, spawnPt.Y);
+ return true;
+ case Key.T:
+ _ = _vm.AddFunctionTemplateAtAsync(spawnPt.X, spawnPt.Y);
+ return true;
+ case Key.N:
+ _vm.AddCommentBlockAt(spawnPt.X, spawnPt.Y);
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ private Point GetKeyboardSpawnPoint()
+ {
+ var sv = GetScrollViewer();
+ double viewportWidth = sv?.Viewport.Width ?? Bounds.Width;
+ double viewportHeight = sv?.Viewport.Height ?? Bounds.Height;
+ double x = ((sv?.Offset.X ?? 0) + viewportWidth / 2.0) / _scale;
+ double y = ((sv?.Offset.Y ?? 0) + viewportHeight / 2.0) / _scale;
+ return new Point(x, y);
+ }
protected override void OnPointerWheelChanged(PointerWheelEventArgs e)
{
@@ -726,6 +781,7 @@ protected override void OnPointerMoved(PointerEventArgs e)
if (_linkOrigin is not null)
{
_linkCurrentPos = mpos;
+ TryAutoScrollForDrag(svPos);
InvalidateVisual();
e.Handled = true;
return;
@@ -772,6 +828,7 @@ protected override void OnPointerMoved(PointerEventArgs e)
if (_selRectStart is not null)
{
_selRectCurrent = mpos;
+ TryAutoScrollForDrag(svPos);
InvalidateVisual();
e.Handled = true;
return;
@@ -1485,7 +1542,7 @@ private bool NavigateToNextParameter(bool backward)
return (nodeVm.X, nodeVm.Y + NodeHeaderHeight, NodeRenderWidth(nodeVm));
}
- var nodeHooks = nodeVm.UnderlyingNode.Hooks;
+ var nodeHooks = GetVisibleHooksForNode(nodeVm);
if (nodeHooks is null) return null;
int hookIdx = -1;
@@ -1570,6 +1627,38 @@ private struct ParameterHookInfo
public NodeViewModel? InlinedParam { get; set; }
}
+ private IReadOnlyList? GetVisibleHooksForNode(NodeViewModel nodeVm)
+ {
+ if (_nodeVisibleHooks.TryGetValue(nodeVm, out var visibleHooks))
+ {
+ return visibleHooks;
+ }
+
+ var nodeHooks = nodeVm.UnderlyingNode.Hooks;
+ if (nodeHooks is null) return null;
+ if (_vm?.ShowAllHooks == true || nodeVm.ShowHooks)
+ {
+ return nodeHooks;
+ }
+
+ var connected = new HashSet();
+ if (_vm is not null)
+ {
+ foreach (var link in _vm.Links)
+ {
+ if (ReferenceEquals(link.Origin, nodeVm))
+ {
+ connected.Add(link.UnderlyingLink.OriginHook);
+ }
+ }
+ }
+
+ return [.. nodeHooks.Where(h =>
+ h.Cardinality == HookCardinality.Single ||
+ h.Cardinality == HookCardinality.AtLeastOne ||
+ connected.Contains(h))];
+ }
+
///
/// Gets all editable parameter targets on the given element, in order.
/// Includes direct value-row editing for parameter nodes and inlined parameters for hooks.
diff --git a/src/XTMF2.GUI/ViewModels/CanvasClipboardPayload.cs b/src/XTMF2.GUI/ViewModels/CanvasClipboardPayload.cs
index 5099425..1dd20dd 100644
--- a/src/XTMF2.GUI/ViewModels/CanvasClipboardPayload.cs
+++ b/src/XTMF2.GUI/ViewModels/CanvasClipboardPayload.cs
@@ -54,6 +54,10 @@ internal sealed record CanvasClipboardPayload(
/// All element types share this record; unused fields are omitted from JSON.
///
/// Discriminator — one of the constants.
+///
+/// Stable ID of the copied model node, when the element represents a node-like object.
+/// Used to reconnect pasted origins to pasted or already-existing link destinations.
+///
///
/// Display name of the element. For ,
/// this is treated as a legacy fallback for older payloads.
@@ -122,22 +126,28 @@ internal sealed record CanvasElementDto(
[property: JsonPropertyName("crossLinks")] List? CrossLinks = null,
[property: JsonPropertyName("isTemplateCompanion")] bool IsTemplateCompanion = false,
[property: JsonPropertyName("body")] string? CommentBody = null,
- [property: JsonPropertyName("commentHeader")] string? CommentHeader = null
+ [property: JsonPropertyName("commentHeader")] string? CommentHeader = null,
+ [property: JsonPropertyName("originalId")] Guid? OriginalId = null
);
///
-/// A link from one copied to another node that
-/// was in the same copy selection. Stored per-origin so it can be recreated on paste
-/// once all nodes have been constructed.
+/// A link from one copied node-like element to another node-like element. Stored per-origin
+/// so it can be recreated on paste once all pasted elements have been constructed, or
+/// reconnected to an existing destination in the target model system by destination ID.
///
/// The name of the hook on the origin node.
///
/// The of the destination node within the same
-/// .
+/// . Used as a legacy fallback for older payloads.
+///
+///
+/// Stable ID of the original destination node. Used to find either the pasted destination
+/// from the same payload, or an already-existing destination in the target model system.
///
internal sealed record CrossNodeLinkDto(
[property: JsonPropertyName("hookName")] string HookName,
- [property: JsonPropertyName("destName")] string DestName);
+ [property: JsonPropertyName("destName")] string? DestName = null,
+ [property: JsonPropertyName("destId")] Guid? DestId = null);
///
/// A single function-parameter slot captured from a .
diff --git a/src/XTMF2.GUI/ViewModels/ModelSystemEditorViewModel.cs b/src/XTMF2.GUI/ViewModels/ModelSystemEditorViewModel.cs
index b42da35..5b00e0f 100644
--- a/src/XTMF2.GUI/ViewModels/ModelSystemEditorViewModel.cs
+++ b/src/XTMF2.GUI/ViewModels/ModelSystemEditorViewModel.cs
@@ -4069,7 +4069,7 @@ internal async Task PasteElementsAsync(CanvasClipboardPayload payload, double an
pastedTemplatesBySnapshot[element.EmbeddedTemplateSnapshot] = pastedTemplate;
}
- // Pass 1: create all nodes (without linking inlined children yet).
+ // Pass 1: create all node-like elements (without linking inlined children yet).
var createdNodes = new List<(CanvasElementDto Element, Node Node)>();
Node? firstNode = null;
foreach (var element in payload.Elements)
@@ -4090,7 +4090,9 @@ internal async Task PasteElementsAsync(CanvasClipboardPayload payload, double an
break;
case CanvasElementKind.FunctionInstance:
- PasteFunctionInstance(element, dx, dy, pastedTemplatesBySnapshot);
+ var pastedInstance = PasteFunctionInstance(element, dx, dy, pastedTemplatesBySnapshot);
+ if (pastedInstance is not null)
+ createdNodes.Add((element, pastedInstance));
break;
case CanvasElementKind.GhostNode:
@@ -4105,17 +4107,21 @@ internal async Task PasteElementsAsync(CanvasClipboardPayload payload, double an
foreach (var (element, node) in createdNodes)
PasteNodeInlinedChildren(element, node);
- // Pass 3: restore links between pasted nodes (cross-node links).
- if (createdNodes.Count > 1)
+ // Pass 3: restore links from pasted origins to pasted destinations, or to
+ // existing destination nodes in the target model system matched by GUID.
+ if (createdNodes.Count > 0)
{
var nameToNode = createdNodes
.Where(e => !string.IsNullOrWhiteSpace(e.Element.Name))
.ToDictionary(e => e.Element.Name!, e => e.Node);
+ var originalIdToPastedNode = createdNodes
+ .Where(e => e.Element.OriginalId.HasValue)
+ .ToDictionary(e => e.Element.OriginalId!.Value, e => e.Node);
foreach (var (element, originNode) in createdNodes)
{
foreach (var crossLink in element.CrossLinks ?? [])
{
- if (!nameToNode.TryGetValue(crossLink.DestName, out var destNode)) continue;
+ if (!TryResolvePastedLinkDestination(crossLink, originalIdToPastedNode, nameToNode, out var destNode)) continue;
var hook = originNode.Hooks?.FirstOrDefault(h => h.Name == crossLink.HookName);
if (hook is null) continue;
Session.AddLink(User, originNode, hook, destNode, out _, out _);
@@ -4135,6 +4141,72 @@ internal async Task PasteElementsAsync(CanvasClipboardPayload payload, double an
}
}
+ private bool TryResolvePastedLinkDestination(
+ CrossNodeLinkDto crossLink,
+ IReadOnlyDictionary originalIdToPastedNode,
+ IReadOnlyDictionary nameToPastedNode,
+ [NotNullWhen(true)] out Node? destination)
+ {
+ if (crossLink.DestId is Guid destId)
+ {
+ if (originalIdToPastedNode.TryGetValue(destId, out destination))
+ return true;
+
+ if (TryFindNodeById(destId, Session.ModelSystem.GlobalBoundary, out destination))
+ return true;
+ }
+
+ if (!string.IsNullOrWhiteSpace(crossLink.DestName)
+ && nameToPastedNode.TryGetValue(crossLink.DestName, out destination))
+ {
+ return true;
+ }
+
+ destination = null;
+ return false;
+ }
+
+ private static bool TryFindNodeById(Guid id, Boundary root, [NotNullWhen(true)] out Node? node)
+ {
+ var queue = new Queue();
+ queue.Enqueue(root);
+ while (queue.Count > 0)
+ {
+ var boundary = queue.Dequeue();
+ foreach (var module in boundary.Modules)
+ {
+ if (module.Id == id)
+ {
+ node = module;
+ return true;
+ }
+ }
+ foreach (var start in boundary.Starts)
+ {
+ if (start.Id == id)
+ {
+ node = start;
+ return true;
+ }
+ }
+ foreach (var instance in boundary.FunctionInstances)
+ {
+ if (instance.Id == id)
+ {
+ node = instance;
+ return true;
+ }
+ }
+ foreach (var template in boundary.FunctionTemplates)
+ queue.Enqueue(template.InternalModules);
+ foreach (var child in boundary.Boundaries)
+ queue.Enqueue(child);
+ }
+
+ node = null;
+ return false;
+ }
+
internal bool TryExportFunctionTemplateSnapshot(FunctionTemplate template, out string? snapshot)
{
snapshot = null;
@@ -4293,14 +4365,14 @@ private void PasteCommentBlockEntry(CanvasElementDto element, float dx, float dy
return ft;
}
- private void PasteFunctionInstance(
+ private FunctionInstance? PasteFunctionInstance(
CanvasElementDto element,
float dx,
float dy,
IReadOnlyDictionary pastedTemplatesBySnapshot)
{
var template = ResolveTemplateForPastedFunctionInstance(element, dx, dy, pastedTemplatesBySnapshot);
- if (template is null) return;
+ if (template is null) return null;
// Generate a unique name if needed.
string baseName = string.IsNullOrWhiteSpace(element.Name) ? "Function Instance" : element.Name;
@@ -4313,7 +4385,9 @@ private void PasteFunctionInstance(
float h = element.H > 0 ? element.H : 70f;
var loc = new Rectangle(element.X + dx, element.Y + dy, w, h);
- Session.AddFunctionInstance(User, _currentBoundary, template, name, loc, out _, out _);
+ return Session.AddFunctionInstance(User, _currentBoundary, template, name, loc, out var instance, out _)
+ ? instance
+ : null;
}
private FunctionTemplate? ResolveTemplateForPastedFunctionInstance(
diff --git a/src/XTMF2.GUI/Views/ModelSystemEditorView.axaml b/src/XTMF2.GUI/Views/ModelSystemEditorView.axaml
index 6c116d5..4e3f789 100644
--- a/src/XTMF2.GUI/Views/ModelSystemEditorView.axaml
+++ b/src/XTMF2.GUI/Views/ModelSystemEditorView.axaml
@@ -98,15 +98,33 @@
-
+
+
+
+
+
diff --git a/src/XTMF2.GUI/Views/TypePickerDialog.axaml b/src/XTMF2.GUI/Views/TypePickerDialog.axaml
index cdf1751..652a7ab 100644
--- a/src/XTMF2.GUI/Views/TypePickerDialog.axaml
+++ b/src/XTMF2.GUI/Views/TypePickerDialog.axaml
@@ -31,6 +31,7 @@
diff --git a/src/XTMF2.GUI/Views/TypePickerDialog.axaml.cs b/src/XTMF2.GUI/Views/TypePickerDialog.axaml.cs
index b6191dc..3ede997 100644
--- a/src/XTMF2.GUI/Views/TypePickerDialog.axaml.cs
+++ b/src/XTMF2.GUI/Views/TypePickerDialog.axaml.cs
@@ -385,6 +385,16 @@ private void UpdateFilter()
/// Pick button for the second type-argument slot (the ReturnType for IFunction).
private async void PickTypeArg1_Click(object? sender, RoutedEventArgs e) => await PickTypeArgAsync(1);
+ private void FilterBox_EnterPressed(object? sender, RoutedEventArgs e)
+ {
+ if (FilteredTypes.Count > 0)
+ {
+ TypeListBox.SelectedIndex = 0;
+ }
+
+ OK_Click(null, e);
+ }
+
private void OK_Click(object? sender, RoutedEventArgs e)
{
if (!CanOK) return;
diff --git a/src/XTMF2/RuntimeModules/BasicEvent.cs b/src/XTMF2/RuntimeModules/BasicEvent.cs
index ce9abab..8e92cff 100644
--- a/src/XTMF2/RuntimeModules/BasicEvent.cs
+++ b/src/XTMF2/RuntimeModules/BasicEvent.cs
@@ -21,63 +21,62 @@ You should have received a copy of the GNU General Public License
using System.Linq;
using System.Text;
-namespace XTMF2.RuntimeModules
+namespace XTMF2.RuntimeModules;
+
+[Module(Name = "Basic Event", DocumentationLink = "https://tmg.utoronto.ca/doc/2.0/xtmf2/modules/XTMF2/RuntimeModules/BasicEvent.html",
+Description = "Provides the ability for modules to invoke a set of other modules that are waiting for something to occur.")]
+public sealed class BasicEvent : BaseEvent
{
- [Module(Name = "Basic Event", DocumentationLink = "https://tmg.utoronto.ca/doc/2.0/xtmf2/modules/XTMF2/RuntimeModules/BasicEvent.html",
- Description = "Provides the ability for modules to invoke a set of other modules that are waiting for something to occur.")]
- public sealed class BasicEvent : BaseEvent
- {
- private readonly List _toInvoke = new List();
+ private readonly List _toInvoke = new List();
- public override void Invoke()
+ public override void Invoke()
+ {
+ // make a copy in case the invocation causes an additional registration
+ List copy;
+ lock(_toInvoke)
{
- // make a copy in case the invocation causes an additional registration
- List copy;
- lock(_toInvoke)
- {
- copy = _toInvoke.ToList();
- }
- foreach(var registered in copy)
- {
- registered.Invoke();
- }
+ copy = _toInvoke.ToList();
}
-
- public override void Register(Action module)
+ foreach(var registered in copy)
{
- lock(_toInvoke)
- {
- _toInvoke.Add(module);
- }
+ registered.Invoke();
}
}
- [Module(Name = "Basic Event", DocumentationLink = "https://tmg.utoronto.ca/doc/2.0/xtmf2/modules/XTMF2/RuntimeModules/BasicEvent.html",
- Description = "Provides the ability for modules to invoke a set of other modules that are waiting for something to occur.")]
- public sealed class BasicEvent : BaseEvent
+ public override void Register(Action module)
{
- private readonly List> _toInvoke = new List>();
+ lock(_toInvoke)
+ {
+ _toInvoke.Add(module);
+ }
+ }
+}
- public override void Invoke(Context context)
+[Module(Name = "Basic Event", DocumentationLink = "https://tmg.utoronto.ca/doc/2.0/xtmf2/modules/XTMF2/RuntimeModules/BasicEvent.html",
+Description = "Provides the ability for modules to invoke a set of other modules that are waiting for something to occur.")]
+public sealed class BasicEvent : BaseEvent
+{
+ private readonly List> _toInvoke = new List>();
+
+ public override void Invoke(Context context)
+ {
+ // make a copy in case the invocation causes an additional registration
+ List> copy;
+ lock (_toInvoke)
+ {
+ copy = _toInvoke.ToList();
+ }
+ foreach (var registered in copy)
{
- // make a copy in case the invocation causes an additional registration
- List> copy;
- lock (_toInvoke)
- {
- copy = _toInvoke.ToList();
- }
- foreach (var registered in copy)
- {
- registered.Invoke(context);
- }
+ registered.Invoke(context);
}
+ }
- public override void Register(Action module)
+ public override void Register(Action module)
+ {
+ lock (_toInvoke)
{
- lock (_toInvoke)
- {
- _toInvoke.Add(module);
- }
+ _toInvoke.Add(module);
}
}
-}
+}
\ No newline at end of file
diff --git a/src/XTMF2/RuntimeModules/BasicParameter.cs b/src/XTMF2/RuntimeModules/BasicParameter.cs
index 47f617e..1c42c2b 100644
--- a/src/XTMF2/RuntimeModules/BasicParameter.cs
+++ b/src/XTMF2/RuntimeModules/BasicParameter.cs
@@ -20,19 +20,18 @@ You should have received a copy of the GNU General Public License
using System.Collections.Generic;
using System.Text;
-namespace XTMF2.RuntimeModules
+namespace XTMF2.RuntimeModules;
+
+[Module(Name = "Basic Parameter", DocumentationLink = "https://tmg.utoronto.ca/doc/2.0/xtmf2/modules/XTMF2/RuntimeModules/BasicParameter.html",
+Description = "Provides the ability to have a value in a model system.")]
+public class BasicParameter : BaseFunction
{
- [Module(Name = "Basic Parameter", DocumentationLink = "https://tmg.utoronto.ca/doc/2.0/xtmf2/modules/XTMF2/RuntimeModules/BasicParameter.html",
- Description = "Provides the ability to have a value in a model system.")]
- public class BasicParameter : BaseFunction
- {
#pragma warning disable CS8618 // Non-nullable field is uninitialized. Consider declaring as nullable.
- public T Value;
+ public T Value;
#pragma warning restore CS8618 // Non-nullable field is uninitialized. Consider declaring as nullable.
- public override T Invoke()
- {
- return Value;
- }
+ public override T Invoke()
+ {
+ return Value;
}
}
diff --git a/src/XTMF2/RuntimeModules/Cache.cs b/src/XTMF2/RuntimeModules/Cache.cs
index 5fb9b2f..e1a5366 100644
--- a/src/XTMF2/RuntimeModules/Cache.cs
+++ b/src/XTMF2/RuntimeModules/Cache.cs
@@ -21,78 +21,77 @@ You should have received a copy of the GNU General Public License
using System.Text;
using System.Threading;
-namespace XTMF2.RuntimeModules
+namespace XTMF2.RuntimeModules;
+
+[Module(Name = "Cache", DocumentationLink = "https://tmg.utoronto.ca/doc/2.0/xtmf2/modules/XTMF2/RuntimeModules/Cache.html",
+Description = "Provides a way to keep the result of a function unless unloaded by an event.")]
+public sealed class Cache : BaseFunction, IDisposable
{
- [Module(Name = "Cache", DocumentationLink = "https://tmg.utoronto.ca/doc/2.0/xtmf2/modules/XTMF2/RuntimeModules/Cache.html",
- Description = "Provides a way to keep the result of a function unless unloaded by an event.")]
- public sealed class Cache : BaseFunction, IDisposable
- {
- private readonly Lock _lock = new Lock();
+ private readonly Lock _lock = new Lock();
#pragma warning disable CS8618 // Non-nullable field is uninitialized. Consider declaring as nullable.
- private T _cachedValue;
+ private T _cachedValue;
#pragma warning restore CS8618 // Non-nullable field is uninitialized. Consider declaring as nullable.
- private bool _initialized = false;
+ private bool _initialized = false;
- [SubModule(Required = true, Name = "Source", Description = "Get the cached data", Index = 0)]
- public IFunction? Source;
+ [SubModule(Required = true, Name = "Source", Description = "Get the cached data", Index = 0)]
+ public IFunction? Source;
- [SubModule(Required = false, Name = "Force Update", Description = "Invoke to force an update", Index = 1)]
- public IEvent? ForceUpdate;
+ [SubModule(Required = false, Name = "Force Update", Description = "Invoke to force an update", Index = 1)]
+ public IEvent? ForceUpdate;
- public override T Invoke()
+ public override T Invoke()
+ {
+ lock (_lock)
{
- lock (_lock)
+ if (!_initialized)
{
- if (!_initialized)
- {
- _cachedValue = Source!.Invoke();
- _initialized = true;
- GC.ReRegisterForFinalize(this);
- }
- return _cachedValue!;
+ _cachedValue = Source!.Invoke();
+ _initialized = true;
+ GC.ReRegisterForFinalize(this);
}
+ return _cachedValue!;
}
+ }
- public override bool RuntimeValidation(ref string? error)
+ public override bool RuntimeValidation(ref string? error)
+ {
+ ForceUpdate?.Register(() =>
{
- ForceUpdate?.Register(() =>
+ lock (_lock)
{
- lock (_lock)
- {
- _initialized = false;
- Dispose();
- }
- });
- return true;
- }
+ _initialized = false;
+ Dispose();
+ }
+ });
+ return true;
+ }
+
+ ~Cache()
+ {
+ Dispose(false);
+ }
- ~Cache()
+ private void Dispose(bool managed)
+ {
+ if(managed)
{
- Dispose(false);
+ GC.SuppressFinalize(this);
}
-
- private void Dispose(bool managed)
+ lock (_lock)
{
- if(managed)
- {
- GC.SuppressFinalize(this);
- }
- lock (_lock)
+ if (_initialized && _cachedValue is IDisposable disposable)
{
- if (_initialized && _cachedValue is IDisposable disposable)
- {
- disposable.Dispose();
- }
- _cachedValue = default!;
- _initialized = false;
+ disposable.Dispose();
}
+ _cachedValue = default!;
+ _initialized = false;
}
+ }
- public void Dispose()
- {
- Dispose(true);
- }
+ public void Dispose()
+ {
+ Dispose(true);
}
}
diff --git a/src/XTMF2/RuntimeModules/CombineContext.cs b/src/XTMF2/RuntimeModules/CombineContext.cs
index d0a88bb..5145ed5 100644
--- a/src/XTMF2/RuntimeModules/CombineContext.cs
+++ b/src/XTMF2/RuntimeModules/CombineContext.cs
@@ -20,103 +20,102 @@ You should have received a copy of the GNU General Public License
using System.Collections.Generic;
using System.Text;
-namespace XTMF2.RuntimeModules
-{
- [Module(Name = "Combine Context From No Context", DocumentationLink = "https://tmg.utoronto.ca/doc/2.0/xtmf2/modules/XTMF2/RuntimeModules/CombineContextFromNoContext.html",
+namespace XTMF2.RuntimeModules;
+
+[Module(Name = "Combine Context From No Context", DocumentationLink = "https://tmg.utoronto.ca/doc/2.0/xtmf2/modules/XTMF2/RuntimeModules/CombineContextFromNoContext.html",
Description = "Combines the contexts as derived from First and Second and invokes To Invoke with the combined context.")]
- public sealed class CombineContextAFromNoContext : BaseAction
+public sealed class CombineContextAFromNoContext : BaseAction
+{
+ [SubModule(Name = "First", Required = true, Index = 0, Description = "The first context to use.")]
+ public IFunction? First;
+
+ [SubModule(Name = "Second", Required = true, Index = 1, Description = "The second context to use.")]
+ public IFunction? Second;
+
+ [SubModule(Name = "To Invoke", Required = true, Index = 2, Description = "The module to invoke with the combined context.")]
+ public IAction<(Context1, Context2)>? ToInvoke;
+
+ public override void Invoke()
{
- [SubModule(Name = "First", Required = true, Index = 0, Description = "The first context to use.")]
- public IFunction? First;
-
- [SubModule(Name = "Second", Required = true, Index = 1, Description = "The second context to use.")]
- public IFunction? Second;
-
- [SubModule(Name = "To Invoke", Required = true, Index = 2, Description = "The module to invoke with the combined context.")]
- public IAction<(Context1, Context2)> ?ToInvoke;
-
- public override void Invoke()
- {
- ToInvoke!.Invoke((First!.Invoke(), Second!.Invoke()));
- }
+ ToInvoke!.Invoke((First!.Invoke(), Second!.Invoke()));
}
+}
- [Module(Name = "Combine Context From No Context", DocumentationLink = "https://tmg.utoronto.ca/doc/2.0/xtmf2/modules/XTMF2/RuntimeModules/CombineContextFromNoContext.html",
+[Module(Name = "Combine Context From No Context", DocumentationLink = "https://tmg.utoronto.ca/doc/2.0/xtmf2/modules/XTMF2/RuntimeModules/CombineContextFromNoContext.html",
Description = "Combines the contexts as derived from First and Second and invokes To Invoke with the combined context.")]
- public sealed class CombineContextA : BaseAction
+public sealed class CombineContextA : BaseAction
+{
+ [SubModule(Name = "Second", Required = true, Index = 0, Description = "The second context to use.")]
+ public IFunction? Second;
+ [SubModule(Name = "To Invoke", Required = true, Index = 1, Description = "The module to invoke with the combined context.")]
+ public IAction<(Context1, Context2)>? ToInvoke;
+
+ public override void Invoke(Context1 context)
{
- [SubModule(Name = "Second", Required = true, Index = 0, Description = "The second context to use.")]
- public IFunction? Second;
- [SubModule(Name = "To Invoke", Required = true, Index = 1, Description = "The module to invoke with the combined context.")]
- public IAction<(Context1, Context2)>? ToInvoke;
-
- public override void Invoke(Context1 context)
- {
- ToInvoke!.Invoke((context, Second!.Invoke()));
- }
+ ToInvoke!.Invoke((context, Second!.Invoke()));
}
+}
- [Module(Name = "Combine Context From No Context", DocumentationLink = "https://tmg.utoronto.ca/doc/2.0/xtmf2/modules/XTMF2/RuntimeModules/CombineContextFromNoContext.html",
+[Module(Name = "Combine Context From No Context", DocumentationLink = "https://tmg.utoronto.ca/doc/2.0/xtmf2/modules/XTMF2/RuntimeModules/CombineContextFromNoContext.html",
Description = "Combines the contexts as derived from First and Second and invokes To Invoke with the combined context.")]
- public sealed class CombineContextAFromContext : BaseAction
+public sealed class CombineContextAFromContext : BaseAction
+{
+ [SubModule(Name = "Second", Required = true, Index = 0, Description = "The second context to use.")]
+ public IFunction? Second;
+ [SubModule(Name = "To Invoke", Required = true, Index = 1, Description = "The module to invoke with the combined context.")]
+ public IAction<(Context1, Context2)>? ToInvoke;
+
+ public override void Invoke(Context1 context)
{
- [SubModule(Name = "Second", Required = true, Index = 0, Description = "The second context to use.")]
- public IFunction? Second;
- [SubModule(Name = "To Invoke", Required = true, Index = 1, Description = "The module to invoke with the combined context.")]
- public IAction<(Context1, Context2)>? ToInvoke;
-
- public override void Invoke(Context1 context)
- {
- ToInvoke!.Invoke((context, Second!.Invoke(context)));
- }
+ ToInvoke!.Invoke((context, Second!.Invoke(context)));
}
+}
- [Module(Name = "Combine Context From No Context", DocumentationLink = "https://tmg.utoronto.ca/doc/2.0/xtmf2/modules/XTMF2/RuntimeModules/CombineContextFromNoContext.html",
+[Module(Name = "Combine Context From No Context", DocumentationLink = "https://tmg.utoronto.ca/doc/2.0/xtmf2/modules/XTMF2/RuntimeModules/CombineContextFromNoContext.html",
Description = "Combines the contexts as derived from First and Second and invokes To Invoke with the combined context.")]
- public sealed class CombineContextFFromNoContext : BaseFunction
- {
- [SubModule(Name = "First", Required = true, Index = 0, Description = "The first context to use.")]
- public IFunction? First;
+public sealed class CombineContextFFromNoContext : BaseFunction
+{
+ [SubModule(Name = "First", Required = true, Index = 0, Description = "The first context to use.")]
+ public IFunction? First;
- [SubModule(Name = "Second", Required = true, Index = 1, Description = "The second context to use.")]
- public IFunction? Second;
+ [SubModule(Name = "Second", Required = true, Index = 1, Description = "The second context to use.")]
+ public IFunction? Second;
- [SubModule(Name = "To Invoke", Required = true, Index = 2, Description = "The module to invoke with the combined context.")]
- public IFunction<(Context1, Context2), Return>? ToInvoke;
+ [SubModule(Name = "To Invoke", Required = true, Index = 2, Description = "The module to invoke with the combined context.")]
+ public IFunction<(Context1, Context2), Return>? ToInvoke;
- public override Return Invoke()
- {
- return ToInvoke!.Invoke((First!.Invoke(), Second!.Invoke()));
- }
+ public override Return Invoke()
+ {
+ return ToInvoke!.Invoke((First!.Invoke(), Second!.Invoke()));
}
+}
- [Module(Name = "Combine Context From No Context", DocumentationLink = "https://tmg.utoronto.ca/doc/2.0/xtmf2/modules/XTMF2/RuntimeModules/CombineContextFromNoContext.html",
+[Module(Name = "Combine Context From No Context", DocumentationLink = "https://tmg.utoronto.ca/doc/2.0/xtmf2/modules/XTMF2/RuntimeModules/CombineContextFromNoContext.html",
Description = "Combines the contexts as derived from First and Second and invokes To Invoke with the combined context.")]
- public sealed class CombineContexF : BaseFunction
+public sealed class CombineContextF : BaseFunction
+{
+ [SubModule(Name = "Second", Required = true, Index = 0, Description = "The second context to use.")]
+ public IFunction? Second;
+ [SubModule(Name = "To Invoke", Required = true, Index = 1, Description = "The module to invoke with the combined context.")]
+ public IFunction<(Context1, Context2), Return>? ToInvoke;
+
+ public override Return Invoke(Context1 context)
{
- [SubModule(Name = "Second", Required = true, Index = 0, Description = "The second context to use.")]
- public IFunction? Second;
- [SubModule(Name = "To Invoke", Required = true, Index = 1, Description = "The module to invoke with the combined context.")]
- public IFunction<(Context1, Context2), Return>? ToInvoke;
-
- public override Return Invoke(Context1 context)
- {
- return ToInvoke!.Invoke((context, Second!.Invoke()));
- }
+ return ToInvoke!.Invoke((context, Second!.Invoke()));
}
+}
- [Module(Name = "Combine Context From No Context", DocumentationLink = "https://tmg.utoronto.ca/doc/2.0/xtmf2/modules/XTMF2/RuntimeModules/CombineContextFromNoContext.html",
+[Module(Name = "Combine Context From No Context", DocumentationLink = "https://tmg.utoronto.ca/doc/2.0/xtmf2/modules/XTMF2/RuntimeModules/CombineContextFromNoContext.html",
Description = "Combines the contexts as derived from First and Second and invokes To Invoke with the combined context.")]
- public sealed class CombineContextFFromContext : BaseFunction
+public sealed class CombineContextFFromContext : BaseFunction
+{
+ [SubModule(Name = "Second", Required = true, Index = 0, Description = "The second context to use.")]
+ public IFunction? Second;
+ [SubModule(Name = "To Invoke", Required = true, Index = 1, Description = "The module to invoke with the combined context.")]
+ public IFunction<(Context1, Context2), Return>? ToInvoke;
+
+ public override Return Invoke(Context1 context)
{
- [SubModule(Name = "Second", Required = true, Index = 0, Description = "The second context to use.")]
- public IFunction? Second;
- [SubModule(Name = "To Invoke", Required = true, Index = 1, Description = "The module to invoke with the combined context.")]
- public IFunction<(Context1, Context2), Return>? ToInvoke;
-
- public override Return Invoke(Context1 context)
- {
- return ToInvoke!.Invoke((context, Second!.Invoke(context)));
- }
+ return ToInvoke!.Invoke((context, Second!.Invoke(context)));
}
}
diff --git a/src/XTMF2/RuntimeModules/DirectoryPath.cs b/src/XTMF2/RuntimeModules/DirectoryPath.cs
index ace38f9..02914ff 100644
--- a/src/XTMF2/RuntimeModules/DirectoryPath.cs
+++ b/src/XTMF2/RuntimeModules/DirectoryPath.cs
@@ -20,25 +20,24 @@ You should have received a copy of the GNU General Public License
using System.Collections.Generic;
using System.Text;
-namespace XTMF2.RuntimeModules
-{
- [Module(Name = "Directory Path", DocumentationLink = "https://tmg.utoronto.ca/doc/2.0/xtmf2/modules/XTMF2/RuntimeModules/DirectoryPath.html",
+namespace XTMF2.RuntimeModules;
+
+[Module(Name = "Directory Path", DocumentationLink = "https://tmg.utoronto.ca/doc/2.0/xtmf2/modules/XTMF2/RuntimeModules/DirectoryPath.html",
Description = "Provides the ability to specify a directory path recursively.")]
- public sealed class DirectoryPath : BaseFunction
- {
- [SubModule(Required = false, Name = "Parent", Description = "Optional parent directory", Index = 0)]
- public DirectoryPath? Parent;
+public sealed class DirectoryPath : BaseFunction
+{
+ [SubModule(Required = false, Name = "Parent", Description = "Optional parent directory", Index = 0)]
+ public DirectoryPath? Parent;
- [Parameter(Name = "Name", DefaultValue = "directoryName", Description = "The path to add to the Parent path", Index = 1)]
- public IFunction? Path;
+ [Parameter(Name = "Name", DefaultValue = "directoryName", Description = "The path to add to the Parent path", Index = 1)]
+ public IFunction? Path;
- public override string Invoke()
+ public override string Invoke()
+ {
+ if(Parent != null)
{
- if(Parent != null)
- {
- return System.IO.Path.Combine(Parent.Invoke(), Path!.Invoke());
- }
- return Path!.Invoke();
+ return System.IO.Path.Combine(Parent.Invoke(), Path!.Invoke());
}
+ return Path!.Invoke();
}
}
diff --git a/src/XTMF2/RuntimeModules/Execute.cs b/src/XTMF2/RuntimeModules/Execute.cs
index f5c4f65..61a21e1 100644
--- a/src/XTMF2/RuntimeModules/Execute.cs
+++ b/src/XTMF2/RuntimeModules/Execute.cs
@@ -21,93 +21,92 @@ You should have received a copy of the GNU General Public License
using System.Text;
using System.Threading.Tasks;
-namespace XTMF2.RuntimeModules
-{
- [Module(Name = "Execute", DocumentationLink = "https://tmg.utoronto.ca/doc/2.0/xtmf2/modules/XTMF2/RuntimeModules/Execute.html",
+namespace XTMF2.RuntimeModules;
+
+[Module(Name = "Execute", DocumentationLink = "https://tmg.utoronto.ca/doc/2.0/xtmf2/modules/XTMF2/RuntimeModules/Execute.html",
Description = "Provides a way to execute a series of actions in order, optionally in parallel or with multiple iterations.")]
- public class Execute : BaseAction
- {
- [Parameter(DefaultValue = "false", Name = "Parallel Execution", Required = false, Index = 0)]
- public IFunction? ParallelExecution;
+public class Execute : BaseAction
+{
+ [Parameter(DefaultValue = "false", Name = "Parallel Execution", Required = false, Index = 0)]
+ public IFunction? ParallelExecution;
- [Parameter(DefaultValue = "1", Name = "Iterations", Required = false, Index = 1)]
- public IFunction? Iterations;
+ [Parameter(DefaultValue = "1", Name = "Iterations", Required = false, Index = 1)]
+ public IFunction? Iterations;
- [SubModule(Name = "Current Iteration", Required = false, Description = "Place to store the current iteration", Index = 2, PassesExecution = true)]
- public ISetableValue? CurrentIteration;
+ [SubModule(Name = "Current Iteration", Required = false, Description = "Place to store the current iteration", Index = 2, PassesExecution = true)]
+ public ISetableValue? CurrentIteration;
- [SubModule(Name = "To Execute", Description = "The modules in order to execute", Index = 3, PassesExecution = true)]
- public IAction[]? ToInvoke;
+ [SubModule(Name = "To Execute", Description = "The modules in order to execute", Index = 3, PassesExecution = true)]
+ public IAction[]? ToInvoke;
- public override void Invoke()
+ public override void Invoke()
+ {
+ var iterations = Iterations?.Invoke() ?? 1;
+ var parallel = ParallelExecution?.Invoke() ?? false;
+ if (parallel)
{
- var iterations = Iterations?.Invoke() ?? 1;
- var parallel = ParallelExecution?.Invoke() ?? false;
- if (parallel)
+ for (int iteration = 0; iteration < iterations; iteration++)
{
- for (int iteration = 0; iteration < iterations; iteration++)
+ CurrentIteration?.Set(iteration);
+ Parallel.ForEach(ToInvoke!, (action) =>
{
- CurrentIteration?.Set(iteration);
- Parallel.ForEach(ToInvoke!, (action) =>
- {
- action?.Invoke();
- });
- }
+ action?.Invoke();
+ });
}
- else
+ }
+ else
+ {
+ for (int iteration = 0; iteration < iterations; iteration++)
{
- for (int iteration = 0; iteration < iterations; iteration++)
+ CurrentIteration?.Set(iteration);
+ foreach (var module in ToInvoke!)
{
- CurrentIteration?.Set(iteration);
- foreach (var module in ToInvoke!)
- {
- module?.Invoke();
- }
+ module?.Invoke();
}
}
}
}
+}
- [Module(Name = "Execute", DocumentationLink = "https://tmg.utoronto.ca/doc/2.0/xtmf2/modules/XTMF2/RuntimeModules/Execute.html",
+[Module(Name = "Execute", DocumentationLink = "https://tmg.utoronto.ca/doc/2.0/xtmf2/modules/XTMF2/RuntimeModules/Execute.html",
Description = "Provides a way to execute a series of actions in order, optionally in parallel or with multiple iterations.")]
- public class Execute : BaseAction
- {
- [Parameter(DefaultValue = "false", Name = "Parallel Execution", Required = false, Index = 0)]
- public IFunction? ParallelExecution;
+public class Execute : BaseAction
+{
+ [Parameter(DefaultValue = "false", Name = "Parallel Execution", Required = false, Index = 0)]
+ public IFunction? ParallelExecution;
- [Parameter(DefaultValue = "1", Name = "Iterations", Required = false, Index = 1)]
- public IFunction? Iterations;
+ [Parameter(DefaultValue = "1", Name = "Iterations", Required = false, Index = 1)]
+ public IFunction? Iterations;
- [SubModule(Name = "Current Iteration", Required = false, Description = "Place to store the current iteration", Index = 2, PassesExecution = true)]
- public ISetableValue? CurrentIteration;
+ [SubModule(Name = "Current Iteration", Required = false, Description = "Place to store the current iteration", Index = 2, PassesExecution = true)]
+ public ISetableValue? CurrentIteration;
- [SubModule(Name = "To Execute", Description = "The modules in order to execute", Index = 3, PassesExecution = true)]
- public IAction[]? ToInvoke;
+ [SubModule(Name = "To Execute", Description = "The modules in order to execute", Index = 3, PassesExecution = true)]
+ public IAction[]? ToInvoke;
- public override void Invoke(Context context)
+ public override void Invoke(Context context)
+ {
+ var iterations = Iterations?.Invoke() ?? 1;
+ var parallel = ParallelExecution?.Invoke() ?? false;
+ if (parallel)
{
- var iterations = Iterations?.Invoke() ?? 1;
- var parallel = ParallelExecution?.Invoke() ?? false;
- if (parallel)
+ for (int iteration = 0; iteration < iterations; iteration++)
{
- for (int iteration = 0; iteration < iterations; iteration++)
+ CurrentIteration?.Set(iteration);
+ Parallel.ForEach(ToInvoke!, (action) =>
{
- CurrentIteration?.Set(iteration);
- Parallel.ForEach(ToInvoke!, (action) =>
- {
- action?.Invoke(context);
- });
- }
+ action?.Invoke(context);
+ });
}
- else
+ }
+ else
+ {
+ for (int iteration = 0; iteration < iterations; iteration++)
{
- for (int iteration = 0; iteration < iterations; iteration++)
+ CurrentIteration?.Set(iteration);
+ foreach (var module in ToInvoke!)
{
- CurrentIteration?.Set(iteration);
- foreach (var module in ToInvoke!)
- {
- module?.Invoke(context);
- }
+ module?.Invoke(context);
}
}
}
diff --git a/src/XTMF2/RuntimeModules/ExecuteActionsThenFunction.cs b/src/XTMF2/RuntimeModules/ExecuteActionsThenFunction.cs
index 25f9e85..6fa6d19 100644
--- a/src/XTMF2/RuntimeModules/ExecuteActionsThenFunction.cs
+++ b/src/XTMF2/RuntimeModules/ExecuteActionsThenFunction.cs
@@ -21,8 +21,7 @@ You should have received a copy of the GNU General Public License
using System.Text;
using System.Threading.Tasks;
-namespace XTMF2.RuntimeModules
-{
+namespace XTMF2.RuntimeModules;
[Module(Name = "Execute Actions Then Function", DocumentationLink = "https://tmg.utoronto.ca/doc/2.0/xtmf2/modules/XTMF2/RuntimeModules/ExecuteActionsThenFunction.html",
Description = "Allows you to execute actions before calling a function. This allows you to ")]
public class ExecuteActionsThenFunction : BaseFunction
@@ -55,4 +54,4 @@ public override Return Invoke()
return EndWith!.Invoke();
}
}
-}
+
diff --git a/src/XTMF2/RuntimeModules/Fail.cs b/src/XTMF2/RuntimeModules/Fail.cs
index 7a4c12a..84a3965 100644
--- a/src/XTMF2/RuntimeModules/Fail.cs
+++ b/src/XTMF2/RuntimeModules/Fail.cs
@@ -20,83 +20,82 @@ You should have received a copy of the GNU General Public License
using System.Collections.Generic;
using System.Text;
-namespace XTMF2.RuntimeModules
-{
- [Module(Name = "Fail", Description = "Crash the model run with a message.",
- DocumentationLink = "https://tmg.utoronto.ca/doc/2.0/xtmf2/modules/XTMF2/RuntimeModules/Fail.html")]
- public sealed class FailA : BaseAction
- {
- [Parameter(Name = "Message", Index = 0, Description = "The message to fail with.", DefaultValue = "Invalid state!")]
- public IFunction? Message;
+namespace XTMF2.RuntimeModules;
- public override void Invoke()
- {
- throw new XTMFRuntimeException(this, Message?.Invoke());
- }
- }
+[Module(Name = "Fail", Description = "Crash the model run with a message.",
+ DocumentationLink = "https://tmg.utoronto.ca/doc/2.0/xtmf2/modules/XTMF2/RuntimeModules/Fail.html")]
+public sealed class FailA : BaseAction
+{
+ [Parameter(Name = "Message", Index = 0, Description = "The message to fail with.", DefaultValue = "Invalid state!")]
+ public IFunction? Message;
- [Module(Name = "Fail", Description = "Crash the model run with a message.",
- DocumentationLink = "https://tmg.utoronto.ca/doc/2.0/xtmf2/modules/XTMF2/RuntimeModules/Fail.html")]
- public sealed class FailA : BaseAction
+ public override void Invoke()
{
- [Parameter(Name = "Message", Index = 0, Description = "The message to fail with.", DefaultValue = "Invalid state!")]
- public IFunction? Message;
-
- public override void Invoke(Context context)
- {
- throw new XTMFRuntimeException(this, Message?.Invoke());
- }
+ throw new XTMFRuntimeException(this, Message?.Invoke());
}
+}
- [Module(Name = "Fail", Description = "Crash the model run with a message.",
+[Module(Name = "Fail", Description = "Crash the model run with a message.",
DocumentationLink = "https://tmg.utoronto.ca/doc/2.0/xtmf2/modules/XTMF2/RuntimeModules/Fail.html")]
- public sealed class FailWithContextA : BaseAction
- {
- [Parameter(Name = "Message", Index = 0, Description = "The message to fail with.", DefaultValue = "Invalid state!")]
- public IFunction? Message;
+public sealed class FailA : BaseAction
+{
+ [Parameter(Name = "Message", Index = 0, Description = "The message to fail with.", DefaultValue = "Invalid state!")]
+ public IFunction? Message;
- public override void Invoke(Context context)
- {
- throw new XTMFRuntimeException(this, Message?.Invoke(context));
- }
+ public override void Invoke(Context context)
+ {
+ throw new XTMFRuntimeException(this, Message?.Invoke());
}
+}
- [Module(Name = "Fail", Description = "Crash the model run with a message.",
- DocumentationLink = "https://tmg.utoronto.ca/doc/2.0/xtmf2/modules/XTMF2/RuntimeModules/Fail.html")]
- public sealed class FailF : BaseFunction
- {
- [Parameter(Name = "Message", Index = 0, Description = "The message to fail with.", DefaultValue = "Invalid state!")]
- public IFunction? Message;
+[Module(Name = "Fail", Description = "Crash the model run with a message.",
+DocumentationLink = "https://tmg.utoronto.ca/doc/2.0/xtmf2/modules/XTMF2/RuntimeModules/Fail.html")]
+public sealed class FailWithContextA : BaseAction
+{
+ [Parameter(Name = "Message", Index = 0, Description = "The message to fail with.", DefaultValue = "Invalid state!")]
+ public IFunction? Message;
- public override Return Invoke()
- {
- throw new XTMFRuntimeException(this, Message?.Invoke());
- }
+ public override void Invoke(Context context)
+ {
+ throw new XTMFRuntimeException(this, Message?.Invoke(context));
}
+}
- [Module(Name = "Fail", Description = "Crash the model run with a message.",
- DocumentationLink = "https://tmg.utoronto.ca/doc/2.0/xtmf2/modules/XTMF2/RuntimeModules/Fail.html")]
- public sealed class FailF : BaseFunction
- {
- [Parameter(Name = "Message", Index = 0, Description = "The message to fail with.", DefaultValue = "Invalid state!")]
- public IFunction? Message;
+[Module(Name = "Fail", Description = "Crash the model run with a message.",
+DocumentationLink = "https://tmg.utoronto.ca/doc/2.0/xtmf2/modules/XTMF2/RuntimeModules/Fail.html")]
+public sealed class FailF : BaseFunction
+{
+ [Parameter(Name = "Message", Index = 0, Description = "The message to fail with.", DefaultValue = "Invalid state!")]
+ public IFunction? Message;
- public override Return Invoke(Context context)
- {
- throw new XTMFRuntimeException(this, Message?.Invoke());
- }
+ public override Return Invoke()
+ {
+ throw new XTMFRuntimeException(this, Message?.Invoke());
}
+}
- [Module(Name = "Fail", Description = "Crash the model run with a message.",
+[Module(Name = "Fail", Description = "Crash the model run with a message.",
DocumentationLink = "https://tmg.utoronto.ca/doc/2.0/xtmf2/modules/XTMF2/RuntimeModules/Fail.html")]
- public sealed class FailWithContextF : BaseFunction
+public sealed class FailF : BaseFunction
+{
+ [Parameter(Name = "Message", Index = 0, Description = "The message to fail with.", DefaultValue = "Invalid state!")]
+ public IFunction? Message;
+
+ public override Return Invoke(Context context)
{
- [Parameter(Name = "Message", Index = 0, Description = "The message to fail with.", DefaultValue = "Invalid state!")]
- public IFunction? Message;
+ throw new XTMFRuntimeException(this, Message?.Invoke());
+ }
+}
- public override Return Invoke(Context context)
- {
- throw new XTMFRuntimeException(this, Message?.Invoke(context));
- }
+[Module(Name = "Fail", Description = "Crash the model run with a message.",
+DocumentationLink = "https://tmg.utoronto.ca/doc/2.0/xtmf2/modules/XTMF2/RuntimeModules/Fail.html")]
+public sealed class FailWithContextF : BaseFunction
+{
+ [Parameter(Name = "Message", Index = 0, Description = "The message to fail with.", DefaultValue = "Invalid state!")]
+ public IFunction? Message;
+
+ public override Return Invoke(Context context)
+ {
+ throw new XTMFRuntimeException(this, Message?.Invoke(context));
}
}
diff --git a/src/XTMF2/RuntimeModules/If.cs b/src/XTMF2/RuntimeModules/If.cs
index 9f81a06..3afc5dd 100644
--- a/src/XTMF2/RuntimeModules/If.cs
+++ b/src/XTMF2/RuntimeModules/If.cs
@@ -20,8 +20,7 @@ You should have received a copy of the GNU General Public License
using System.Collections.Generic;
using System.Text;
-namespace XTMF2.RuntimeModules
-{
+namespace XTMF2.RuntimeModules;
[Module(Name = "If", DocumentationLink = "https://tmg.utoronto.ca/doc/2.0/xtmf2/modules/XTMF2/RuntimeModules/If.html",
Description = "Provides a way to conditionally execute. If the condition is true or false different functions will be invoked.")]
public sealed class IfF : BaseFunction
@@ -183,4 +182,3 @@ public override void Invoke(Context context)
}
}
}
-}
diff --git a/src/XTMF2/RuntimeModules/Ignore.cs b/src/XTMF2/RuntimeModules/Ignore.cs
index b487a67..591a3fe 100644
--- a/src/XTMF2/RuntimeModules/Ignore.cs
+++ b/src/XTMF2/RuntimeModules/Ignore.cs
@@ -21,8 +21,7 @@ You should have received a copy of the GNU General Public License
using System.Text;
using XTMF2;
-namespace XTMF2.RuntimeModules
-{
+namespace XTMF2.RuntimeModules;
[Module(Name = "Ignore Result", DocumentationLink = "https://tmg.utoronto.ca/doc/2.0/xtmf2/modules/XTMF2/RuntimeModules/IgnoreResult.html",
Description = "Ignore the result of a function call. This allows you to call functions from an action.")]
public class IgnoreResult : BaseAction
@@ -74,4 +73,3 @@ public override Return Invoke(Context context)
return ToInvoke!.Invoke();
}
}
-}
diff --git a/src/XTMF2/RuntimeModules/Log.cs b/src/XTMF2/RuntimeModules/Log.cs
index f998cae..ac9f210 100644
--- a/src/XTMF2/RuntimeModules/Log.cs
+++ b/src/XTMF2/RuntimeModules/Log.cs
@@ -23,77 +23,76 @@ You should have received a copy of the GNU General Public License
using System.Text.Unicode;
using System.Threading;
-namespace XTMF2.RuntimeModules
-{
- [Module(Name = "Log", DocumentationLink = "https://tmg.utoronto.ca/doc/2.0/xtmf2/modules/XTMF2/RuntimeModules/Log.html",
+namespace XTMF2.RuntimeModules;
+
+[Module(Name = "Log", DocumentationLink = "https://tmg.utoronto.ca/doc/2.0/xtmf2/modules/XTMF2/RuntimeModules/Log.html",
Description = "Provides functionality for synchronizing the writing of events to a log and providing time stamps.")]
- public sealed class Log : BaseAction, IFunction, IDisposable
- {
- [SubModule(Required = true, Name = "LogStream", Description = "The stream to save the log to.", Index = 0)]
- public IFunction? LogStream;
+public sealed class Log : BaseAction, IFunction, IDisposable
+{
+ [SubModule(Required = true, Name = "LogStream", Description = "The stream to save the log to.", Index = 0)]
+ public IFunction? LogStream;
- private readonly Lock _writeLock = new();
+ private readonly Lock _writeLock = new();
- private StreamWriter? _writer;
+ private StreamWriter? _writer;
- private bool _alwaysFlush = false;
+ private bool _alwaysFlush = false;
- public override void Invoke(string message)
+ public override void Invoke(string message)
+ {
+ lock (_writeLock)
{
- lock (_writeLock)
+ if(_writer is null)
{
- if(_writer is null)
+ if (LogStream?.Invoke() is WriteStream writeStream)
{
- if (LogStream?.Invoke() is WriteStream writeStream)
- {
- // Check to see if we need to always flush the stream.
- _alwaysFlush = writeStream is RunStatusStream;
- var encoding = _alwaysFlush ? new UTF8Encoding(false, true) : Encoding.UTF8;
- _writer = new StreamWriter(writeStream, encoding, 0x4000, false);
- }
- else
- {
- throw new XTMFRuntimeException(this, "Unable to create a write stream to store the log into!");
- }
+ // Check to see if we need to always flush the stream.
+ _alwaysFlush = writeStream is RunStatusStream;
+ var encoding = _alwaysFlush ? new UTF8Encoding(false, true) : Encoding.UTF8;
+ _writer = new StreamWriter(writeStream, encoding, 0x4000, false);
}
- // don't block while writing
- _writer.Write(TimeStampMessage(message));
- if (_alwaysFlush)
+ else
{
- _writer.Flush();
+ throw new XTMFRuntimeException(this, "Unable to create a write stream to store the log into!");
}
}
+ // don't block while writing
+ _writer.Write(TimeStampMessage(message));
+ if (_alwaysFlush)
+ {
+ _writer.Flush();
+ }
}
+ }
- Log IFunction.Invoke()
- {
- return this;
- }
+ Log IFunction.Invoke()
+ {
+ return this;
+ }
- private static string TimeStampMessage(string message)
- {
- var now = DateTime.Now;
- return $"[{now.Hour:D2}:{now.Minute:D2}:{now.Second:D2}] {message}";
- }
+ private static string TimeStampMessage(string message)
+ {
+ var now = DateTime.Now;
+ return $"[{now.Hour:D2}:{now.Minute:D2}:{now.Second:D2}] {message}";
+ }
- private void Dispose(bool managed)
+ private void Dispose(bool managed)
+ {
+ if(managed)
{
- if(managed)
- {
- GC.SuppressFinalize(this);
- }
- _writer?.Dispose();
- _writer = null;
+ GC.SuppressFinalize(this);
}
+ _writer?.Dispose();
+ _writer = null;
+ }
- public void Dispose()
- {
- Dispose(true);
- }
+ public void Dispose()
+ {
+ Dispose(true);
+ }
- ~Log()
- {
- Dispose(false);
- }
+ ~Log()
+ {
+ Dispose(false);
}
}
diff --git a/src/XTMF2/RuntimeModules/OpenReadStreamFromFile.cs b/src/XTMF2/RuntimeModules/OpenReadStreamFromFile.cs
index aab73e2..6379352 100644
--- a/src/XTMF2/RuntimeModules/OpenReadStreamFromFile.cs
+++ b/src/XTMF2/RuntimeModules/OpenReadStreamFromFile.cs
@@ -20,58 +20,57 @@ You should have received a copy of the GNU General Public License
using System.IO;
using System.Text;
-namespace XTMF2.RuntimeModules
-{
- [Module(Name = "Open Read Stream From File", DocumentationLink = "https://tmg.utoronto.ca/doc/2.0/xtmf2/modules/XTMF2/RuntimeModules/OpenReadStreamFromFile.html",
+namespace XTMF2.RuntimeModules;
+
+[Module(Name = "Open Read Stream From File", DocumentationLink = "https://tmg.utoronto.ca/doc/2.0/xtmf2/modules/XTMF2/RuntimeModules/OpenReadStreamFromFile.html",
Description = "Provides the ability to read a file from the path given to it via the context.")]
- public class OpenReadStreamFromFile : BaseFunction
- {
- [Parameter(DefaultValue = "true", Description = "True if the file should be checked at runtime to ensure that it exists.", Index=1,
- Name="Check File Exists At Run Start", Required = true)]
- public IFunction? CheckFileExistsAtRunStart;
+public class OpenReadStreamFromFile : BaseFunction
+{
+ [Parameter(DefaultValue = "true", Description = "True if the file should be checked at runtime to ensure that it exists.", Index=1,
+ Name="Check File Exists At Run Start", Required = true)]
+ public IFunction? CheckFileExistsAtRunStart;
- [Parameter(DefaultValue = "", Description = "The path to the file to load.", Index = 0,
- Name = "File Path", Required = true)]
- public IFunction? FilePath;
+ [Parameter(DefaultValue = "", Description = "The path to the file to load.", Index = 0,
+ Name = "File Path", Required = true)]
+ public IFunction? FilePath;
- public override ReadStream Invoke()
+ public override ReadStream Invoke()
+ {
+ try
{
- try
+ if (FilePath?.Invoke() is string path)
{
- if (FilePath?.Invoke() is string path)
- {
- return new ReadStream(File.OpenRead(path));
- }
- else
- {
- throw new XTMFRuntimeException(this, "No path was given to open a ReadStream from!");
- }
+ return new ReadStream(File.OpenRead(path));
}
- catch(IOException e)
+ else
{
- throw new XTMFRuntimeException(this, e.Message, e);
+ throw new XTMFRuntimeException(this, "No path was given to open a ReadStream from!");
}
}
+ catch(IOException e)
+ {
+ throw new XTMFRuntimeException(this, e.Message, e);
+ }
+ }
- public override bool RuntimeValidation(ref string? error)
+ public override bool RuntimeValidation(ref string? error)
+ {
+ if(CheckFileExistsAtRunStart?.Invoke() == true)
{
- if(CheckFileExistsAtRunStart?.Invoke() == true)
+ if (FilePath?.Invoke() is string filePath)
{
- if (FilePath?.Invoke() is string filePath)
+ if (!File.Exists(filePath))
{
- if (!File.Exists(filePath))
- {
- error = $"The file '{filePath}' does not exist!";
- return false;
- }
- }
- else
- {
-
+ error = $"The file '{filePath}' does not exist!";
return false;
}
}
- return true;
+ else
+ {
+ error = "No path was given to open a ReadStream from!";
+ return false;
+ }
}
+ return true;
}
}
diff --git a/src/XTMF2/RuntimeModules/OpenReadStreamFromMemoryPipe.cs b/src/XTMF2/RuntimeModules/OpenReadStreamFromMemoryPipe.cs
index 9bd5ee4..7e1a30f 100644
--- a/src/XTMF2/RuntimeModules/OpenReadStreamFromMemoryPipe.cs
+++ b/src/XTMF2/RuntimeModules/OpenReadStreamFromMemoryPipe.cs
@@ -17,19 +17,18 @@ You should have received a copy of the GNU General Public License
along with XTMF2. If not, see .
*/
-namespace XTMF2.RuntimeModules
+namespace XTMF2.RuntimeModules;
+
+[Module(Name = "Open Read Stream From Memory Pipe",
+ Description = "Gets a ReadStream that is backed by memory.",
+ DocumentationLink = "https://tmg.utoronto.ca/doc/2.0/xtmf2/modules/XTMF2/RuntimeModules/OpenReadStreamFromMemoryPipe.html")]
+public sealed class OpenReadStreamFromMemoryPipe : BaseFunction
{
- [Module(Name = "Open Read Stream From Memory Pipe",
- Description = "Gets a ReadStream that is backed by memory.",
- DocumentationLink = "https://tmg.utoronto.ca/doc/2.0/xtmf2/modules/XTMF2/RuntimeModules/OpenReadStreamFromMemoryPipe.html")]
- public sealed class OpenReadStreamFromMemoryPipe : BaseFunction
- {
- [SubModule(Index=0,Name = "Pipe", Description = "The pipe to read from", Required = true)]
- public IFunction? Pipe;
+ [SubModule(Index=0,Name = "Pipe", Description = "The pipe to read from", Required = true)]
+ public IFunction? Pipe;
- public override ReadStream Invoke()
- {
- return Pipe!.Invoke().GetReadStream(this);
- }
+ public override ReadStream Invoke()
+ {
+ return Pipe!.Invoke().GetReadStream(this);
}
}
diff --git a/src/XTMF2/RuntimeModules/OpenWriteStreamFromFile.cs b/src/XTMF2/RuntimeModules/OpenWriteStreamFromFile.cs
index 3351269..ffe15fe 100644
--- a/src/XTMF2/RuntimeModules/OpenWriteStreamFromFile.cs
+++ b/src/XTMF2/RuntimeModules/OpenWriteStreamFromFile.cs
@@ -21,42 +21,41 @@ You should have received a copy of the GNU General Public License
using System.IO;
using System.Text;
-namespace XTMF2.RuntimeModules
-{
- [Module(Name = "Open Write Stream From File", DocumentationLink = "https://tmg.utoronto.ca/doc/2.0/xtmf2/modules/XTMF2/RuntimeModules/OpenWriteStreamFromFile.html",
+namespace XTMF2.RuntimeModules;
+
+[Module(Name = "Open Write Stream From File", DocumentationLink = "https://tmg.utoronto.ca/doc/2.0/xtmf2/modules/XTMF2/RuntimeModules/OpenWriteStreamFromFile.html",
Description = "Provides a WriteStream to the given file name from context.")]
- public class OpenWriteStreamFromFile : BaseFunction
- {
+public class OpenWriteStreamFromFile : BaseFunction
+{
- [Parameter(DefaultValue = "", Description = "The path to the file to load.", Index = 0,
- Name = "File Path", Required = true)]
- public IFunction? FilePath;
+ [Parameter(DefaultValue = "", Description = "The path to the file to load.", Index = 0,
+ Name = "File Path", Required = true)]
+ public IFunction? FilePath;
- public override WriteStream Invoke()
+ public override WriteStream Invoke()
+ {
+ var context = FilePath!.Invoke();
+ if(String.IsNullOrWhiteSpace(context))
{
- var context = FilePath!.Invoke();
- if(String.IsNullOrWhiteSpace(context))
- {
- throw new XTMFRuntimeException(this, "The provided file path was empty!");
- }
- try
+ throw new XTMFRuntimeException(this, "The provided file path was empty!");
+ }
+ try
+ {
+ FileInfo f = new FileInfo(context);
+ var dir = f.Directory;
+ if(dir is null)
{
- FileInfo f = new FileInfo(context);
- var dir = f.Directory;
- if(dir is null)
- {
- throw new XTMFRuntimeException(this, $"The provided file path is not a valid file path!");
- }
- if (!dir.Exists)
- {
- dir.Create();
- }
- return new WriteStream(File.Open(context, FileMode.Create, FileAccess.Write));
+ throw new XTMFRuntimeException(this, $"The provided file path is not a valid file path!");
}
- catch(IOException e)
+ if (!dir.Exists)
{
- throw new XTMFRuntimeException(this, e.Message, e);
+ dir.Create();
}
+ return new WriteStream(File.Open(context, FileMode.Create, FileAccess.Write));
+ }
+ catch(IOException e)
+ {
+ throw new XTMFRuntimeException(this, e.Message, e);
}
}
}
diff --git a/src/XTMF2/RuntimeModules/OpenWriteStreamFromMemoryPipe.cs b/src/XTMF2/RuntimeModules/OpenWriteStreamFromMemoryPipe.cs
index 4f9361a..dbea1b0 100644
--- a/src/XTMF2/RuntimeModules/OpenWriteStreamFromMemoryPipe.cs
+++ b/src/XTMF2/RuntimeModules/OpenWriteStreamFromMemoryPipe.cs
@@ -18,19 +18,18 @@ You should have received a copy of the GNU General Public License
*/
-namespace XTMF2.RuntimeModules
+namespace XTMF2.RuntimeModules;
+
+[Module(Name = "Open Write Stream From Memory Pipe",
+Description = "Gets a WriteStream that is backed by memory.",
+DocumentationLink = "https://tmg.utoronto.ca/doc/2.0/xtmf2/modules/XTMF2/RuntimeModules/OpenWriteStreamFromMemoryPipe.html")]
+public sealed class OpenWriteStreamFromMemoryPipe : BaseFunction
{
- [Module(Name = "Open Write Stream From Memory Pipe",
- Description = "Gets a WriteStream that is backed by memory.",
- DocumentationLink = "https://tmg.utoronto.ca/doc/2.0/xtmf2/modules/XTMF2/RuntimeModules/OpenWriteStreamFromMemoryPipe.html")]
- public sealed class OpenWriteStreamFromMemoryPipe : BaseFunction
- {
- [SubModule(Index = 0, Name = "Pipe", Description = "The pipe to write to", Required = true)]
- public IFunction? Pipe;
+ [SubModule(Index = 0, Name = "Pipe", Description = "The pipe to write to", Required = true)]
+ public IFunction? Pipe;
- public override WriteStream Invoke()
- {
- return Pipe!.Invoke().GetWriteStream(this);
- }
+ public override WriteStream Invoke()
+ {
+ return Pipe!.Invoke().GetWriteStream(this);
}
}
diff --git a/src/XTMF2/RuntimeModules/ReportInvocation.cs b/src/XTMF2/RuntimeModules/ReportInvocation.cs
index 6fcba20..a19fb25 100644
--- a/src/XTMF2/RuntimeModules/ReportInvocation.cs
+++ b/src/XTMF2/RuntimeModules/ReportInvocation.cs
@@ -21,8 +21,7 @@ You should have received a copy of the GNU General Public License
using System.Text;
using XTMF2.Configuration;
-namespace XTMF2.RuntimeModules
-{
+namespace XTMF2.RuntimeModules;
[Module(Name = "Report Invocation", DocumentationLink = "https://tmg.utoronto.ca/doc/2.0/xtmf2/modules/XTMF2/RuntimeModules/ReportInvocation.html",
Description = "Reports to XTMF that the model system has run through this point.")]
public sealed class ReportFunctionInvocation : BaseFunction
@@ -143,4 +142,3 @@ public override void Invoke(Context context)
ToInvoke!.Invoke(context);
}
}
-}
diff --git a/src/XTMF2/RuntimeModules/ScriptedParameter.cs b/src/XTMF2/RuntimeModules/ScriptedParameter.cs
index f5d0407..a36d85d 100644
--- a/src/XTMF2/RuntimeModules/ScriptedParameter.cs
+++ b/src/XTMF2/RuntimeModules/ScriptedParameter.cs
@@ -20,64 +20,63 @@ You should have received a copy of the GNU General Public License
using System.Diagnostics.CodeAnalysis;
using XTMF2.ModelSystemConstruct;
-namespace XTMF2.RuntimeModules
+namespace XTMF2.RuntimeModules;
+
+[Module(Name = "Scripted Parameter", DocumentationLink = "https://tmg.utoronto.ca/doc/2.0/xtmf2/modules/XTMF2/RuntimeModules/ScriptedParameter.html",
+Description = "Provides the ability to have a value that is calculated in an expression.")]
+public sealed class ScriptedParameter : BaseFunction
{
- [Module(Name = "Scripted Parameter", DocumentationLink = "https://tmg.utoronto.ca/doc/2.0/xtmf2/modules/XTMF2/RuntimeModules/ScriptedParameter.html",
- Description = "Provides the ability to have a value that is calculated in an expression.")]
- public sealed class ScriptedParameter : BaseFunction
- {
#pragma warning disable CS8618 // Non-nullable field is uninitialized. Consider declaring as nullable.
- public ParameterExpression Expression;
+ public ParameterExpression Expression;
#pragma warning restore CS8618 // Non-nullable field is uninitialized. Consider declaring as nullable.
- public override T Invoke()
+ public override T Invoke()
+ {
+ string? error = null;
+ if (!Expression.IsCompatible(typeof(T), ref error))
{
- string? error = null;
- if (!Expression.IsCompatible(typeof(T), ref error))
- {
- Throw(error);
- }
- var ret = Expression.GetValue(this, typeof(T), ref error);
- if (ret is null)
- {
- ThrowGotNull();
- }
- return (T)ret;
+ Throw(error);
}
-
- ///
- ///
- ///
- ///
- /// The requested error message.
- [DoesNotReturn]
- private void Throw(string error)
+ var ret = Expression.GetValue(this, typeof(T), ref error);
+ if (ret is null)
{
- throw new XTMFRuntimeException(this, error);
+ ThrowGotNull();
}
+ return (T)ret;
+ }
- ///
- ///
- ///
- ///
- [DoesNotReturn]
- private void ThrowGotNull()
+ ///
+ ///
+ ///
+ ///
+ /// The requested error message.
+ [DoesNotReturn]
+ private void Throw(string error)
+ {
+ throw new XTMFRuntimeException(this, error);
+ }
+
+ ///
+ ///
+ ///
+ ///
+ [DoesNotReturn]
+ private void ThrowGotNull()
+ {
+ throw new XTMFRuntimeException(this, $"Unable to get a {typeof(T).FullName} value from expression '{Expression.Representation}'!");
+ }
+
+ public override bool RuntimeValidation(ref string? error)
+ {
+ if(!base.RuntimeValidation(ref error))
{
- throw new XTMFRuntimeException(this, $"Unable to get a {typeof(T).FullName} value from expression '{Expression.Representation}'!");
+ return false;
}
-
- public override bool RuntimeValidation(ref string? error)
+ if (Expression is null)
{
- if(!base.RuntimeValidation(ref error))
- {
- return false;
- }
- if (Expression is null)
- {
- error = "Expression is not set!";
- return false;
- }
- return true;
+ error = "Expression is not set!";
+ return false;
}
+ return true;
}
}
diff --git a/src/XTMF2/RuntimeModules/SetableParameter.cs b/src/XTMF2/RuntimeModules/SetableParameter.cs
index 647c171..82b2c73 100644
--- a/src/XTMF2/RuntimeModules/SetableParameter.cs
+++ b/src/XTMF2/RuntimeModules/SetableParameter.cs
@@ -20,20 +20,19 @@ You should have received a copy of the GNU General Public License
using System.Collections.Generic;
using System.Text;
-namespace XTMF2.RuntimeModules
+namespace XTMF2.RuntimeModules;
+
+[Module(Name = "Setable Parameter", Description = "A basic data store of variable type that can be set.",
+ DocumentationLink = "https://tmg.utoronto.ca/doc/2.0/xtmf2/modules/XTMF2/RuntimeModules/SetableParameter.html")]
+public sealed class SetableParameter : BasicParameter, ISetableValue
{
- [Module(Name = "Setable Parameter", Description = "A basic data store of variable type that can be set.",
- DocumentationLink = "https://tmg.utoronto.ca/doc/2.0/xtmf2/modules/XTMF2/RuntimeModules/SetableParameter.html")]
- public sealed class SetableParameter : BasicParameter, ISetableValue
+ public T Get()
{
- public T Get()
- {
- return Value;
- }
+ return Value;
+ }
- public void Set(T value)
- {
- Value = value;
- }
+ public void Set(T value)
+ {
+ Value = value;
}
}
diff --git a/src/XTMF2/RuntimeModules/StartModule.cs b/src/XTMF2/RuntimeModules/StartModule.cs
index 30be9d9..f8eadcd 100644
--- a/src/XTMF2/RuntimeModules/StartModule.cs
+++ b/src/XTMF2/RuntimeModules/StartModule.cs
@@ -20,8 +20,7 @@ You should have received a copy of the GNU General Public License
using System.Collections.Generic;
using System.Text;
-namespace XTMF2.RuntimeModules
-{
+namespace XTMF2.RuntimeModules;
///
/// The type used for a start node
///
@@ -37,4 +36,3 @@ public override void Invoke()
ToExecute?.Invoke();
}
}
-}
diff --git a/src/XTMF2/RuntimeModules/StepUp.cs b/src/XTMF2/RuntimeModules/StepUp.cs
index 3567729..3fc7d11 100644
--- a/src/XTMF2/RuntimeModules/StepUp.cs
+++ b/src/XTMF2/RuntimeModules/StepUp.cs
@@ -20,47 +20,46 @@ You should have received a copy of the GNU General Public License
using System.Collections.Generic;
using System.Text;
-namespace XTMF2.RuntimeModules
-{
- [Module(Name = "Step Return Up", DocumentationLink = "https://tmg.utoronto.ca/doc/2.0/xtmf2/modules/XTMF2/RuntimeModules/StepReturnUp.html",
+namespace XTMF2.RuntimeModules;
+
+[Module(Name = "Step Return Up", DocumentationLink = "https://tmg.utoronto.ca/doc/2.0/xtmf2/modules/XTMF2/RuntimeModules/StepReturnUp.html",
Description = "Converts the result of a function to the expected type from the calling module.")]
- public sealed class StepReturnUp : BaseFunction
- where Original : ConvertTo
- {
- [SubModule(Required = true, Name = "ToInvoke", Description = "Invoke with converted context", Index = 0)]
- public IFunction? ToInvoke;
+public sealed class StepReturnUp : BaseFunction
+ where Original : ConvertTo
+{
+ [SubModule(Required = true, Name = "ToInvoke", Description = "Invoke with converted context", Index = 0)]
+ public IFunction? ToInvoke;
- public override ConvertTo Invoke()
- {
- return ToInvoke!.Invoke();
- }
+ public override ConvertTo Invoke()
+ {
+ return ToInvoke!.Invoke();
}
+}
- [Module(Name = "Step Return Up", DocumentationLink = "https://tmg.utoronto.ca/doc/2.0/xtmf2/modules/XTMF2/RuntimeModules/StepReturnUp.html",
+[Module(Name = "Step Return Up", DocumentationLink = "https://tmg.utoronto.ca/doc/2.0/xtmf2/modules/XTMF2/RuntimeModules/StepReturnUp.html",
Description = "Converts the result of a function to the expected type from the calling module.")]
- public sealed class StepReturnUp : BaseFunction
- where Original : ConvertTo
- {
- [SubModule(Required = true, Name = "ToInvoke", Description = "Invoke with converted context", Index = 0)]
- public IFunction? ToInvoke;
+public sealed class StepReturnUp : BaseFunction
+ where Original : ConvertTo
+{
+ [SubModule(Required = true, Name = "ToInvoke", Description = "Invoke with converted context", Index = 0)]
+ public IFunction? ToInvoke;
- public override ConvertTo Invoke(Context context)
- {
- return ToInvoke!.Invoke(context);
- }
+ public override ConvertTo Invoke(Context context)
+ {
+ return ToInvoke!.Invoke(context);
}
+}
- [Module(Name = "Step Return Up", DocumentationLink = "https://tmg.utoronto.ca/doc/2.0/xtmf2/modules/XTMF2/RuntimeModules/StepReturnUp.html",
+[Module(Name = "Step Return Up", DocumentationLink = "https://tmg.utoronto.ca/doc/2.0/xtmf2/modules/XTMF2/RuntimeModules/StepReturnUp.html",
Description = "Converts the result of a function to the expected type from the calling module.")]
- public sealed class StepActionUp : BaseAction
- where Original : ConvertTo
- {
- [SubModule(Required = true, Name = "ToInvoke", Description = "Invoke with converted context", Index = 0)]
- public IAction? ToInvoke;
+public sealed class StepActionUp : BaseAction
+ where Original : ConvertTo
+{
+ [SubModule(Required = true, Name = "ToInvoke", Description = "Invoke with converted context", Index = 0)]
+ public IAction? ToInvoke;
- public override void Invoke(Original context)
- {
- ToInvoke!.Invoke(context);
- }
+ public override void Invoke(Original context)
+ {
+ ToInvoke!.Invoke(context);
}
}
diff --git a/src/XTMF2/RuntimeModules/WriteToLog.cs b/src/XTMF2/RuntimeModules/WriteToLog.cs
index 8c16ef1..dddd0ce 100644
--- a/src/XTMF2/RuntimeModules/WriteToLog.cs
+++ b/src/XTMF2/RuntimeModules/WriteToLog.cs
@@ -20,132 +20,129 @@ You should have received a copy of the GNU General Public License
using System.Collections.Generic;
using System.Text;
-namespace XTMF2.RuntimeModules
+namespace XTMF2.RuntimeModules;
+[Module(Name = "Write to Log", DocumentationLink = "https://tmg.utoronto.ca/doc/2.0/xtmf2/modules/XTMF2/RuntimeModules/WriteToLog.html",
+ Description = "Writes the provided message to the log and then invokes the next step.")]
+public sealed class WriteToLogF : BaseFunction
{
- [Module(Name = "Write to Log", DocumentationLink = "https://tmg.utoronto.ca/doc/2.0/xtmf2/modules/XTMF2/RuntimeModules/WriteToLog.html",
- Description = "Writes the provided mess to the log and then invokes the next step.")]
- public sealed class WriteToLogF : BaseFunction
- {
- [SubModule(Required = true, Name = "Log", Description = "The log that will be written to.", Index = 0)]
- public IFunction Log = null!;
+ [SubModule(Required = true, Name = "Log", Description = "The log that will be written to.", Index = 0)]
+ public IFunction Log = null!;
- [SubModule(Required = true, Name = "To Invoke", Description = "The function to execute after writing to the log.", Index = 1, PassesExecution = true)]
- public IFunction ToInvoke = null!;
+ [SubModule(Required = true, Name = "To Invoke", Description = "The function to execute after writing to the log.", Index = 1, PassesExecution = true)]
+ public IFunction ToInvoke = null!;
- [Parameter(Required = true, Name = "Message", Description = "The message to write to the log.", DefaultValue = "", Index = 2)]
- public IFunction Message = null!;
+ [Parameter(Required = true, Name = "Message", Description = "The message to write to the log.", DefaultValue = "", Index = 2)]
+ public IFunction Message = null!;
- public override Return Invoke()
- {
- var log = Log.Invoke();
- log.Invoke(Message.Invoke());
- return ToInvoke.Invoke();
- }
+ public override Return Invoke()
+ {
+ var log = Log.Invoke();
+ log.Invoke(Message.Invoke());
+ return ToInvoke.Invoke();
}
+}
- [Module(Name = "Write to Log", DocumentationLink = "https://tmg.utoronto.ca/doc/2.0/xtmf2/modules/XTMF2/RuntimeModules/WriteToLog.html",
- Description = "Writes the provided mess to the log and then invokes the next step.")]
- public sealed class WriteToLogF : BaseFunction
- {
- [SubModule(Required = true, Name = "Log", Description = "The log that will be written to.", Index = 0)]
- public IFunction Log = null!;
+[Module(Name = "Write to Log", DocumentationLink = "https://tmg.utoronto.ca/doc/2.0/xtmf2/modules/XTMF2/RuntimeModules/WriteToLog.html",
+ Description = "Writes the provided message to the log and then invokes the next step.")]
+public sealed class WriteToLogF : BaseFunction
+{
+ [SubModule(Required = true, Name = "Log", Description = "The log that will be written to.", Index = 0)]
+ public IFunction Log = null!;
- [SubModule(Required = true, Name = "To Invoke", Description = "The function to execute after writing to the log.", Index = 1, PassesExecution = true)]
- public IFunction ToInvoke = null!;
+ [SubModule(Required = true, Name = "To Invoke", Description = "The function to execute after writing to the log.", Index = 1, PassesExecution = true)]
+ public IFunction ToInvoke = null!;
- [Parameter(Required = true, Name = "Message", Description = "The message to write to the log.", DefaultValue = "", Index = 2)]
- public IFunction Message = null!;
+ [Parameter(Required = true, Name = "Message", Description = "The message to write to the log.", DefaultValue = "", Index = 2)]
+ public IFunction Message = null!;
- public override Return Invoke(Context context)
- {
- var log = Log.Invoke();
- log.Invoke(Message.Invoke());
- return ToInvoke.Invoke(context);
- }
+ public override Return Invoke(Context context)
+ {
+ var log = Log.Invoke();
+ log.Invoke(Message.Invoke());
+ return ToInvoke.Invoke(context);
}
+}
- [Module(Name = "Write to Log", DocumentationLink = "https://tmg.utoronto.ca/doc/2.0/xtmf2/modules/XTMF2/RuntimeModules/WriteToLog.html",
- Description = "Writes the provided mess to the log and then invokes the next step.")]
- public sealed class WriteToLogBasedOnContextF : BaseFunction
- {
- [SubModule(Required = true, Name = "Log", Description = "The log that will be written to.", Index = 0)]
- public IFunction Log = null!;
+[Module(Name = "Write to Log", DocumentationLink = "https://tmg.utoronto.ca/doc/2.0/xtmf2/modules/XTMF2/RuntimeModules/WriteToLog.html",
+ Description = "Writes the provided message to the log and then invokes the next step.")]
+public sealed class WriteToLogBasedOnContextF : BaseFunction
+{
+ [SubModule(Required = true, Name = "Log", Description = "The log that will be written to.", Index = 0)]
+ public IFunction Log = null!;
- [SubModule(Required = true, Name = "To Invoke", Description = "The function to execute after writing to the log.", Index = 1, PassesExecution = true)]
- public IFunction ToInvoke = null!;
+ [SubModule(Required = true, Name = "To Invoke", Description = "The function to execute after writing to the log.", Index = 1, PassesExecution = true)]
+ public IFunction ToInvoke = null!;
- [Parameter(Required = true, Name = "Message", Description = "The message to write to the log.", DefaultValue = "", Index = 2)]
- public IFunction Message = null!;
+ [Parameter(Required = true, Name = "Message", Description = "The message to write to the log.", DefaultValue = "", Index = 2)]
+ public IFunction Message = null!;
- public override Return Invoke(Context context)
- {
- var log = Log.Invoke();
- log.Invoke(Message.Invoke(context));
- return ToInvoke.Invoke(context);
- }
+ public override Return Invoke(Context context)
+ {
+ var log = Log.Invoke();
+ log.Invoke(Message.Invoke(context));
+ return ToInvoke.Invoke(context);
}
+}
- [Module(Name = "Write to Log", DocumentationLink = "https://tmg.utoronto.ca/doc/2.0/xtmf2/modules/XTMF2/RuntimeModules/WriteToLog.html",
- Description = "Writes the provided mess to the log and then invokes the next step.")]
- public sealed class WriteToLogA : BaseAction
- {
- [SubModule(Required = true, Name = "Log", Description = "The log that will be written to.", Index = 0)]
- public IFunction Log = null!;
+[Module(Name = "Write to Log", DocumentationLink = "https://tmg.utoronto.ca/doc/2.0/xtmf2/modules/XTMF2/RuntimeModules/WriteToLog.html",
+ Description = "Writes the provided message to the log and then invokes the next step.")]
+public sealed class WriteToLogA : BaseAction
+{
+ [SubModule(Required = true, Name = "Log", Description = "The log that will be written to.", Index = 0)]
+ public IFunction Log = null!;
- [SubModule(Required = false, Name = "To Invoke", Description = "The function to execute after writing to the log.", Index = 1, PassesExecution = true)]
- public IAction? ToInvoke;
+ [SubModule(Required = false, Name = "To Invoke", Description = "The function to execute after writing to the log.", Index = 1, PassesExecution = true)]
+ public IAction? ToInvoke;
- [Parameter(Required = true, Name = "Message", Description = "The message to write to the log.", DefaultValue = "", Index = 2)]
- public IFunction Message = null!;
+ [Parameter(Required = true, Name = "Message", Description = "The message to write to the log.", DefaultValue = "", Index = 2)]
+ public IFunction Message = null!;
- public override void Invoke()
- {
- var log = Log.Invoke();
- log.Invoke(Message.Invoke());
- ToInvoke?.Invoke();
- }
+ public override void Invoke()
+ {
+ var log = Log.Invoke();
+ log.Invoke(Message.Invoke());
+ ToInvoke?.Invoke();
}
+}
- [Module(Name = "Write to Log", DocumentationLink = "https://tmg.utoronto.ca/doc/2.0/xtmf2/modules/XTMF2/RuntimeModules/WriteToLog.html",
- Description = "Writes the provided mess to the log and then invokes the next step.")]
- public sealed class WriteToLogA : BaseAction
- {
- [SubModule(Required = true, Name = "Log", Description = "The log that will be written to.", Index = 0)]
- public IFunction Log = null!;
+[Module(Name = "Write to Log", DocumentationLink = "https://tmg.utoronto.ca/doc/2.0/xtmf2/modules/XTMF2/RuntimeModules/WriteToLog.html",
+ Description = "Writes the provided message to the log and then invokes the next step.")]
+public sealed class WriteToLogA : BaseAction
+{
+ [SubModule(Required = true, Name = "Log", Description = "The log that will be written to.", Index = 0)]
+ public IFunction Log = null!;
- [SubModule(Required = false, Name = "To Invoke", Description = "The function to execute after writing to the log.", Index = 1, PassesExecution = true)]
- public IAction? ToInvoke;
+ [SubModule(Required = false, Name = "To Invoke", Description = "The function to execute after writing to the log.", Index = 1, PassesExecution = true)]
+ public IAction? ToInvoke;
- [Parameter(Required = true, Name = "Message", Description = "The message to write to the log.", DefaultValue = "", Index = 2)]
- public IFunction Message = null!;
+ [Parameter(Required = true, Name = "Message", Description = "The message to write to the log.", DefaultValue = "", Index = 2)]
+ public IFunction Message = null!;
- public override void Invoke(Context context)
- {
- var log = Log.Invoke();
- log.Invoke(Message.Invoke());
- ToInvoke?.Invoke(context);
- }
+ public override void Invoke(Context context)
+ {
+ var log = Log.Invoke();
+ log.Invoke(Message.Invoke());
+ ToInvoke?.Invoke(context);
}
+}
- [Module(Name = "Write to Log", DocumentationLink = "https://tmg.utoronto.ca/doc/2.0/xtmf2/modules/XTMF2/RuntimeModules/WriteToLog.html",
- Description = "Writes the provided mess to the log and then invokes the next step.")]
- public sealed class WriteToLogBasedOnContextA : BaseAction
- {
- [SubModule(Required = true, Name = "Log", Description = "The log that will be written to.", Index = 0)]
- public IFunction Log = null!;
+[Module(Name = "Write to Log", DocumentationLink = "https://tmg.utoronto.ca/doc/2.0/xtmf2/modules/XTMF2/RuntimeModules/WriteToLog.html",
+ Description = "Writes the provided message to the log and then invokes the next step.")]
+public sealed class WriteToLogBasedOnContextA : BaseAction
+{
+ [SubModule(Required = true, Name = "Log", Description = "The log that will be written to.", Index = 0)]
+ public IFunction Log = null!;
- [SubModule(Required = true, Name = "To Invoke", Description = "The function to execute after writing to the log.", Index = 1, PassesExecution = true)]
- public IAction ToInvoke = null!;
+ [SubModule(Required = true, Name = "To Invoke", Description = "The function to execute after writing to the log.", Index = 1, PassesExecution = true)]
+ public IAction ToInvoke = null!;
- [Parameter(Required = true, Name = "Message", Description = "The message to write to the log.", DefaultValue = "", Index = 2)]
- public IFunction Message = null!;
+ [Parameter(Required = true, Name = "Message", Description = "The message to write to the log.", DefaultValue = "", Index = 2)]
+ public IFunction Message = null!;
- public override void Invoke(Context context)
- {
- var log = Log.Invoke();
- log.Invoke(Message.Invoke(context));
- ToInvoke.Invoke(context);
- }
+ public override void Invoke(Context context)
+ {
+ var log = Log.Invoke();
+ log.Invoke(Message.Invoke(context));
+ ToInvoke.Invoke(context);
}
-
}
diff --git a/tests/XTMF2.GUI.Tests/Headless/ModelSystemCanvasHeadlessTests.cs b/tests/XTMF2.GUI.Tests/Headless/ModelSystemCanvasHeadlessTests.cs
index 5b919d3..74bae87 100644
--- a/tests/XTMF2.GUI.Tests/Headless/ModelSystemCanvasHeadlessTests.cs
+++ b/tests/XTMF2.GUI.Tests/Headless/ModelSystemCanvasHeadlessTests.cs
@@ -19,6 +19,8 @@ You should have received a copy of the GNU General Public License
using Avalonia.Headless;
using Avalonia.Controls;
+using Avalonia.Input;
+using Avalonia.Threading;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using XTMF2.GUI.Controls;
using XTMF2.GUI.Tests.Modules;
@@ -71,6 +73,277 @@ public void ModelSystemCanvas_DataContext_DefaultsToNull()
}, System.Threading.CancellationToken.None).GetAwaiter().GetResult();
}
+ [TestMethod]
+ public void ModelSystemCanvas_AddShortcuts_CreateCommentAndFunctionTemplate()
+ {
+ TestGuiHelper.RunInModelSystemContext(
+ nameof(ModelSystemCanvas_AddShortcuts_CreateCommentAndFunctionTemplate),
+ (user, projectSession, msSession) =>
+ {
+ using var vm = new ModelSystemEditorViewModel(msSession, user, runController: null);
+
+ Session.Dispatch(() =>
+ {
+ var canvas = new ModelSystemCanvas { DataContext = vm };
+ canvas.Measure(new Avalonia.Size(800, 600));
+ canvas.Arrange(new Avalonia.Rect(0, 0, 800, 600));
+
+ var handleAddShortcut = typeof(ModelSystemCanvas).GetMethod(
+ "TryHandleAddShortcut",
+ BindingFlags.Instance | BindingFlags.NonPublic);
+ Assert.IsNotNull(handleAddShortcut);
+
+ var commentShortcut = new KeyEventArgs
+ {
+ Key = Key.N,
+ KeyModifiers = KeyModifiers.Control
+ };
+ var templateShortcut = new KeyEventArgs
+ {
+ Key = Key.T,
+ KeyModifiers = KeyModifiers.Control
+ };
+
+ Assert.IsTrue((bool)handleAddShortcut!.Invoke(canvas, new object[] { commentShortcut })!);
+ Assert.IsTrue((bool)handleAddShortcut.Invoke(canvas, new object[] { templateShortcut })!);
+
+ Assert.HasCount(1, vm.CommentBlocks);
+ Assert.HasCount(1, vm.FunctionTemplates);
+ }, System.Threading.CancellationToken.None).GetAwaiter().GetResult();
+ });
+ }
+
+ [TestMethod]
+ public void ModelSystemCanvas_ContextMenu_ShortcutHeadersUseTwoColumnGrid()
+ {
+ TestGuiHelper.RunInModelSystemContext(
+ nameof(ModelSystemCanvas_ContextMenu_ShortcutHeadersUseTwoColumnGrid),
+ (user, projectSession, msSession) =>
+ {
+ using var vm = new ModelSystemEditorViewModel(msSession, user, runController: null);
+
+ Session.Dispatch(() =>
+ {
+ var canvas = new ModelSystemCanvas { DataContext = vm };
+ var showMenu = typeof(ModelSystemCanvas).GetMethod(
+ "ShowContextMenu",
+ BindingFlags.Instance | BindingFlags.NonPublic);
+ Assert.IsNotNull(showMenu);
+
+ showMenu!.Invoke(canvas, new object?[] { null, null });
+
+ var menu = canvas.ContextMenu;
+ Assert.IsNotNull(menu);
+
+ var addModuleItem = menu!.Items.OfType