From 79a768fd7c3388e2ba925e3c13061909cda8a016 Mon Sep 17 00:00:00 2001 From: glopesdev Date: Tue, 7 Jun 2022 12:12:43 +0100 Subject: [PATCH 1/7] Add support for renaming subjects --- .../GraphModel/WorkflowBuilderExtensions.cs | 149 ++++++++++++++++++ Bonsai.Editor/GraphModel/WorkflowEditor.cs | 69 +++++++- 2 files changed, 217 insertions(+), 1 deletion(-) create mode 100644 Bonsai.Editor/GraphModel/WorkflowBuilderExtensions.cs diff --git a/Bonsai.Editor/GraphModel/WorkflowBuilderExtensions.cs b/Bonsai.Editor/GraphModel/WorkflowBuilderExtensions.cs new file mode 100644 index 000000000..4997aee21 --- /dev/null +++ b/Bonsai.Editor/GraphModel/WorkflowBuilderExtensions.cs @@ -0,0 +1,149 @@ +using System.Collections.Generic; +using System.Linq; +using Bonsai.Expressions; + +namespace Bonsai.Editor.GraphModel +{ + static class WorkflowBuilderExtensions + { + static bool IsGroup(IWorkflowExpressionBuilder builder) + { + return builder is IncludeWorkflowBuilder || builder is GroupWorkflowBuilder; + } + + static IEnumerable SelectContextElements(ExpressionBuilderGraph source) + { + foreach (var node in source) + { + var element = ExpressionBuilder.Unwrap(node.Value); + yield return element; + + if (element is IWorkflowExpressionBuilder workflowBuilder && IsGroup(workflowBuilder)) + { + var workflow = workflowBuilder.Workflow; + if (workflow == null) continue; + foreach (var groupElement in SelectContextElements(workflow)) + { + yield return groupElement; + } + } + } + } + + static bool GetCallContext(ExpressionBuilderGraph source, ExpressionBuilderGraph target, Stack context) + { + context.Push(source); + if (source == target) + { + return true; + } + + foreach (var element in SelectContextElements(source)) + { + var groupBuilder = element as IWorkflowExpressionBuilder; + if (IsGroup(groupBuilder) && groupBuilder.Workflow == target) + { + return true; + } + + if (element is WorkflowExpressionBuilder workflowBuilder) + { + if (GetCallContext(workflowBuilder.Workflow, target, context)) + { + return true; + } + } + } + + context.Pop(); + return false; + } + + public static SubjectDeclaration GetSubjectDeclaration(this WorkflowBuilder source, ExpressionBuilderGraph target, string name) + { + var callContext = new Stack(); + if (GetCallContext(source.Workflow, target, callContext)) + { + return (from level in callContext + from element in SelectContextElements(level) + let subjectBuilder = element as SubjectExpressionBuilder + where subjectBuilder != null && subjectBuilder.Name == name + select new SubjectDeclaration(level, subjectBuilder)) + .LastOrDefault(); + } + + return null; + } + + public static IEnumerable GetDependentExpressions(this ExpressionBuilderGraph source, SubjectExpressionBuilder subject) + { + var callContext = new Stack(); + callContext.Push(source); + + while (callContext.Count > 0) + { + var root = callContext.Pop(); + + var exclude = false; + var snapshot = callContext.Count; + foreach (var element in SelectContextElements(root)) + { + if (element is SubjectExpressionBuilder subjectDeclaration && + subjectDeclaration != subject && + subjectDeclaration.Name == subject.Name) + { + exclude = true; + while (callContext.Count > snapshot) callContext.Pop(); + break; + } + + if (element is WorkflowExpressionBuilder workflowBuilder) + { + callContext.Push(workflowBuilder.Workflow); + } + } + + if (!exclude) + { + yield return new ContextGrouping(root, SelectContextElements(root)); + } + } + } + } + + class SubjectDeclaration + { + public SubjectDeclaration(ExpressionBuilderGraph root, SubjectExpressionBuilder subject) + { + Root = root; + Subject = subject; + } + + public ExpressionBuilderGraph Root { get; } + + public SubjectExpressionBuilder Subject { get; } + } + + class ContextGrouping : IGrouping + { + public ContextGrouping(ExpressionBuilderGraph key, IEnumerable elements) + { + Key = key; + Elements = elements; + } + + public ExpressionBuilderGraph Key { get; } + + IEnumerable Elements { get; } + + public IEnumerator GetEnumerator() + { + return Elements.GetEnumerator(); + } + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } +} diff --git a/Bonsai.Editor/GraphModel/WorkflowEditor.cs b/Bonsai.Editor/GraphModel/WorkflowEditor.cs index ea5f9a118..2b9b47574 100644 --- a/Bonsai.Editor/GraphModel/WorkflowEditor.cs +++ b/Bonsai.Editor/GraphModel/WorkflowEditor.cs @@ -1,4 +1,4 @@ -using Bonsai.Dag; +using Bonsai.Dag; using Bonsai.Design; using Bonsai.Editor.Properties; using Bonsai.Expressions; @@ -20,6 +20,7 @@ class WorkflowEditor readonly Subject error; readonly Subject updateLayout; readonly Subject updateParentLayout; + readonly Subject invalidateLayout; readonly Subject> updateSelection; readonly Subject closeWorkflowEditor; @@ -31,6 +32,7 @@ public WorkflowEditor(IServiceProvider provider, IGraphView view) error = new Subject(); updateLayout = new Subject(); updateParentLayout = new Subject(); + invalidateLayout = new Subject(); updateSelection = new Subject>(); closeWorkflowEditor = new Subject(); } @@ -43,6 +45,8 @@ public WorkflowEditor(IServiceProvider provider, IGraphView view) public IObservable UpdateParentLayout => updateParentLayout; + public IObservable InvalidateLayout => invalidateLayout; + public IObservable> UpdateSelection => updateSelection; public IObservable CloseWorkflowEditor => closeWorkflowEditor; @@ -93,6 +97,11 @@ private Action CreateUpdateGraphLayoutDelegate() return () => updateLayout.OnNext(true); } + private Action CreateInvalidateGraphLayoutDelegate() + { + return () => invalidateLayout.OnNext(true); + } + private Action CreateUpdateSelectionDelegate() { return CreateUpdateSelectionDelegate(Enumerable.Empty()); @@ -1588,6 +1597,64 @@ public void EnableGraphNodes(IEnumerable nodes) UpdateGraphNodes(nodes, EnableGraphNode); } + public void RenameSubject(string currentName, string newName) + { + var workflow = Workflow; + var workflowBuilder = (WorkflowBuilder)serviceProvider.GetService(typeof(WorkflowBuilder)); + var declaration = workflowBuilder.GetSubjectDeclaration(workflow, currentName); + if (declaration == null) return; + + var subscribeDependents = new List(); + var multicastDependents = new List(); + foreach (var dependent in declaration.Root.GetDependentExpressions(declaration.Subject) + .SelectMany(context => context) + .Where(builder => builder is INamedElement namedElement && + namedElement.Name == currentName)) + { + if (dependent is SubscribeSubjectBuilder subscribeSubject) + { + subscribeDependents.Add(subscribeSubject); + } + + if (dependent is MulticastSubjectBuilder multicastSubject) + { + multicastDependents.Add(multicastSubject); + } + } + + var invalidateGraphLayout = CreateInvalidateGraphLayoutDelegate(); + commandExecutor.BeginCompositeCommand(); + commandExecutor.Execute(EmptyAction, invalidateGraphLayout); + commandExecutor.Execute(() => + { + declaration.Subject.Name = newName; + foreach (var dependent in subscribeDependents) + { + dependent.Name = newName; + } + + foreach (var dependent in multicastDependents) + { + dependent.Name = newName; + } + }, + () => + { + declaration.Subject.Name = currentName; + foreach (var dependent in subscribeDependents) + { + dependent.Name = currentName; + } + + foreach (var dependent in multicastDependents) + { + dependent.Name = currentName; + } + }); + commandExecutor.Execute(invalidateGraphLayout, EmptyAction); + commandExecutor.EndCompositeCommand(); + } + public GraphNode FindGraphNode(ExpressionBuilder value) { return graphView.Nodes.SelectMany(layer => layer).FirstOrDefault(n => n.Value == value); From abd69557505958ea3c4dc7558d9a027a62f72aaf Mon Sep 17 00:00:00 2001 From: glopesdev Date: Tue, 7 Jun 2022 12:15:03 +0100 Subject: [PATCH 2/7] Ensure graph view is refreshed after rename --- Bonsai.Editor/GraphView/GraphViewControl.cs | 15 +++++++++++++++ Bonsai.Editor/GraphView/WorkflowGraphView.cs | 19 +++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/Bonsai.Editor/GraphView/GraphViewControl.cs b/Bonsai.Editor/GraphView/GraphViewControl.cs index 538fd44eb..8ba7b52da 100644 --- a/Bonsai.Editor/GraphView/GraphViewControl.cs +++ b/Bonsai.Editor/GraphView/GraphViewControl.cs @@ -326,6 +326,21 @@ void InvalidateSelection() } } + public override void Refresh() + { + using (var graphics = CreateGraphics()) + { + foreach (var layout in layoutNodes) + { + if (layout.Text != layout.Node.Text) + { + layout.SetNodeLabel(layout.Node.Text, Font, graphics); + } + } + } + base.Refresh(); + } + public void Invalidate(GraphNode node) { if (node != null && layoutNodes.Contains(node)) diff --git a/Bonsai.Editor/GraphView/WorkflowGraphView.cs b/Bonsai.Editor/GraphView/WorkflowGraphView.cs index 69f135984..779f4caf4 100644 --- a/Bonsai.Editor/GraphView/WorkflowGraphView.cs +++ b/Bonsai.Editor/GraphView/WorkflowGraphView.cs @@ -797,6 +797,19 @@ private void UpdateGraphLayout(bool validateWorkflow) UpdateSelection(); } + private void InvalidateGraphLayout(bool validateWorkflow) + { + graphView.Refresh(); + if (Launcher != null) + { + Launcher.ParentView.InvalidateGraphLayout(validateWorkflow); + } + else if (validateWorkflow) + { + editorService.ValidateWorkflow(); + } + } + #endregion #region Controller @@ -1251,6 +1264,12 @@ private void InitializeViewBindings() } }); + Editor.InvalidateLayout.Subscribe(validateWorkflow => + { + if (Launcher != null) Launcher.WorkflowGraphView.InvalidateGraphLayout(validateWorkflow); + else InvalidateGraphLayout(validateWorkflow); + }); + Editor.UpdateSelection.Subscribe(selection => { var activeView = Launcher != null ? Launcher.WorkflowGraphView.GraphView : graphView; From d82f26237c11cdba10c53990bf041de5573f1ba8 Mon Sep 17 00:00:00 2001 From: glopesdev Date: Tue, 7 Jun 2022 12:22:00 +0100 Subject: [PATCH 3/7] Add context menu items and shortcuts for renaming --- Bonsai.Editor/EditorForm.Designer.cs | 14 ++++- Bonsai.Editor/EditorForm.cs | 79 +++++++++++++++++++++++++--- 2 files changed, 86 insertions(+), 7 deletions(-) diff --git a/Bonsai.Editor/EditorForm.Designer.cs b/Bonsai.Editor/EditorForm.Designer.cs index aefbf8a84..e98c33780 100644 --- a/Bonsai.Editor/EditorForm.Designer.cs +++ b/Bonsai.Editor/EditorForm.Designer.cs @@ -139,6 +139,7 @@ private void InitializeComponent() this.createGroupToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.subscribeSubjectToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.multicastSubjectToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.renameSubjectToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.commandExecutor = new Bonsai.Design.CommandExecutor(); this.workflowFileWatcher = new System.IO.FileSystemWatcher(); this.exportImageDialog = new System.Windows.Forms.SaveFileDialog(); @@ -944,6 +945,7 @@ private void InitializeComponent() this.toolboxTreeView.Size = new System.Drawing.Size(197, 222); this.toolboxTreeView.TabIndex = 0; this.toolboxTreeView.ItemDrag += new System.Windows.Forms.ItemDragEventHandler(this.toolboxTreeView_ItemDrag); + this.toolboxTreeView.AfterLabelEdit += new System.Windows.Forms.NodeLabelEditEventHandler(this.toolboxTreeView_AfterLabelEdit); this.toolboxTreeView.AfterSelect += new System.Windows.Forms.TreeViewEventHandler(this.toolboxTreeView_AfterSelect); this.toolboxTreeView.NodeMouseDoubleClick += new System.Windows.Forms.TreeNodeMouseClickEventHandler(this.toolboxTreeView_NodeMouseDoubleClick); this.toolboxTreeView.Enter += new System.EventHandler(this.toolboxTreeView_Enter); @@ -1159,7 +1161,8 @@ private void InitializeComponent() this.createBranchToolStripMenuItem, this.createGroupToolStripMenuItem, this.subscribeSubjectToolStripMenuItem, - this.multicastSubjectToolStripMenuItem}); + this.multicastSubjectToolStripMenuItem, + this.renameSubjectToolStripMenuItem}); this.toolboxContextMenuStrip.Name = "toolboxContextMenuStrip"; this.toolboxContextMenuStrip.Size = new System.Drawing.Size(207, 136); // @@ -1211,6 +1214,14 @@ private void InitializeComponent() this.multicastSubjectToolStripMenuItem.Text = "Multicast"; this.multicastSubjectToolStripMenuItem.Click += new System.EventHandler(this.createGroupToolStripMenuItem_Click); // + // renameToolStripMenuItem + // + this.renameSubjectToolStripMenuItem.Name = "renameSubjectToolStripMenuItem"; + this.renameSubjectToolStripMenuItem.ShortcutKeys = System.Windows.Forms.Keys.F2; + this.renameSubjectToolStripMenuItem.Size = new System.Drawing.Size(206, 32); + this.renameSubjectToolStripMenuItem.Text = "Rename"; + this.renameSubjectToolStripMenuItem.Click += new System.EventHandler(this.renameSubjectToolStripMenuItem_Click); + // // commandExecutor // this.commandExecutor.StatusChanged += new System.EventHandler(this.commandExecutor_StatusChanged); @@ -1368,6 +1379,7 @@ private void InitializeComponent() private System.Windows.Forms.ToolStripMenuItem createGroupToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem subscribeSubjectToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem multicastSubjectToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem renameSubjectToolStripMenuItem; private System.Windows.Forms.ToolStripSeparator toolStripSeparator7; private System.Windows.Forms.ToolStripMenuItem reloadExtensionsToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem reloadExtensionsDebugToolStripMenuItem; diff --git a/Bonsai.Editor/EditorForm.cs b/Bonsai.Editor/EditorForm.cs index d72a46f3f..090ad7be6 100644 --- a/Bonsai.Editor/EditorForm.cs +++ b/Bonsai.Editor/EditorForm.cs @@ -444,7 +444,7 @@ IObservable InitializeSubjectSources() var isEmpty = subjectCategory.Nodes.Count == 0; subjectCategory.Nodes.Clear(); - var nameProperty = TypeDescriptor.GetProperties(typeof(SubscribeSubjectBuilder))["Name"]; + var nameProperty = TypeDescriptor.GetProperties(typeof(SubscribeSubjectBuilder))[nameof(SubscribeSubjectBuilder.Name)]; var subjects = nameProperty.Converter.GetStandardValues(new TypeDescriptorContext(workflowBuilder, nameProperty, editorSite)); if (subjects != null && subjects.Count > 0) { @@ -1767,6 +1767,9 @@ private void searchTextBox_KeyDown(object sender, KeyEventArgs e) toolboxTreeView.SelectedNode = toolboxTreeView.SelectedNode.NextVisibleNode ?? toolboxTreeView.SelectedNode; e.Handled = true; break; + case Keys.F2: + toolboxTreeView_KeyDown(sender, e); + break; case Keys.Return: toolboxTreeView_KeyDown(sender, e); searchTextBox.Clear(); @@ -1846,17 +1849,57 @@ void UpdateTreeViewSelection(bool focused) selectedNode.BackColor = focused ? Color.Empty : themeRenderer.ToolStripRenderer.ColorTable.InactiveCaption; } + void SelectTreeViewSubjectNode(string subjectName) + { + var subjectCategory = toolboxCategories[SubjectCategoryName]; + var subjectNode = subjectCategory.Nodes.Find(subjectName, false); + if (subjectNode.Length > 0) + { + toolboxTreeView.SelectedNode = subjectNode[0]; + } + } + private void toolboxTreeView_KeyDown(object sender, KeyEventArgs e) { - if (e.KeyCode == Keys.Return && - toolboxTreeView.SelectedNode != null && - toolboxTreeView.SelectedNode.Tag != null) + var selectedNode = toolboxTreeView.SelectedNode; + if (e.KeyCode == Keys.Return && selectedNode?.Tag != null) { - var typeNode = toolboxTreeView.SelectedNode; - CreateGraphNode(typeNode, e.Modifiers); + CreateGraphNode(selectedNode, e.Modifiers); e.Handled = true; e.SuppressKeyPress = true; } + + if (e.KeyCode == Keys.F2 && selectedNode?.Tag != null) + { + var elementCategory = WorkflowGraphView.GetToolboxElementCategory(selectedNode); + if (!selectedNode.IsEditing && elementCategory == ~ElementCategory.Source) + { + toolboxTreeView.LabelEdit = true; + selectedNode.BeginEdit(); + } + } + } + + private void toolboxTreeView_AfterLabelEdit(object sender, NodeLabelEditEventArgs e) + { + e.CancelEdit = true; + + var selectedView = selectionModel.SelectedView; + if (e.Node == null || string.IsNullOrEmpty(e.Label) || selectedView == null) + { + return; + } + + var newName = e.Label; + var currentName = e.Node.Name; + var currentLabel = e.Node.Text; + selectedView.Editor.RenameSubject(currentName, newName); + e.Node.Name = newName; + e.Node.Text = newName + currentLabel.Substring(currentName.Length); + toolboxTreeView.LabelEdit = false; + searchTextBox.Clear(); + SelectTreeViewSubjectNode(newName); + UpdatePropertyGrid(); } private void toolboxTreeView_NodeMouseDoubleClick(object sender, TreeNodeMouseClickEventArgs e) @@ -1893,6 +1936,7 @@ private void toolboxTreeView_MouseUp(object sender, MouseEventArgs e) } subscribeSubjectToolStripMenuItem.Visible = true; multicastSubjectToolStripMenuItem.Visible = true; + renameSubjectToolStripMenuItem.Visible = true; } else { @@ -1908,6 +1952,7 @@ private void toolboxTreeView_MouseUp(object sender, MouseEventArgs e) createGroupToolStripMenuItem.Visible = allowGroup || selectionType != null; subscribeSubjectToolStripMenuItem.Visible = false; multicastSubjectToolStripMenuItem.Visible = false; + renameSubjectToolStripMenuItem.Visible = false; insertBeforeToolStripMenuItem.Visible = true; } toolboxContextMenuStrip.Show(toolboxTreeView, e.X, e.Y); @@ -1946,6 +1991,27 @@ private void createGroupToolStripMenuItem_Click(object sender, EventArgs e) toolboxTreeView_KeyDown(sender, new KeyEventArgs(Keys.Control | Keys.Return)); } + private void renameSubjectToolStripMenuItem_Click(object sender, EventArgs e) + { + if (!toolboxTreeView.Focused) + { + var model = selectionModel.SelectedView; + if (!model.GraphView.Focused) return; + + var selection = selectionModel.SelectedNodes.ToArray(); + var selectedBuilder = selection?.Length == 1 && selection?[0].Value is InspectBuilder inspectBuilder ? inspectBuilder.Builder : null; + if (selectedBuilder is SubjectExpressionBuilder || + selectedBuilder is SubscribeSubjectBuilder || + selectedBuilder is MulticastSubjectBuilder) + { + var subjectName = ((INamedElement)selectedBuilder).Name; + SelectTreeViewSubjectNode(subjectName); + } + } + + toolboxTreeView_KeyDown(sender, new KeyEventArgs(Keys.F2)); + } + private void MainForm_KeyDown(object sender, KeyEventArgs e) { editorSite.OnKeyDown(e); @@ -2290,6 +2356,7 @@ public void OnKeyDown(KeyEventArgs e) HandleMenuItemShortcutKeys(e, siteForm.startToolStripButtonMenuItem, siteForm.startToolStripMenuItem_Click); HandleMenuItemShortcutKeys(e, siteForm.restartToolStripMenuItem, siteForm.restartToolStripMenuItem_Click); HandleMenuItemShortcutKeys(e, siteForm.stopToolStripMenuItem, siteForm.stopToolStripMenuItem_Click); + HandleMenuItemShortcutKeys(e, siteForm.renameSubjectToolStripMenuItem, siteForm.renameSubjectToolStripMenuItem_Click); } public void OnKeyPress(KeyPressEventArgs e) From 6f0212b74d1aeee1a3f50f412aa5b684963a0a2b Mon Sep 17 00:00:00 2001 From: glopesdev Date: Tue, 14 Jun 2022 10:11:04 +0100 Subject: [PATCH 4/7] Avoid renaming read-only subject declarations --- Bonsai.Editor/EditorForm.cs | 15 +- .../GraphModel/WorkflowBuilderExtensions.cs | 121 +- Bonsai.Editor/GraphModel/WorkflowEditor.cs | 34 +- .../Properties/Resources.Designer.cs | 1183 +++++++++-------- Bonsai.Editor/Properties/Resources.resx | 9 + 5 files changed, 718 insertions(+), 644 deletions(-) diff --git a/Bonsai.Editor/EditorForm.cs b/Bonsai.Editor/EditorForm.cs index 090ad7be6..57bb43067 100644 --- a/Bonsai.Editor/EditorForm.cs +++ b/Bonsai.Editor/EditorForm.cs @@ -1874,6 +1874,18 @@ private void toolboxTreeView_KeyDown(object sender, KeyEventArgs e) var elementCategory = WorkflowGraphView.GetToolboxElementCategory(selectedNode); if (!selectedNode.IsEditing && elementCategory == ~ElementCategory.Source) { + var currentName = selectedNode.Name; + var selectedView = selectionModel.SelectedView; + var definition = workflowBuilder.GetSubjectDefinition(selectedView.Workflow, currentName); + if (definition == null || definition.IsReadOnly) + { + var message = definition == null + ? Resources.SubjectDefinitionNotFound_Error + : string.Format(Resources.RenameReadOnlySubjectDefinition_Error, currentName); + editorSite.ShowError(message); + return; + } + toolboxTreeView.LabelEdit = true; selectedNode.BeginEdit(); } @@ -1893,7 +1905,8 @@ private void toolboxTreeView_AfterLabelEdit(object sender, NodeLabelEditEventArg var newName = e.Label; var currentName = e.Node.Name; var currentLabel = e.Node.Text; - selectedView.Editor.RenameSubject(currentName, newName); + var definition = workflowBuilder.GetSubjectDefinition(selectedView.Workflow, currentName); + selectedView.Editor.RenameSubject(definition, newName); e.Node.Name = newName; e.Node.Text = newName + currentLabel.Substring(currentName.Length); toolboxTreeView.LabelEdit = false; diff --git a/Bonsai.Editor/GraphModel/WorkflowBuilderExtensions.cs b/Bonsai.Editor/GraphModel/WorkflowBuilderExtensions.cs index 4997aee21..e56af65a6 100644 --- a/Bonsai.Editor/GraphModel/WorkflowBuilderExtensions.cs +++ b/Bonsai.Editor/GraphModel/WorkflowBuilderExtensions.cs @@ -6,79 +6,61 @@ namespace Bonsai.Editor.GraphModel { static class WorkflowBuilderExtensions { - static bool IsGroup(IWorkflowExpressionBuilder builder) + public static bool IsGroup(this IWorkflowExpressionBuilder builder) { return builder is IncludeWorkflowBuilder || builder is GroupWorkflowBuilder; } - static IEnumerable SelectContextElements(ExpressionBuilderGraph source) + static bool GetCallContext(ExpressionBuilderGraph source, bool readOnly, ExpressionBuilderGraph target, Stack callContext) { - foreach (var node in source) - { - var element = ExpressionBuilder.Unwrap(node.Value); - yield return element; - - if (element is IWorkflowExpressionBuilder workflowBuilder && IsGroup(workflowBuilder)) - { - var workflow = workflowBuilder.Workflow; - if (workflow == null) continue; - foreach (var groupElement in SelectContextElements(workflow)) - { - yield return groupElement; - } - } - } - } - - static bool GetCallContext(ExpressionBuilderGraph source, ExpressionBuilderGraph target, Stack context) - { - context.Push(source); + var context = new ContextGrouping(source, readOnly); + callContext.Push(context); if (source == target) { return true; } - foreach (var element in SelectContextElements(source)) + foreach (var element in context) { - var groupBuilder = element as IWorkflowExpressionBuilder; + var groupBuilder = element.Builder as IWorkflowExpressionBuilder; if (IsGroup(groupBuilder) && groupBuilder.Workflow == target) { return true; } - if (element is WorkflowExpressionBuilder workflowBuilder) + if (element.Builder is WorkflowExpressionBuilder workflowBuilder) { - if (GetCallContext(workflowBuilder.Workflow, target, context)) + if (GetCallContext(workflowBuilder.Workflow, element.IsReadOnly, target, callContext)) { return true; } } } - context.Pop(); + callContext.Pop(); return false; } - public static SubjectDeclaration GetSubjectDeclaration(this WorkflowBuilder source, ExpressionBuilderGraph target, string name) + public static SubjectDefinition GetSubjectDefinition(this WorkflowBuilder source, ExpressionBuilderGraph target, string name) { - var callContext = new Stack(); - if (GetCallContext(source.Workflow, target, callContext)) + var callContext = new Stack(); + if (GetCallContext(source.Workflow, readOnly: false, target, callContext)) { return (from level in callContext - from element in SelectContextElements(level) - let subjectBuilder = element as SubjectExpressionBuilder + from element in level + let subjectBuilder = element.Builder as SubjectExpressionBuilder where subjectBuilder != null && subjectBuilder.Name == name - select new SubjectDeclaration(level, subjectBuilder)) + select new SubjectDefinition(level, subjectBuilder, element.IsReadOnly)) .LastOrDefault(); } return null; } - public static IEnumerable GetDependentExpressions(this ExpressionBuilderGraph source, SubjectExpressionBuilder subject) + public static IEnumerable GetDependentExpressions(this SubjectDefinition source) { - var callContext = new Stack(); - callContext.Push(source); + var callContext = new Stack(); + callContext.Push(source.Root); while (callContext.Count > 0) { @@ -86,57 +68,75 @@ public static IEnumerable GetDependentExpressions(this Expressi var exclude = false; var snapshot = callContext.Count; - foreach (var element in SelectContextElements(root)) + foreach (var element in root) { - if (element is SubjectExpressionBuilder subjectDeclaration && - subjectDeclaration != subject && - subjectDeclaration.Name == subject.Name) + if (element.Builder is SubjectExpressionBuilder subjectDefinition && + subjectDefinition != source.Subject && + subjectDefinition.Name == source.Subject.Name) { exclude = true; while (callContext.Count > snapshot) callContext.Pop(); break; } - if (element is WorkflowExpressionBuilder workflowBuilder) + if (element.Builder is WorkflowExpressionBuilder workflowBuilder) { - callContext.Push(workflowBuilder.Workflow); + callContext.Push(new ContextGrouping(workflowBuilder.Workflow, element.IsReadOnly)); } } if (!exclude) { - yield return new ContextGrouping(root, SelectContextElements(root)); + yield return root; } } } } - class SubjectDeclaration + class SubjectDefinition { - public SubjectDeclaration(ExpressionBuilderGraph root, SubjectExpressionBuilder subject) + public SubjectDefinition(ContextGrouping root, SubjectExpressionBuilder subject, bool readOnly) { Root = root; Subject = subject; + IsReadOnly = readOnly; } - public ExpressionBuilderGraph Root { get; } + public ContextGrouping Root { get; } public SubjectExpressionBuilder Subject { get; } + + public bool IsReadOnly { get; } + } + + struct ContextElement + { + public ExpressionBuilder Builder; + public bool IsReadOnly; + + public ContextElement(ExpressionBuilder element, bool readOnly) + { + Builder = element; + IsReadOnly = readOnly; + } } - class ContextGrouping : IGrouping + class ContextGrouping : IGrouping { - public ContextGrouping(ExpressionBuilderGraph key, IEnumerable elements) + public ContextGrouping(ExpressionBuilderGraph key, bool readOnly) { Key = key; - Elements = elements; + IsReadOnly = readOnly; + Elements = SelectContextElements(key, readOnly); } public ExpressionBuilderGraph Key { get; } - IEnumerable Elements { get; } + public bool IsReadOnly { get; } - public IEnumerator GetEnumerator() + IEnumerable Elements { get; } + + public IEnumerator GetEnumerator() { return Elements.GetEnumerator(); } @@ -145,5 +145,24 @@ System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return GetEnumerator(); } + + static IEnumerable SelectContextElements(ExpressionBuilderGraph source, bool readOnly) + { + foreach (var node in source) + { + var builder = ExpressionBuilder.Unwrap(node.Value); + yield return new ContextElement(builder, readOnly); + + if (builder is IWorkflowExpressionBuilder workflowBuilder && workflowBuilder.IsGroup()) + { + var workflow = workflowBuilder.Workflow; + if (workflow == null) continue; + foreach (var groupElement in SelectContextElements(workflow, readOnly || workflowBuilder is IncludeWorkflowBuilder)) + { + yield return groupElement; + } + } + } + } } } diff --git a/Bonsai.Editor/GraphModel/WorkflowEditor.cs b/Bonsai.Editor/GraphModel/WorkflowEditor.cs index 2b9b47574..59cda57c9 100644 --- a/Bonsai.Editor/GraphModel/WorkflowEditor.cs +++ b/Bonsai.Editor/GraphModel/WorkflowEditor.cs @@ -1597,26 +1597,32 @@ public void EnableGraphNodes(IEnumerable nodes) UpdateGraphNodes(nodes, EnableGraphNode); } - public void RenameSubject(string currentName, string newName) + public void RenameSubject(SubjectDefinition definition, string newName) { - var workflow = Workflow; - var workflowBuilder = (WorkflowBuilder)serviceProvider.GetService(typeof(WorkflowBuilder)); - var declaration = workflowBuilder.GetSubjectDeclaration(workflow, currentName); - if (declaration == null) return; + if (definition == null) + { + throw new ArgumentNullException(nameof(definition)); + } + + if (definition.IsReadOnly) + { + throw new ArgumentException(Resources.ReadOnlySubjectDefinition_Error, nameof(definition)); + } + var currentName = definition.Subject.Name; var subscribeDependents = new List(); var multicastDependents = new List(); - foreach (var dependent in declaration.Root.GetDependentExpressions(declaration.Subject) - .SelectMany(context => context) - .Where(builder => builder is INamedElement namedElement && - namedElement.Name == currentName)) + foreach (var dependent in definition.GetDependentExpressions() + .SelectMany(context => context) + .Where(element => element.Builder is INamedElement namedElement && + namedElement.Name == definition.Subject.Name)) { - if (dependent is SubscribeSubjectBuilder subscribeSubject) + if (dependent.IsReadOnly) continue; + else if (dependent.Builder is SubscribeSubjectBuilder subscribeSubject) { subscribeDependents.Add(subscribeSubject); } - - if (dependent is MulticastSubjectBuilder multicastSubject) + else if (dependent.Builder is MulticastSubjectBuilder multicastSubject) { multicastDependents.Add(multicastSubject); } @@ -1627,7 +1633,7 @@ public void RenameSubject(string currentName, string newName) commandExecutor.Execute(EmptyAction, invalidateGraphLayout); commandExecutor.Execute(() => { - declaration.Subject.Name = newName; + definition.Subject.Name = newName; foreach (var dependent in subscribeDependents) { dependent.Name = newName; @@ -1640,7 +1646,7 @@ public void RenameSubject(string currentName, string newName) }, () => { - declaration.Subject.Name = currentName; + definition.Subject.Name = currentName; foreach (var dependent in subscribeDependents) { dependent.Name = currentName; diff --git a/Bonsai.Editor/Properties/Resources.Designer.cs b/Bonsai.Editor/Properties/Resources.Designer.cs index 7d533524d..8687cea6d 100644 --- a/Bonsai.Editor/Properties/Resources.Designer.cs +++ b/Bonsai.Editor/Properties/Resources.Designer.cs @@ -1,578 +1,605 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version:4.0.30319.42000 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace Bonsai.Editor.Properties { - using System; - - - /// - /// A strongly-typed resource class, for looking up localized strings, etc. - /// - // This class was auto-generated by the StronglyTypedResourceBuilder - // class via a tool like ResGen or Visual Studio. - // To add or remove a member, edit your .ResX file then rerun ResGen - // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - internal class Resources { - - private static global::System.Resources.ResourceManager resourceMan; - - private static global::System.Globalization.CultureInfo resourceCulture; - - [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - internal Resources() { - } - - /// - /// Returns the cached ResourceManager instance used by this class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Resources.ResourceManager ResourceManager { - get { - if (object.ReferenceEquals(resourceMan, null)) { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Bonsai.Editor.Properties.Resources", typeof(Resources).Assembly); - resourceMan = temp; - } - return resourceMan; - } - } - - /// - /// Overrides the current thread's CurrentUICulture property for all - /// resource lookups using this strongly typed resource class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Globalization.CultureInfo Culture { - get { - return resourceCulture; - } - set { - resourceCulture = value; - } - } - - /// - /// Looks up a localized string similar to - ///The Bonsai framework includes software from the Reactive Extensions for .NET and the NuGet projects developed at the .NET Foundation (https://dotnetfoundation.org/). - /// - ///Copyright (c) .NET Foundation and Contributors. - /// - internal static string AttributionNotices { - get { - return ResourceManager.GetString("AttributionNotices", resourceCulture); - } - } - - /// - /// Looks up a localized resource of type System.Drawing.Bitmap. - /// - internal static System.Drawing.Bitmap Bonsai { - get { - object obj = ResourceManager.GetObject("Bonsai", resourceCulture); - return ((System.Drawing.Bitmap)(obj)); - } - } - - /// - /// Looks up a localized string similar to Bonsai. - /// - internal static string BonsaiTitle { - get { - return ResourceManager.GetString("BonsaiTitle", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Build Error. - /// - internal static string BuildError_Caption { - get { - return ResourceManager.GetString("BuildError_Caption", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to None. - /// - internal static string ContextMenu_NoneMenuItemLabel { - get { - return ResourceManager.GetString("ContextMenu_NoneMenuItemLabel", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to There was an error copying the selected elements to the clipboard: - ///{0}. - /// - internal static string CopyToClipboard_Error { - get { - return ResourceManager.GetString("CopyToClipboard_Error", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Extensions folder does not exist in the current working directory. Do you want to create it?. - /// - internal static string CreateExtensionsFolder_Question { - get { - return ResourceManager.GetString("CreateExtensionsFolder_Question", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Create Extensions Folder. - /// - internal static string CreateExtensionsFolder_Question_Caption { - get { - return ResourceManager.GetString("CreateExtensionsFolder_Question_Caption", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The workflow must be saved before adding new extensions.. - /// - internal static string CreateExtensionsWorkflow_Warning { - get { - return ResourceManager.GetString("CreateExtensionsWorkflow_Warning", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Save Workflow. - /// - internal static string CreateExtensionsWorkflow_Warning_Caption { - get { - return ResourceManager.GetString("CreateExtensionsWorkflow_Warning_Caption", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Create Group. - /// - internal static string CreateGroupAction { - get { - return ResourceManager.GetString("CreateGroupAction", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Create Source. - /// - internal static string CreateSourceMenuItemLabel { - get { - return ResourceManager.GetString("CreateSourceMenuItemLabel", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Failed to create {0}: - ///{1}. - /// - internal static string CreateTypeNode_Error { - get { - return ResourceManager.GetString("CreateTypeNode_Error", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Editor Error. - /// - internal static string Editor_Error_Caption { - get { - return ResourceManager.GetString("Editor_Error_Caption", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The workflow needs to be saved before proceeding. Do you want to save the workflow?. - /// - internal static string EnsureSavedWorkflow_Question { - get { - return ResourceManager.GetString("EnsureSavedWorkflow_Question", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Exception builder node not found in active workflow editor.. - /// - internal static string ExceptionNodeNotFound_Error { - get { - return ResourceManager.GetString("ExceptionNodeNotFound_Error", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Unable to group broken branches.. - /// - internal static string GroupBrokenBranches_Error { - get { - return ResourceManager.GetString("GroupBrokenBranches_Error", resourceCulture); - } - } - - /// - /// Looks up a localized resource of type System.Drawing.Icon similar to (Icon). - /// - internal static System.Drawing.Icon Icon { - get { - object obj = ResourceManager.GetObject("Icon", resourceCulture); - return ((System.Drawing.Icon)(obj)); - } - } - - /// - /// Looks up a localized string similar to Connecting the target nodes in the specified order would create a loop in the workflow. Consider inserting the operator as a branch.. - /// - internal static string InsertValidation_Error { - get { - return ResourceManager.GetString("InsertValidation_Error", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Unable to open script editor. - ///Do you want to install Visual Studio Code? - /// - ///NOTE: You will have to restart Bonsai for any changes to take effect.. - /// - internal static string InstallScriptEditor_Question { - get { - return ResourceManager.GetString("InstallScriptEditor_Question", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Install Script Editor. - /// - internal static string InstallScriptEditor_Question_Caption { - get { - return ResourceManager.GetString("InstallScriptEditor_Question_Caption", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The specified type is not a workflow expression builder.. - /// - internal static string InvalidExpressionBuilderType_Error { - get { - return ResourceManager.GetString("InvalidExpressionBuilderType_Error", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Replaced node must be workflow expression builder.. - /// - internal static string InvalidReplaceGroupNode_Error { - get { - return ResourceManager.GetString("InvalidReplaceGroupNode_Error", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to There was an error opening the workflow {0}: - ///{1}. - /// - internal static string OpenWorkflow_Error { - get { - return ResourceManager.GetString("OpenWorkflow_Error", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Open Error. - /// - internal static string OpenWorkflow_Error_Caption { - get { - return ResourceManager.GetString("OpenWorkflow_Error_Caption", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Output. - /// - internal static string OutputMenuItemLabel { - get { - return ResourceManager.GetString("OutputMenuItemLabel", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Package updates are available. - /// - internal static string PackageUpdatesAvailable_Notification { - get { - return ResourceManager.GetString("PackageUpdatesAvailable_Notification", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to There was an error pasting the selected elements from the clipboard: - ///{0}. - /// - internal static string PasteFromClipboard_Error { - get { - return ResourceManager.GetString("PasteFromClipboard_Error", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Ready. - /// - internal static string ReadyStatus { - get { - return ResourceManager.GetString("ReadyStatus", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The file "{0}" does not exist anymore. Do you want to remove the reference to it from the list of recent files?. - /// - internal static string RemoveRecentFile_Question { - get { - return ResourceManager.GetString("RemoveRecentFile_Question", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Bonsai. - /// - internal static string RemoveRecentFile_Question_Caption { - get { - return ResourceManager.GetString("RemoveRecentFile_Question_Caption", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to There was an error while reordering the selected nodes: - ///{0}. - /// - internal static string ReorderGraphNodes_Error { - get { - return ResourceManager.GetString("ReorderGraphNodes_Error", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Running. - /// - internal static string RunningStatus { - get { - return ResourceManager.GetString("RunningStatus", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Runtime Error. - /// - internal static string RuntimeError_Caption { - get { - return ResourceManager.GetString("RuntimeError_Caption", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Save Error. - /// - internal static string SaveElement_Error_Caption { - get { - return ResourceManager.GetString("SaveElement_Error_Caption", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to There was an error saving the visualizer layout: - ///{0}. - /// - internal static string SaveLayout_Error { - get { - return ResourceManager.GetString("SaveLayout_Error", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to There was an error saving the Bonsai workflow: - ///{0}. - /// - internal static string SaveWorkflow_Error { - get { - return ResourceManager.GetString("SaveWorkflow_Error", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Search Modules (Ctrl+E). - /// - internal static string SearchModuleCueBanner { - get { - return ResourceManager.GetString("SearchModuleCueBanner", resourceCulture); - } - } - - /// - /// Looks up a localized resource of type System.Drawing.Bitmap. - /// - internal static System.Drawing.Bitmap StatusBlockedImage { - get { - object obj = ResourceManager.GetObject("StatusBlockedImage", resourceCulture); - return ((System.Drawing.Bitmap)(obj)); - } - } - - /// - /// Looks up a localized resource of type System.Drawing.Bitmap. - /// - internal static System.Drawing.Bitmap StatusCriticalImage { - get { - object obj = ResourceManager.GetObject("StatusCriticalImage", resourceCulture); - return ((System.Drawing.Bitmap)(obj)); - } - } - - /// - /// Looks up a localized resource of type System.Drawing.Bitmap. - /// - internal static System.Drawing.Bitmap StatusReadyImage { - get { - object obj = ResourceManager.GetObject("StatusReadyImage", resourceCulture); - return ((System.Drawing.Bitmap)(obj)); - } - } - - /// - /// Looks up a localized resource of type System.Drawing.Bitmap. - /// - internal static System.Drawing.Bitmap StatusRunningImage { - get { - object obj = ResourceManager.GetObject("StatusRunningImage", resourceCulture); - return ((System.Drawing.Bitmap)(obj)); - } - } - - /// - /// Looks up a localized resource of type System.Drawing.Bitmap. - /// - internal static System.Drawing.Bitmap StatusUpdateAvailable { - get { - object obj = ResourceManager.GetObject("StatusUpdateAvailable", resourceCulture); - return ((System.Drawing.Bitmap)(obj)); - } - } - - /// - /// Looks up a localized string similar to Do you want to stop the workflow?. - /// - internal static string StopWorkflow_Question { - get { - return ResourceManager.GetString("StopWorkflow_Question", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Workflow Running. - /// - internal static string StopWorkflow_Question_Caption { - get { - return ResourceManager.GetString("StopWorkflow_Question_Caption", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Subject Type. - /// - internal static string SubjectTypeAction { - get { - return ResourceManager.GetString("SubjectTypeAction", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The specified type could not be found.. - /// - internal static string TypeNotFound_Error { - get { - return ResourceManager.GetString("TypeNotFound_Error", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Unable to evaluate uncompiled script expression. Please ensure all script files have been created and reload extensions.. - /// - internal static string UncompiledScriptExpression_Error { - get { - return ResourceManager.GetString("UncompiledScriptExpression_Error", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Workflow has unsaved changes. Save project file?. - /// - internal static string UnsavedWorkflow_Question { - get { - return ResourceManager.GetString("UnsavedWorkflow_Question", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Unsaved Changes. - /// - internal static string UnsavedWorkflow_Question_Caption { - get { - return ResourceManager.GetString("UnsavedWorkflow_Question_Caption", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to There was an error while updating the workflow. Please check if there are any missing packages that need to be installed before trying to upgrade again.. - /// - internal static string UpdateWorkflow_Error { - get { - return ResourceManager.GetString("UpdateWorkflow_Error", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to This workflow file has been created with an older version of Bonsai. Further modifications will be incompatible with previous versions.. - /// - internal static string UpdateWorkflow_Warning { - get { - return ResourceManager.GetString("UpdateWorkflow_Warning", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Update Workflow. - /// - internal static string UpdateWorkflow_Warning_Caption { - get { - return ResourceManager.GetString("UpdateWorkflow_Warning_Caption", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Cannot set visualizer layout with a null workflow.. - /// - internal static string VisualizerLayoutOnNullWorkflow_Error { - get { - return ResourceManager.GetString("VisualizerLayoutOnNullWorkflow_Error", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Externalized properties of this workflow can be configured below.. - /// - internal static string WorkflowPropertiesDescription { - get { - return ResourceManager.GetString("WorkflowPropertiesDescription", resourceCulture); - } - } - } -} +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Bonsai.Editor.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Bonsai.Editor.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to + ///The Bonsai framework includes software from the Reactive Extensions for .NET and the NuGet projects developed at the .NET Foundation (https://dotnetfoundation.org/). + /// + ///Copyright (c) .NET Foundation and Contributors. + /// + internal static string AttributionNotices { + get { + return ResourceManager.GetString("AttributionNotices", resourceCulture); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap Bonsai { + get { + object obj = ResourceManager.GetObject("Bonsai", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized string similar to Bonsai. + /// + internal static string BonsaiTitle { + get { + return ResourceManager.GetString("BonsaiTitle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Build Error. + /// + internal static string BuildError_Caption { + get { + return ResourceManager.GetString("BuildError_Caption", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to None. + /// + internal static string ContextMenu_NoneMenuItemLabel { + get { + return ResourceManager.GetString("ContextMenu_NoneMenuItemLabel", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to There was an error copying the selected elements to the clipboard: + ///{0}. + /// + internal static string CopyToClipboard_Error { + get { + return ResourceManager.GetString("CopyToClipboard_Error", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Extensions folder does not exist in the current working directory. Do you want to create it?. + /// + internal static string CreateExtensionsFolder_Question { + get { + return ResourceManager.GetString("CreateExtensionsFolder_Question", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Create Extensions Folder. + /// + internal static string CreateExtensionsFolder_Question_Caption { + get { + return ResourceManager.GetString("CreateExtensionsFolder_Question_Caption", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The workflow must be saved before adding new extensions.. + /// + internal static string CreateExtensionsWorkflow_Warning { + get { + return ResourceManager.GetString("CreateExtensionsWorkflow_Warning", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Save Workflow. + /// + internal static string CreateExtensionsWorkflow_Warning_Caption { + get { + return ResourceManager.GetString("CreateExtensionsWorkflow_Warning_Caption", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Create Group. + /// + internal static string CreateGroupAction { + get { + return ResourceManager.GetString("CreateGroupAction", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Create Source. + /// + internal static string CreateSourceMenuItemLabel { + get { + return ResourceManager.GetString("CreateSourceMenuItemLabel", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Failed to create {0}: + ///{1}. + /// + internal static string CreateTypeNode_Error { + get { + return ResourceManager.GetString("CreateTypeNode_Error", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Editor Error. + /// + internal static string Editor_Error_Caption { + get { + return ResourceManager.GetString("Editor_Error_Caption", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The workflow needs to be saved before proceeding. Do you want to save the workflow?. + /// + internal static string EnsureSavedWorkflow_Question { + get { + return ResourceManager.GetString("EnsureSavedWorkflow_Question", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Exception builder node not found in active workflow editor.. + /// + internal static string ExceptionNodeNotFound_Error { + get { + return ResourceManager.GetString("ExceptionNodeNotFound_Error", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Unable to group broken branches.. + /// + internal static string GroupBrokenBranches_Error { + get { + return ResourceManager.GetString("GroupBrokenBranches_Error", resourceCulture); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Icon similar to (Icon). + /// + internal static System.Drawing.Icon Icon { + get { + object obj = ResourceManager.GetObject("Icon", resourceCulture); + return ((System.Drawing.Icon)(obj)); + } + } + + /// + /// Looks up a localized string similar to Connecting the target nodes in the specified order would create a loop in the workflow. Consider inserting the operator as a branch.. + /// + internal static string InsertValidation_Error { + get { + return ResourceManager.GetString("InsertValidation_Error", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Unable to open script editor. + ///Do you want to install Visual Studio Code? + /// + ///NOTE: You will have to restart Bonsai for any changes to take effect.. + /// + internal static string InstallScriptEditor_Question { + get { + return ResourceManager.GetString("InstallScriptEditor_Question", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Install Script Editor. + /// + internal static string InstallScriptEditor_Question_Caption { + get { + return ResourceManager.GetString("InstallScriptEditor_Question_Caption", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The specified type is not a workflow expression builder.. + /// + internal static string InvalidExpressionBuilderType_Error { + get { + return ResourceManager.GetString("InvalidExpressionBuilderType_Error", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Replaced node must be workflow expression builder.. + /// + internal static string InvalidReplaceGroupNode_Error { + get { + return ResourceManager.GetString("InvalidReplaceGroupNode_Error", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to There was an error opening the workflow {0}: + ///{1}. + /// + internal static string OpenWorkflow_Error { + get { + return ResourceManager.GetString("OpenWorkflow_Error", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Open Error. + /// + internal static string OpenWorkflow_Error_Caption { + get { + return ResourceManager.GetString("OpenWorkflow_Error_Caption", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Output. + /// + internal static string OutputMenuItemLabel { + get { + return ResourceManager.GetString("OutputMenuItemLabel", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Package updates are available. + /// + internal static string PackageUpdatesAvailable_Notification { + get { + return ResourceManager.GetString("PackageUpdatesAvailable_Notification", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to There was an error pasting the selected elements from the clipboard: + ///{0}. + /// + internal static string PasteFromClipboard_Error { + get { + return ResourceManager.GetString("PasteFromClipboard_Error", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The specified subject definition is read-only.. + /// + internal static string ReadOnlySubjectDefinition_Error { + get { + return ResourceManager.GetString("ReadOnlySubjectDefinition_Error", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Ready. + /// + internal static string ReadyStatus { + get { + return ResourceManager.GetString("ReadyStatus", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The file "{0}" does not exist anymore. Do you want to remove the reference to it from the list of recent files?. + /// + internal static string RemoveRecentFile_Question { + get { + return ResourceManager.GetString("RemoveRecentFile_Question", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Bonsai. + /// + internal static string RemoveRecentFile_Question_Caption { + get { + return ResourceManager.GetString("RemoveRecentFile_Question_Caption", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The definition of {0} is read-only and cannot be renamed.. + /// + internal static string RenameReadOnlySubjectDefinition_Error { + get { + return ResourceManager.GetString("RenameReadOnlySubjectDefinition_Error", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to There was an error while reordering the selected nodes: + ///{0}. + /// + internal static string ReorderGraphNodes_Error { + get { + return ResourceManager.GetString("ReorderGraphNodes_Error", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Running. + /// + internal static string RunningStatus { + get { + return ResourceManager.GetString("RunningStatus", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Runtime Error. + /// + internal static string RuntimeError_Caption { + get { + return ResourceManager.GetString("RuntimeError_Caption", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Save Error. + /// + internal static string SaveElement_Error_Caption { + get { + return ResourceManager.GetString("SaveElement_Error_Caption", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to There was an error saving the visualizer layout: + ///{0}. + /// + internal static string SaveLayout_Error { + get { + return ResourceManager.GetString("SaveLayout_Error", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to There was an error saving the Bonsai workflow: + ///{0}. + /// + internal static string SaveWorkflow_Error { + get { + return ResourceManager.GetString("SaveWorkflow_Error", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Search Modules (Ctrl+E). + /// + internal static string SearchModuleCueBanner { + get { + return ResourceManager.GetString("SearchModuleCueBanner", resourceCulture); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap StatusBlockedImage { + get { + object obj = ResourceManager.GetObject("StatusBlockedImage", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap StatusCriticalImage { + get { + object obj = ResourceManager.GetObject("StatusCriticalImage", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap StatusReadyImage { + get { + object obj = ResourceManager.GetObject("StatusReadyImage", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap StatusRunningImage { + get { + object obj = ResourceManager.GetObject("StatusRunningImage", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap StatusUpdateAvailable { + get { + object obj = ResourceManager.GetObject("StatusUpdateAvailable", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized string similar to Do you want to stop the workflow?. + /// + internal static string StopWorkflow_Question { + get { + return ResourceManager.GetString("StopWorkflow_Question", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Workflow Running. + /// + internal static string StopWorkflow_Question_Caption { + get { + return ResourceManager.GetString("StopWorkflow_Question_Caption", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The specified subject definition could not be found.. + /// + internal static string SubjectDefinitionNotFound_Error { + get { + return ResourceManager.GetString("SubjectDefinitionNotFound_Error", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Subject Type. + /// + internal static string SubjectTypeAction { + get { + return ResourceManager.GetString("SubjectTypeAction", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The specified type could not be found.. + /// + internal static string TypeNotFound_Error { + get { + return ResourceManager.GetString("TypeNotFound_Error", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Unable to evaluate uncompiled script expression. Please ensure all script files have been created and reload extensions.. + /// + internal static string UncompiledScriptExpression_Error { + get { + return ResourceManager.GetString("UncompiledScriptExpression_Error", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Workflow has unsaved changes. Save project file?. + /// + internal static string UnsavedWorkflow_Question { + get { + return ResourceManager.GetString("UnsavedWorkflow_Question", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Unsaved Changes. + /// + internal static string UnsavedWorkflow_Question_Caption { + get { + return ResourceManager.GetString("UnsavedWorkflow_Question_Caption", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to There was an error while updating the workflow. Please check if there are any missing packages that need to be installed before trying to upgrade again.. + /// + internal static string UpdateWorkflow_Error { + get { + return ResourceManager.GetString("UpdateWorkflow_Error", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to This workflow file has been created with an older version of Bonsai. Further modifications will be incompatible with previous versions.. + /// + internal static string UpdateWorkflow_Warning { + get { + return ResourceManager.GetString("UpdateWorkflow_Warning", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Update Workflow. + /// + internal static string UpdateWorkflow_Warning_Caption { + get { + return ResourceManager.GetString("UpdateWorkflow_Warning_Caption", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Cannot set visualizer layout with a null workflow.. + /// + internal static string VisualizerLayoutOnNullWorkflow_Error { + get { + return ResourceManager.GetString("VisualizerLayoutOnNullWorkflow_Error", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Externalized properties of this workflow can be configured below.. + /// + internal static string WorkflowPropertiesDescription { + get { + return ResourceManager.GetString("WorkflowPropertiesDescription", resourceCulture); + } + } + } +} diff --git a/Bonsai.Editor/Properties/Resources.resx b/Bonsai.Editor/Properties/Resources.resx index 591f44367..0c53dc87f 100644 --- a/Bonsai.Editor/Properties/Resources.resx +++ b/Bonsai.Editor/Properties/Resources.resx @@ -329,4 +329,13 @@ NOTE: You will have to restart Bonsai for any changes to take effect. Output + + The specified subject definition is read-only. + + + The definition of {0} is read-only and cannot be renamed. + + + The specified subject definition could not be found. + \ No newline at end of file From aac8122dc1510bcf9973a46c1e9875194d5dcf9d Mon Sep 17 00:00:00 2001 From: glopesdev Date: Tue, 14 Jun 2022 12:43:20 +0100 Subject: [PATCH 5/7] Add editor support for finding subject definitions Fixes #933 --- Bonsai.Editor/EditorForm.cs | 42 +++++++++++++++++++ .../GraphModel/WorkflowBuilderExtensions.cs | 41 ++++++++++++++++++ 2 files changed, 83 insertions(+) diff --git a/Bonsai.Editor/EditorForm.cs b/Bonsai.Editor/EditorForm.cs index 57bb43067..0866e03cb 100644 --- a/Bonsai.Editor/EditorForm.cs +++ b/Bonsai.Editor/EditorForm.cs @@ -1452,6 +1452,35 @@ void HandleWorkflowCompleted() else clearErrors(); } + void HighlightDeclaration(WorkflowGraphView workflowView, ExpressionDeclaration declaration) + { + if (workflowView == null) + { + throw new ArgumentNullException(nameof(workflowView)); + } + + var graphNode = workflowView.FindGraphNode(declaration.Value); + if (graphNode != null) + { + workflowView.GraphView.SelectedNode = graphNode; + var nestedDeclaration = declaration.InnerDeclaration; + if (nestedDeclaration != null) + { + workflowView.LaunchWorkflowView(graphNode); + var editorLauncher = workflowView.GetWorkflowEditorLauncher(graphNode); + if (editorLauncher != null) + { + HighlightDeclaration(editorLauncher.WorkflowGraphView, nestedDeclaration); + } + } + else + { + var ownerForm = workflowView.EditorControl.ParentForm; + if (ownerForm != null) ownerForm.Activate(); + } + } + } + #endregion #region Workflow Controller @@ -2523,6 +2552,19 @@ public bool HasDefinition(object component) public void ShowDefinition(object component) { + if (component is INamedElement namedElement && + (namedElement is SubscribeSubjectBuilder || namedElement is MulticastSubjectBuilder)) + { + var model = siteForm.selectionModel.SelectedView ?? siteForm.editorControl.WorkflowGraphView; + var definition = siteForm.workflowBuilder.GetSubjectDefinition(model.Workflow, namedElement.Name); + if (definition != null) + { + var declaration = siteForm.workflowBuilder.GetDeclaration(definition.Subject); + siteForm.HighlightDeclaration(siteForm.editorControl.WorkflowGraphView, declaration); + return; + } + } + Type componentType; if (siteForm.scriptEnvironment == null) return; if (siteForm.scriptEnvironment.AssemblyName != null && diff --git a/Bonsai.Editor/GraphModel/WorkflowBuilderExtensions.cs b/Bonsai.Editor/GraphModel/WorkflowBuilderExtensions.cs index e56af65a6..074427959 100644 --- a/Bonsai.Editor/GraphModel/WorkflowBuilderExtensions.cs +++ b/Bonsai.Editor/GraphModel/WorkflowBuilderExtensions.cs @@ -91,6 +91,47 @@ public static IEnumerable GetDependentExpressions(this SubjectD } } } + + public static ExpressionDeclaration GetDeclaration(this WorkflowBuilder source, ExpressionBuilder target) + { + return GetDeclaration(source.Workflow, target); + } + + static ExpressionDeclaration GetDeclaration(ExpressionBuilderGraph source, ExpressionBuilder target) + { + foreach (var node in source) + { + var builder = ExpressionBuilder.Unwrap(node.Value); + if (builder == target) + { + return new ExpressionDeclaration(node.Value, innerDeclaration: null); + } + + if (builder is IWorkflowExpressionBuilder workflowBuilder) + { + var innerDeclaration = GetDeclaration(workflowBuilder.Workflow, target); + if (innerDeclaration != null) + { + return new ExpressionDeclaration(node.Value, innerDeclaration); + } + } + } + + return null; + } + } + + class ExpressionDeclaration + { + public ExpressionDeclaration(ExpressionBuilder value, ExpressionDeclaration innerDeclaration) + { + Value = value; + InnerDeclaration = innerDeclaration; + } + + public ExpressionBuilder Value { get; } + + public ExpressionDeclaration InnerDeclaration { get; } } class SubjectDefinition From 0f900e02c1593a54ec79b1a7efae5e3a8a38bb75 Mon Sep 17 00:00:00 2001 From: glopesdev Date: Tue, 2 Aug 2022 10:50:25 +0300 Subject: [PATCH 6/7] Add toolbox context menu item to find definitions --- Bonsai.Editor/EditorForm.Designer.cs | 19 +++++++++++++++---- Bonsai.Editor/EditorForm.cs | 25 +++++++++++++++++++++---- 2 files changed, 36 insertions(+), 8 deletions(-) diff --git a/Bonsai.Editor/EditorForm.Designer.cs b/Bonsai.Editor/EditorForm.Designer.cs index e98c33780..4782fdbbc 100644 --- a/Bonsai.Editor/EditorForm.Designer.cs +++ b/Bonsai.Editor/EditorForm.Designer.cs @@ -140,6 +140,7 @@ private void InitializeComponent() this.subscribeSubjectToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.multicastSubjectToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.renameSubjectToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.goToDefinitionToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.commandExecutor = new Bonsai.Design.CommandExecutor(); this.workflowFileWatcher = new System.IO.FileSystemWatcher(); this.exportImageDialog = new System.Windows.Forms.SaveFileDialog(); @@ -1162,9 +1163,10 @@ private void InitializeComponent() this.createGroupToolStripMenuItem, this.subscribeSubjectToolStripMenuItem, this.multicastSubjectToolStripMenuItem, - this.renameSubjectToolStripMenuItem}); + this.renameSubjectToolStripMenuItem, + this.goToDefinitionToolStripMenuItem}); this.toolboxContextMenuStrip.Name = "toolboxContextMenuStrip"; - this.toolboxContextMenuStrip.Size = new System.Drawing.Size(207, 136); + this.toolboxContextMenuStrip.Size = new System.Drawing.Size(207, 202); // // insertAfterToolStripMenuItem // @@ -1214,14 +1216,22 @@ private void InitializeComponent() this.multicastSubjectToolStripMenuItem.Text = "Multicast"; this.multicastSubjectToolStripMenuItem.Click += new System.EventHandler(this.createGroupToolStripMenuItem_Click); // - // renameToolStripMenuItem + // renameSubjectToolStripMenuItem // this.renameSubjectToolStripMenuItem.Name = "renameSubjectToolStripMenuItem"; this.renameSubjectToolStripMenuItem.ShortcutKeys = System.Windows.Forms.Keys.F2; - this.renameSubjectToolStripMenuItem.Size = new System.Drawing.Size(206, 32); + this.renameSubjectToolStripMenuItem.Size = new System.Drawing.Size(206, 22); this.renameSubjectToolStripMenuItem.Text = "Rename"; this.renameSubjectToolStripMenuItem.Click += new System.EventHandler(this.renameSubjectToolStripMenuItem_Click); // + // goToDefinitionToolStripMenuItem + // + this.goToDefinitionToolStripMenuItem.Name = "goToDefinitionToolStripMenuItem"; + this.goToDefinitionToolStripMenuItem.ShortcutKeys = System.Windows.Forms.Keys.F12; + this.goToDefinitionToolStripMenuItem.Size = new System.Drawing.Size(206, 22); + this.goToDefinitionToolStripMenuItem.Text = "Go To Definition"; + this.goToDefinitionToolStripMenuItem.Click += new System.EventHandler(this.goToDefinitionToolStripMenuItem_Click); + // // commandExecutor // this.commandExecutor.StatusChanged += new System.EventHandler(this.commandExecutor_StatusChanged); @@ -1380,6 +1390,7 @@ private void InitializeComponent() private System.Windows.Forms.ToolStripMenuItem subscribeSubjectToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem multicastSubjectToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem renameSubjectToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem goToDefinitionToolStripMenuItem; private System.Windows.Forms.ToolStripSeparator toolStripSeparator7; private System.Windows.Forms.ToolStripMenuItem reloadExtensionsToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem reloadExtensionsDebugToolStripMenuItem; diff --git a/Bonsai.Editor/EditorForm.cs b/Bonsai.Editor/EditorForm.cs index 0866e03cb..88ca31940 100644 --- a/Bonsai.Editor/EditorForm.cs +++ b/Bonsai.Editor/EditorForm.cs @@ -1898,7 +1898,9 @@ private void toolboxTreeView_KeyDown(object sender, KeyEventArgs e) e.SuppressKeyPress = true; } - if (e.KeyCode == Keys.F2 && selectedNode?.Tag != null) + var rename = e.KeyCode == Keys.F2; + var goToDefinition = e.KeyCode == Keys.F12; + if ((rename || goToDefinition) && selectedNode?.Tag != null) { var elementCategory = WorkflowGraphView.GetToolboxElementCategory(selectedNode); if (!selectedNode.IsEditing && elementCategory == ~ElementCategory.Source) @@ -1906,7 +1908,7 @@ private void toolboxTreeView_KeyDown(object sender, KeyEventArgs e) var currentName = selectedNode.Name; var selectedView = selectionModel.SelectedView; var definition = workflowBuilder.GetSubjectDefinition(selectedView.Workflow, currentName); - if (definition == null || definition.IsReadOnly) + if (definition == null || rename && definition.IsReadOnly) { var message = definition == null ? Resources.SubjectDefinitionNotFound_Error @@ -1915,8 +1917,16 @@ private void toolboxTreeView_KeyDown(object sender, KeyEventArgs e) return; } - toolboxTreeView.LabelEdit = true; - selectedNode.BeginEdit(); + if (rename) + { + toolboxTreeView.LabelEdit = true; + selectedNode.BeginEdit(); + } + else + { + var declaration = workflowBuilder.GetDeclaration(definition.Subject); + HighlightDeclaration(editorControl.WorkflowGraphView, declaration); + } } } } @@ -1979,6 +1989,7 @@ private void toolboxTreeView_MouseUp(object sender, MouseEventArgs e) subscribeSubjectToolStripMenuItem.Visible = true; multicastSubjectToolStripMenuItem.Visible = true; renameSubjectToolStripMenuItem.Visible = true; + goToDefinitionToolStripMenuItem.Visible = true; } else { @@ -1995,6 +2006,7 @@ private void toolboxTreeView_MouseUp(object sender, MouseEventArgs e) subscribeSubjectToolStripMenuItem.Visible = false; multicastSubjectToolStripMenuItem.Visible = false; renameSubjectToolStripMenuItem.Visible = false; + goToDefinitionToolStripMenuItem.Visible = false; insertBeforeToolStripMenuItem.Visible = true; } toolboxContextMenuStrip.Show(toolboxTreeView, e.X, e.Y); @@ -2054,6 +2066,11 @@ selectedBuilder is SubscribeSubjectBuilder || toolboxTreeView_KeyDown(sender, new KeyEventArgs(Keys.F2)); } + private void goToDefinitionToolStripMenuItem_Click(object sender, EventArgs e) + { + toolboxTreeView_KeyDown(sender, new KeyEventArgs(Keys.F12)); + } + private void MainForm_KeyDown(object sender, KeyEventArgs e) { editorSite.OnKeyDown(e); From 3ba1f6458b0a371fc7a91b20a2be6c8e7c71e867 Mon Sep 17 00:00:00 2001 From: glopesdev Date: Mon, 8 Aug 2022 12:26:35 +0100 Subject: [PATCH 7/7] Ensure declaration node editor is fully activated --- Bonsai.Editor/EditorForm.cs | 1 + Bonsai.Editor/GraphView/WorkflowGraphView.cs | 13 +++++++++---- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/Bonsai.Editor/EditorForm.cs b/Bonsai.Editor/EditorForm.cs index 88ca31940..b27827ecc 100644 --- a/Bonsai.Editor/EditorForm.cs +++ b/Bonsai.Editor/EditorForm.cs @@ -1477,6 +1477,7 @@ void HighlightDeclaration(WorkflowGraphView workflowView, ExpressionDeclaration { var ownerForm = workflowView.EditorControl.ParentForm; if (ownerForm != null) ownerForm.Activate(); + workflowView.SelectGraphNode(graphNode); } } } diff --git a/Bonsai.Editor/GraphView/WorkflowGraphView.cs b/Bonsai.Editor/GraphView/WorkflowGraphView.cs index 779f4caf4..49751182f 100644 --- a/Bonsai.Editor/GraphView/WorkflowGraphView.cs +++ b/Bonsai.Editor/GraphView/WorkflowGraphView.cs @@ -371,13 +371,18 @@ internal void SelectBuilderNode(ExpressionBuilder builder) var graphNode = FindGraphNode(builder); if (graphNode != null) { - GraphView.SelectedNode = graphNode; - EditorControl.SelectTab(this); - GraphView.Select(); - UpdateSelection(); + SelectGraphNode(graphNode); } } + internal void SelectGraphNode(GraphNode node) + { + GraphView.SelectedNode = node; + EditorControl.SelectTab(this); + GraphView.Select(); + UpdateSelection(); + } + private bool HasDefaultEditor(ExpressionBuilder builder) { if (builder is IWorkflowExpressionBuilder) return true;