diff --git a/Bonsai.Editor/EditorForm.Designer.cs b/Bonsai.Editor/EditorForm.Designer.cs index cd34c952f..aedfca916 100644 --- a/Bonsai.Editor/EditorForm.Designer.cs +++ b/Bonsai.Editor/EditorForm.Designer.cs @@ -1,4 +1,4 @@ -namespace Bonsai.Editor +namespace Bonsai.Editor { partial class EditorForm { @@ -95,6 +95,9 @@ private void InitializeComponent() this.openToolStripButton = new System.Windows.Forms.ToolStripButton(); this.saveToolStripButton = new System.Windows.Forms.ToolStripButton(); this.fileToolStripSeparator = new System.Windows.Forms.ToolStripSeparator(); + this.findNextToolStripButton = new System.Windows.Forms.ToolStripButton(); + this.findPreviousToolStripButton = new System.Windows.Forms.ToolStripButton(); + this.findToolStripSeparator = new System.Windows.Forms.ToolStripSeparator(); this.undoToolStripButton = new System.Windows.Forms.ToolStripButton(); this.redoToolStripButton = new System.Windows.Forms.ToolStripButton(); this.editToolStripSeparator = new System.Windows.Forms.ToolStripSeparator(); @@ -142,6 +145,8 @@ private void InitializeComponent() this.multicastSubjectToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.replaceToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.renameSubjectToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.findNextToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.findPreviousToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.goToDefinitionToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.commandExecutor = new Bonsai.Design.CommandExecutor(); this.workflowFileWatcher = new System.IO.FileSystemWatcher(); @@ -657,6 +662,9 @@ private void InitializeComponent() this.openToolStripButton, this.saveToolStripButton, this.fileToolStripSeparator, + this.findNextToolStripButton, + this.findPreviousToolStripButton, + this.findToolStripSeparator, this.undoToolStripButton, this.redoToolStripButton, this.editToolStripSeparator, @@ -710,6 +718,31 @@ private void InitializeComponent() this.fileToolStripSeparator.Name = "fileToolStripSeparator"; this.fileToolStripSeparator.Size = new System.Drawing.Size(6, 25); // + // findNextToolStripButton + // + this.findNextToolStripButton.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; + this.findNextToolStripButton.Image = ((System.Drawing.Image)(resources.GetObject("findNextToolStripButton.Image"))); + this.findNextToolStripButton.ImageTransparentColor = System.Drawing.Color.Magenta; + this.findNextToolStripButton.Name = "findNextToolStripButton"; + this.findNextToolStripButton.Size = new System.Drawing.Size(23, 22); + this.findNextToolStripButton.Text = "Find Next (F3)"; + this.findNextToolStripButton.Click += new System.EventHandler(this.findNextToolStripMenuItem_Click); + // + // findPreviousToolStripButton + // + this.findPreviousToolStripButton.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; + this.findPreviousToolStripButton.Image = ((System.Drawing.Image)(resources.GetObject("findPreviousToolStripButton.Image"))); + this.findPreviousToolStripButton.ImageTransparentColor = System.Drawing.Color.Magenta; + this.findPreviousToolStripButton.Name = "findPreviousToolStripButton"; + this.findPreviousToolStripButton.Size = new System.Drawing.Size(23, 22); + this.findPreviousToolStripButton.Text = "Find Previous (Shift+F3)"; + this.findPreviousToolStripButton.Click += new System.EventHandler(this.findPreviousToolStripMenuItem_Click); + // + // findToolStripSeparator + // + this.findToolStripSeparator.Name = "findToolStripSeparator"; + this.findToolStripSeparator.Size = new System.Drawing.Size(6, 25); + // // undoToolStripButton // this.undoToolStripButton.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; @@ -1169,9 +1202,11 @@ private void InitializeComponent() this.multicastSubjectToolStripMenuItem, this.replaceToolStripMenuItem, this.renameSubjectToolStripMenuItem, + this.findNextToolStripMenuItem, + this.findPreviousToolStripMenuItem, this.goToDefinitionToolStripMenuItem}); this.toolboxContextMenuStrip.Name = "toolboxContextMenuStrip"; - this.toolboxContextMenuStrip.Size = new System.Drawing.Size(207, 246); + this.toolboxContextMenuStrip.Size = new System.Drawing.Size(207, 290); // // toolboxDocsToolStripMenuItem // @@ -1247,6 +1282,26 @@ private void InitializeComponent() this.renameSubjectToolStripMenuItem.Text = "Rename"; this.renameSubjectToolStripMenuItem.Click += new System.EventHandler(this.renameSubjectToolStripMenuItem_Click); // + // findNextToolStripMenuItem + // + this.findNextToolStripMenuItem.Image = ((System.Drawing.Image)(resources.GetObject("findNextToolStripButton.Image"))); + this.findNextToolStripMenuItem.ImageTransparentColor = System.Drawing.Color.Magenta; + this.findNextToolStripMenuItem.Name = "findNextToolStripMenuItem"; + this.findNextToolStripMenuItem.ShortcutKeys = System.Windows.Forms.Keys.F3; + this.findNextToolStripMenuItem.Size = new System.Drawing.Size(206, 22); + this.findNextToolStripMenuItem.Text = "Find Next"; + this.findNextToolStripMenuItem.Click += new System.EventHandler(this.findNextToolStripMenuItem_Click); + // + // findPreviousToolStripMenuItem + // + this.findPreviousToolStripMenuItem.Image = ((System.Drawing.Image)(resources.GetObject("findPreviousToolStripButton.Image"))); + this.findPreviousToolStripMenuItem.ImageTransparentColor = System.Drawing.Color.Magenta; + this.findPreviousToolStripMenuItem.Name = "findPreviousToolStripMenuItem"; + this.findPreviousToolStripMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Shift | System.Windows.Forms.Keys.F3))); + this.findPreviousToolStripMenuItem.Size = new System.Drawing.Size(206, 22); + this.findPreviousToolStripMenuItem.Text = "Find Previous"; + this.findPreviousToolStripMenuItem.Click += new System.EventHandler(this.findPreviousToolStripMenuItem_Click); + // // goToDefinitionToolStripMenuItem // this.goToDefinitionToolStripMenuItem.Name = "goToDefinitionToolStripMenuItem"; @@ -1385,6 +1440,9 @@ private void InitializeComponent() private Bonsai.Editor.CueBannerTextBox searchTextBox; private System.Windows.Forms.ToolStripSeparator toolStripSeparator2; private System.Windows.Forms.ToolStripMenuItem groupToolStripMenuItem; + private System.Windows.Forms.ToolStripButton findNextToolStripButton; + private System.Windows.Forms.ToolStripButton findPreviousToolStripButton; + private System.Windows.Forms.ToolStripSeparator findToolStripSeparator; private System.Windows.Forms.ToolStripButton undoToolStripButton; private System.Windows.Forms.ToolStripButton redoToolStripButton; private System.Windows.Forms.ToolStripButton stopToolStripButton; @@ -1414,6 +1472,8 @@ private void InitializeComponent() private System.Windows.Forms.ToolStripMenuItem multicastSubjectToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem replaceToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem renameSubjectToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem findNextToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem findPreviousToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem goToDefinitionToolStripMenuItem; private System.Windows.Forms.ToolStripSeparator toolStripSeparator7; private System.Windows.Forms.ToolStripMenuItem reloadExtensionsToolStripMenuItem; diff --git a/Bonsai.Editor/EditorForm.cs b/Bonsai.Editor/EditorForm.cs index e170ebff9..0c8f9e074 100644 --- a/Bonsai.Editor/EditorForm.cs +++ b/Bonsai.Editor/EditorForm.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; @@ -1412,25 +1412,25 @@ void HandleWorkflowCompleted() else clearErrors(); } - void HighlightDeclaration(WorkflowGraphView workflowView, ExpressionDeclaration declaration) + void HighlightExpression(WorkflowGraphView workflowView, ExpressionScope scope) { if (workflowView == null) { throw new ArgumentNullException(nameof(workflowView)); } - var graphNode = workflowView.FindGraphNode(declaration.Value); + var graphNode = workflowView.FindGraphNode(scope.Value); if (graphNode != null) { workflowView.GraphView.SelectedNode = graphNode; - var nestedDeclaration = declaration.InnerDeclaration; - if (nestedDeclaration != null) + var innerScope = scope.InnerScope; + if (innerScope != null) { workflowView.LaunchWorkflowView(graphNode); var editorLauncher = workflowView.GetWorkflowEditorLauncher(graphNode); if (editorLauncher != null) { - HighlightDeclaration(editorLauncher.WorkflowGraphView, nestedDeclaration); + HighlightExpression(editorLauncher.WorkflowGraphView, innerScope); } } else @@ -1762,6 +1762,7 @@ private void searchTextBox_KeyDown(object sender, KeyEventArgs e) e.Handled = true; break; case Keys.F2: + case Keys.F3: toolboxTreeView_KeyDown(sender, e); break; case Keys.Return: @@ -1853,6 +1854,61 @@ void SelectTreeViewSubjectNode(string subjectName) } } + void FindNextTypeMatch(TreeNode typeNode, bool findPrevious) + { + var currentNode = selectionModel.SelectedNodes.FirstOrDefault(); + var elementCategory = WorkflowGraphView.GetToolboxElementCategory(typeNode); + Func predicate = elementCategory switch + { + ~ElementCategory.Workflow => builder => builder.MatchIncludeWorkflow(typeNode.Name), + ~ElementCategory.Source => builder => builder.MatchSubjectReference(typeNode.Name), + _ => builder => builder.MatchElementType(typeNode.Name), + }; + + FindNextMatch(predicate, currentNode?.Value, findPrevious); + } + + void FindNextGraphNode(bool findPrevious) + { + var model = selectionModel.SelectedView; + if (!model.GraphView.Focused) return; + + var selection = selectionModel.SelectedNodes.ToArray(); + if (selection.Length == 0) return; + + Func predicate; + var currentBuilder = ExpressionBuilder.Unwrap(selection[0].Value); + if (currentBuilder is SubjectExpressionBuilder || + currentBuilder is SubscribeSubject || + currentBuilder is MulticastSubject) + { + var subjectName = ((INamedElement)currentBuilder).Name; + predicate = builder => builder.MatchSubjectReference(subjectName); + } + else if (currentBuilder is IncludeWorkflowBuilder includeBuilder) + { + predicate = builder => builder.MatchIncludeWorkflow(includeBuilder.Path); + } + else + { + var workflowElement = ExpressionBuilder.GetWorkflowElement(currentBuilder); + var typeName = workflowElement.GetType().AssemblyQualifiedName; + predicate = builder => builder.MatchElementType(typeName); + } + + FindNextMatch(predicate, currentBuilder, findPrevious); + } + + void FindNextMatch(Func predicate, ExpressionBuilder current, bool findPrevious) + { + var match = workflowBuilder.Find(predicate, current, findPrevious); + if (match != null) + { + var scope = workflowBuilder.GetExpressionScope(match); + HighlightExpression(editorControl.WorkflowGraphView, scope); + } + } + private void toolboxTreeView_KeyDown(object sender, KeyEventArgs e) { var selectedNode = toolboxTreeView.SelectedNode; @@ -1863,6 +1919,12 @@ private void toolboxTreeView_KeyDown(object sender, KeyEventArgs e) e.SuppressKeyPress = true; } + if (e.KeyCode == Keys.F3 && selectedNode?.Tag != null) + { + var findPrevious = e.Modifiers == Keys.Shift; + FindNextTypeMatch(selectedNode, findPrevious); + } + var rename = e.KeyCode == Keys.F2; var goToDefinition = e.KeyCode == Keys.F12; if ((rename || goToDefinition) && selectedNode?.Tag != null) @@ -1889,8 +1951,8 @@ private void toolboxTreeView_KeyDown(object sender, KeyEventArgs e) } else { - var declaration = workflowBuilder.GetDeclaration(definition.Subject); - HighlightDeclaration(editorControl.WorkflowGraphView, declaration); + var scope = workflowBuilder.GetExpressionScope(definition.Subject); + HighlightExpression(editorControl.WorkflowGraphView, scope); } } } @@ -1956,6 +2018,8 @@ private void toolboxTreeView_MouseUp(object sender, MouseEventArgs e) renameSubjectToolStripMenuItem.Visible = true; goToDefinitionToolStripMenuItem.Visible = true; replaceToolStripMenuItem.Visible = true; + findNextToolStripMenuItem.Visible = true; + findPreviousToolStripMenuItem.Visible = true; } else { @@ -2042,6 +2106,24 @@ selectedBuilder is SubscribeSubject || toolboxTreeView_KeyDown(sender, new KeyEventArgs(Keys.F2)); } + private void findNextToolStripMenuItem_Click(object sender, EventArgs e) + { + if (toolboxTreeView.Focused || searchTextBox.Focused) + { + toolboxTreeView_KeyDown(sender, new KeyEventArgs(findNextToolStripMenuItem.ShortcutKeys)); + } + else FindNextGraphNode(findPrevious: false); + } + + private void findPreviousToolStripMenuItem_Click(object sender, EventArgs e) + { + if (toolboxTreeView.Focused || searchTextBox.Focused) + { + toolboxTreeView_KeyDown(sender, new KeyEventArgs(findPreviousToolStripMenuItem.ShortcutKeys)); + } + else FindNextGraphNode(findPrevious: true); + } + private void goToDefinitionToolStripMenuItem_Click(object sender, EventArgs e) { toolboxTreeView_KeyDown(sender, new KeyEventArgs(Keys.F12)); @@ -2489,6 +2571,8 @@ public void OnKeyDown(KeyEventArgs e) HandleMenuItemShortcutKeys(e, siteForm.restartToolStripMenuItem, siteForm.restartToolStripMenuItem_Click); HandleMenuItemShortcutKeys(e, siteForm.stopToolStripMenuItem, siteForm.stopToolStripMenuItem_Click); HandleMenuItemShortcutKeys(e, siteForm.renameSubjectToolStripMenuItem, siteForm.renameSubjectToolStripMenuItem_Click); + HandleMenuItemShortcutKeys(e, siteForm.findNextToolStripMenuItem, siteForm.findNextToolStripMenuItem_Click); + HandleMenuItemShortcutKeys(e, siteForm.findPreviousToolStripMenuItem, siteForm.findPreviousToolStripMenuItem_Click); HandleMenuItemShortcutKeys(e, siteForm.docsToolStripMenuItem, siteForm.docsToolStripMenuItem_Click); } @@ -2650,8 +2734,8 @@ public void ShowDefinition(object component) 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); + var scope = siteForm.workflowBuilder.GetExpressionScope(definition.Subject); + siteForm.HighlightExpression(siteForm.editorControl.WorkflowGraphView, scope); return; } } diff --git a/Bonsai.Editor/EditorForm.resx b/Bonsai.Editor/EditorForm.resx index da84714f3..1730384c6 100644 --- a/Bonsai.Editor/EditorForm.resx +++ b/Bonsai.Editor/EditorForm.resx @@ -1,4 +1,4 @@ - +