Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/issue-1823' into preview
Browse files Browse the repository at this point in the history
  • Loading branch information
glopesdev committed Sep 3, 2024
2 parents c822587 + 24dbff3 commit 76e8ce9
Show file tree
Hide file tree
Showing 29 changed files with 1,830 additions and 1,238 deletions.
1 change: 1 addition & 0 deletions Bonsai.Editor.Tests/MockGraphView.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ public MockGraphView(ExpressionBuilderGraph workflow = null)
Workflow = workflow ?? new ExpressionBuilderGraph();
CommandExecutor = new CommandExecutor();
var serviceContainer = new ServiceContainer();
serviceContainer.AddService(typeof(WorkflowBuilder), new WorkflowBuilder(Workflow));
serviceContainer.AddService(typeof(CommandExecutor), CommandExecutor);
ServiceProvider = serviceContainer;
}
Expand Down
1 change: 0 additions & 1 deletion Bonsai.Editor.Tests/WorkflowEditorTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,6 @@ static string ToString(IEnumerable<T> sequence)
var editor = new WorkflowEditor(graphView.ServiceProvider, graphView);
editor.UpdateLayout.Subscribe(graphView.UpdateGraphLayout);
editor.UpdateSelection.Subscribe(graphView.UpdateSelection);
editor.Workflow = graphView.Workflow;

var nodeSequence = editor.GetGraphValues().ToArray();
return (editor, assertIsReversible: () =>
Expand Down
127 changes: 101 additions & 26 deletions Bonsai.Editor/EditorForm.Designer.cs

Large diffs are not rendered by default.

324 changes: 159 additions & 165 deletions Bonsai.Editor/EditorForm.cs

Large diffs are not rendered by default.

9 changes: 9 additions & 0 deletions Bonsai.Editor/EditorSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ sealed class EditorSettings
internal EditorSettings(string path)
{
AnnotationPanelSize = 400;
ExplorerSplitterDistance = 300;
settingsPath = path;
}

Expand All @@ -37,6 +38,8 @@ public static EditorSettings Instance

public int AnnotationPanelSize { get; set; }

public int ExplorerSplitterDistance { get; set; }

public RecentlyUsedFileCollection RecentlyUsedFiles
{
get { return recentlyUsedFiles; }
Expand Down Expand Up @@ -74,6 +77,11 @@ static EditorSettings Load()
int.TryParse(reader.ReadElementContentAsString(), out int annotationPanelSize);
settings.AnnotationPanelSize = annotationPanelSize;
}
else if (reader.Name == nameof(ExplorerSplitterDistance))
{
int.TryParse(reader.ReadElementContentAsString(), out int explorerSplitterDistance);
settings.ExplorerSplitterDistance = explorerSplitterDistance;
}
else if (reader.Name == nameof(DesktopBounds))
{
reader.ReadToFollowing(nameof(Rectangle.X));
Expand Down Expand Up @@ -120,6 +128,7 @@ public void Save()
writer.WriteElementString(nameof(WindowState), WindowState.ToString());
writer.WriteElementString(nameof(EditorTheme), EditorTheme.ToString());
writer.WriteElementString(nameof(AnnotationPanelSize), AnnotationPanelSize.ToString(CultureInfo.InvariantCulture));
writer.WriteElementString(nameof(ExplorerSplitterDistance), ExplorerSplitterDistance.ToString(CultureInfo.InvariantCulture));

writer.WriteStartElement(nameof(DesktopBounds));
writer.WriteElementString(nameof(Rectangle.X), DesktopBounds.X.ToString(CultureInfo.InvariantCulture));
Expand Down
212 changes: 212 additions & 0 deletions Bonsai.Editor/ExplorerTreeView.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,212 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
using Bonsai.Editor.GraphModel;
using Bonsai.Editor.Properties;
using Bonsai.Expressions;

namespace Bonsai.Editor
{
class ExplorerTreeView : ToolboxTreeView
{
bool activeDoubleClick;
readonly ImageList imageList;
readonly ImageList stateImageList;

public ExplorerTreeView()
{
imageList = new();
stateImageList = new();
imageList.Images.Add(Resources.WorkflowEditableImage);
imageList.Images.Add(Resources.WorkflowReadOnlyImage);
#if NETFRAMEWORK
stateImageList.Images.Add(Resources.StatusReadyImage);
stateImageList.Images.Add(Resources.StatusBlockedImage);
#else
// TreeView.StateImageList.ImageSize is internally scaled according to initial system DPI (not font).
// To avoid excessive scaling of images we must prepare correctly sized ImageList beforehand.
const float DefaultDpi = 96f;
using var graphics = CreateGraphics();
var dpiScale = graphics.DpiY / DefaultDpi;
stateImageList.ImageSize = new Size(
(int)(16 * dpiScale),
(int)(16 * dpiScale));
stateImageList.Images.Add(ResizeMakeBorder(Resources.StatusReadyImage, stateImageList.ImageSize));
stateImageList.Images.Add(ResizeMakeBorder(Resources.StatusBlockedImage, stateImageList.ImageSize));

static Bitmap ResizeMakeBorder(Bitmap original, Size newSize)
{
//TODO: DrawImageUnscaledAndClipped gives best results but blending is not great
var image = new Bitmap(newSize.Width, newSize.Height, original.PixelFormat);
using var graphics = Graphics.FromImage(image);
var offsetX = (newSize.Width - original.Width) / 2;
var offsetY = (newSize.Height - original.Height) / 2;
graphics.DrawImageUnscaledAndClipped(original, new Rectangle(offsetX, offsetY, original.Width, original.Height));
return image;
}
#endif

StateImageList = stateImageList;
ImageList = imageList;
}

protected override void OnBeforeCollapse(TreeViewCancelEventArgs e)
{
if (activeDoubleClick && e.Action == TreeViewAction.Collapse)
e.Cancel = true;
activeDoubleClick = false;
base.OnBeforeCollapse(e);
}

protected override void OnBeforeExpand(TreeViewCancelEventArgs e)
{
if (activeDoubleClick && e.Action == TreeViewAction.Expand)
e.Cancel = true;
activeDoubleClick = false;
base.OnBeforeExpand(e);
}

protected override void OnMouseDown(MouseEventArgs e)
{
activeDoubleClick = e.Clicks > 1;
base.OnMouseDown(e);
}

public void UpdateWorkflow(string name, WorkflowBuilder workflowBuilder)
{
BeginUpdate();
Nodes.Clear();

var rootNode = Nodes.Add(name);
AddWorkflow(rootNode.Nodes, null, workflowBuilder.Workflow, ExplorerNodeType.Editable);

static void AddWorkflow(
TreeNodeCollection nodes,
WorkflowEditorPath basePath,
ExpressionBuilderGraph workflow,
ExplorerNodeType parentNodeType)
{
for (int i = 0; i < workflow.Count; i++)
{
var builder = workflow[i].Value;
if (ExpressionBuilder.Unwrap(builder) is IWorkflowExpressionBuilder workflowBuilder &&
workflowBuilder.Workflow != null)
{
var nodeType = parentNodeType == ExplorerNodeType.ReadOnly || workflowBuilder is IncludeWorkflowBuilder
? ExplorerNodeType.ReadOnly
: ExplorerNodeType.Editable;
var displayName = ExpressionBuilder.GetElementDisplayName(builder);
var builderPath = new WorkflowEditorPath(i, basePath);
var node = nodes.Add(displayName);
node.ImageIndex = node.SelectedImageIndex = GetImageIndex(nodeType);
node.Tag = builderPath;
AddWorkflow(node.Nodes, builderPath, workflowBuilder.Workflow, nodeType);
}
}
}

SetNodeStatus(ExplorerNodeStatus.Ready);
rootNode.Expand();
EndUpdate();
}

public void SelectNode(WorkflowEditorPath path)
{
SelectNode(Nodes, path);
}

bool SelectNode(TreeNodeCollection nodes, WorkflowEditorPath path)
{
foreach (TreeNode node in nodes)
{
var nodePath = (WorkflowEditorPath)node.Tag;
if (nodePath == path)
{
SelectedNode = node;
return true;
}

var selected = SelectNode(node.Nodes, path);
if (selected) break;
}

return false;
}

private static int GetImageIndex(ExplorerNodeType status)
{
return status switch
{
ExplorerNodeType.Editable => 0,
ExplorerNodeType.ReadOnly => 1,
_ => throw new ArgumentException("Invalid node type.", nameof(status))
};
}

private static int GetStateImageIndex(ExplorerNodeStatus status)
{
return status switch
{
ExplorerNodeStatus.Ready => -1,
ExplorerNodeStatus.Blocked => 1,
_ => throw new ArgumentException("Invalid node status.", nameof(status))
};
}

public void SetNodeStatus(ExplorerNodeStatus status)
{
var imageIndex = GetStateImageIndex(status);
SetNodeImageIndex(Nodes, imageIndex);

static void SetNodeImageIndex(TreeNodeCollection nodes, int index)
{
foreach (TreeNode node in nodes)
{
if (node.StateImageIndex == index)
continue;

node.StateImageIndex = index;
SetNodeImageIndex(node.Nodes, index);
}
}
}

public void SetNodeStatus(IEnumerable<WorkflowEditorPath> pathElements, ExplorerNodeStatus status)
{
var nodes = Nodes;
var imageIndex = GetStateImageIndex(status);
foreach (var path in pathElements.Prepend(null))
{
var found = false;
for (int n = 0; n < nodes.Count; n++)
{
var groupNode = nodes[n];
if ((WorkflowEditorPath)groupNode.Tag == path)
{
groupNode.StateImageIndex = imageIndex;
nodes = groupNode.Nodes;
found = true;
break;
}
}

if (!found)
break;
}
}
}

enum ExplorerNodeType
{
Editable,
ReadOnly
}

enum ExplorerNodeStatus
{
Ready,
Blocked
}
}
41 changes: 0 additions & 41 deletions Bonsai.Editor/GraphModel/WorkflowBuilderExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -91,47 +91,6 @@ public static IEnumerable<ContextGrouping> GetDependentExpressions(this SubjectD
}
}
}

public static ExpressionScope GetExpressionScope(this WorkflowBuilder source, ExpressionBuilder target)
{
return GetExpressionScope(source.Workflow, target);
}

static ExpressionScope GetExpressionScope(ExpressionBuilderGraph source, ExpressionBuilder target)
{
foreach (var node in source)
{
var builder = ExpressionBuilder.Unwrap(node.Value);
if (builder == target)
{
return new ExpressionScope(node.Value, innerScope: null);
}

if (builder is IWorkflowExpressionBuilder workflowBuilder)
{
var innerScope = GetExpressionScope(workflowBuilder.Workflow, target);
if (innerScope != null)
{
return new ExpressionScope(node.Value, innerScope);
}
}
}

return null;
}
}

class ExpressionScope
{
public ExpressionScope(ExpressionBuilder value, ExpressionScope innerScope)
{
Value = value;
InnerScope = innerScope;
}

public ExpressionBuilder Value { get; }

public ExpressionScope InnerScope { get; }
}

class SubjectDefinition
Expand Down
Loading

0 comments on commit 76e8ce9

Please sign in to comment.