[
  {
    "path": ".editorconfig",
    "content": "\n[*.{c,c++,cc,cginc,compute,cp,cpp,cu,cuh,cxx,h,hh,hlsl,hlsli,hlslinc,hpp,hxx,inc,inl,ino,ipp,mpp,mq4,mq5,mqh,tpp,usf,ush}]\nindent_style = tab\nindent_size = tab\ntab_width = 4\n\n[*.{asax,ascx,aspx,axaml,cs,cshtml,css,htm,html,js,jsx,master,paml,razor,skin,ts,tsx,vb,xaml,xamlx,xoml}]\nindent_style = space\nindent_size = 4\ntab_width = 4\n\n[*.{appxmanifest,axml,build,config,csproj,dbml,discomap,dtd,json,jsproj,lsproj,njsproj,nuspec,proj,props,resjson,resw,resx,StyleCop,targets,tasks,vbproj,xml,xsd}]\nindent_style = space\nindent_size = 2\ntab_width = 2\n\n[*]\n\n# Microsoft .NET properties\ncsharp_new_line_before_members_in_object_initializers = false\ncsharp_preferred_modifier_order = public, private, protected, internal, new, abstract, virtual, sealed, override, static, readonly, extern, unsafe, volatile, async:suggestion\ncsharp_style_var_for_built_in_types = false:suggestion\ndotnet_style_parentheses_in_arithmetic_binary_operators = never_if_unnecessary:none\ndotnet_style_parentheses_in_other_binary_operators = never_if_unnecessary:none\ndotnet_style_parentheses_in_relational_binary_operators = never_if_unnecessary:none\ndotnet_style_predefined_type_for_locals_parameters_members = true:suggestion\ndotnet_style_predefined_type_for_member_access = true:suggestion\ndotnet_style_qualification_for_event = false:suggestion\ndotnet_style_qualification_for_field = false:suggestion\ndotnet_style_qualification_for_method = false:suggestion\ndotnet_style_qualification_for_property = false:suggestion\ndotnet_style_require_accessibility_modifiers = for_non_interface_members:suggestion\n\n# ReSharper properties\nresharper_for_built_in_types = use_var_when_evident\nresharper_for_simple_types = use_var_when_evident\n\n# ReSharper inspection severities\nresharper_arrange_object_creation_when_type_evident_highlighting = none\nresharper_arrange_redundant_parentheses_highlighting = hint\nresharper_arrange_this_qualifier_highlighting = hint\nresharper_arrange_type_member_modifiers_highlighting = hint\nresharper_arrange_type_modifiers_highlighting = hint\nresharper_built_in_type_reference_style_for_member_access_highlighting = hint\nresharper_built_in_type_reference_style_highlighting = hint\nresharper_merge_sequential_patterns_highlighting = none\nresharper_redundant_base_qualifier_highlighting = warning\nresharper_suggest_var_or_type_built_in_types_highlighting = hint\n"
  },
  {
    "path": ".github/FUNDING.yml",
    "content": "# These are supported funding model platforms\n\ngithub: miroiu\ncustom: [\"https://www.buymeacoffee.com/miroiu\", \"https://paypal.me/miroiuemanuel\"]\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/ask-a-question.md",
    "content": "---\nname: \"❓ Ask a question\"\nabout: Need help or have a question?\ntitle: \"[Question]\"\nlabels: question\nassignees: miroiu\n\n---\n\n\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "content": "---\nname: \"\\U0001F41B Bug report\"\nabout: Create a bug report to help this project improve\ntitle: \"[Bug]\"\nlabels: bug\nassignees: miroiu\n\n---\n\n<!--\n  Hi! \n  \n  All fields are optional and are present only to guide you.\n  Thanks for contributing!\n-->\n\n**Describe the bug**\nA clear and concise description of what the bug is.\n\n**To Reproduce**\nSteps to reproduce the behavior.\n\n**Expected behavior**\nA clear and concise description of what you expected to happen.\n\n**Screenshots**\nIf applicable, add screenshots to help explain your problem.\n\n**Does it happen in WPF version?**\nIf possible, please try to reproduce the bug in the [WPF version](https://github.com/miroiu/nodify). If it also happen in WPF's, it has to be fixed in the upstream and then it can be merged to this Avalonia port.\n\n**Additional context**\nAdd any other context about the problem here.\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "content": "blank_issues_enabled: false\ncontact_links:\n    - name: 📝 Read the docs\n      url: https://github.com/miroiu/nodify/wiki\n      about: Be sure you've read the docs!\n"
  },
  {
    "path": ".github/workflows/build.yml",
    "content": "name: Build\n\non:\n  push:\n    branches: [\"master\", \"release-v*\", \"avalonia_port\"]\n    paths-ignore:\n      - \"docs/**\"\n  pull_request:\n    paths-ignore:\n      - \"docs/**\"\n\njobs:\n  build:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v4\n      - name: Setup .NET\n        uses: actions/setup-dotnet@v4\n        with:\n          dotnet-version: \"9.0.x\"\n      - name: Build\n        run: |\n          dotnet workload restore\n          dotnet build --configuration Release\n"
  },
  {
    "path": ".github/workflows/codeql-analysis.yml",
    "content": "name: \"CodeQL\"\n\non:\n  push:\n    branches: [\"master\", \"release-v*\", \"avalonia_port\"]\n    paths:\n      - Nodify/**\n  pull_request:\n    branches: [\"master\", \"release-v*\", \"avalonia_port\"]\n  schedule:\n    - cron: \"27 6 * * 2\"\n\njobs:\n  analyze:\n    name: Analyze\n    runs-on: ubuntu-latest\n\n    strategy:\n      fail-fast: false\n      matrix:\n        include:\n          - language: csharp\n            build-mode: none\n\n    steps:\n      - name: Checkout repository\n        uses: actions/checkout@v4\n\n      # Initializes the CodeQL tools for scanning.\n      - name: Initialize CodeQL\n        uses: github/codeql-action/init@v3\n        with:\n          languages: ${{ matrix.language }}\n          build-mode: ${{ matrix.build-mode }}\n\n      - name: Perform CodeQL Analysis\n        uses: github/codeql-action/analyze@v3\n        with:\n          category: \"/language:${{matrix.language}}\"\n"
  },
  {
    "path": ".github/workflows/create-release-branch.yml",
    "content": "name: Create release branch\n\non:\n  push:\n    tags:\n      - \"v*.0.0\"\n\njobs:\n  create-release:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v4\n      - name: Save tag version\n        uses: little-core-labs/get-git-tag@v3.0.1\n        id: tagName\n      - name: Setup .NET Core\n        uses: actions/setup-dotnet@v2.1.0\n        with:\n          dotnet-version: '9.0.x'\n      - name: Install dependencies\n        run:  |\n          dotnet workload restore\n          dotnet restore\n      - name: Build\n        run: dotnet build --configuration Release --no-restore\n      - name: Create release branch\n        uses: peterjgrainger/action-create-branch@v2.2.0\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n        with:\n          branch: release-${{ steps.tagName.outputs.tag }}\n    \n"
  },
  {
    "path": ".github/workflows/publish-package.yml",
    "content": "name: Publish package\n\non:\n  workflow_dispatch:\n  push:\n    tags:\n      - \"v*.*.*\"\n\njobs:\n  publish:\n    runs-on: ubuntu-latest\n\n    steps:\n      - uses: actions/checkout@v4\n      - name: Setup .NET Core\n        uses: actions/setup-dotnet@v4\n        with:\n          dotnet-version: '9.0.x'\n      - name: Install dependencies\n        run:  |\n          dotnet workload restore\n          dotnet restore\n      - name: Build\n        run: dotnet build --configuration Release --no-restore\n      - name: Publish the package\n        run: dotnet nuget push \"*/bin/Release/*.nupkg\" -k ${{ secrets.NUGET_KEY }} -s https://api.nuget.org/v3/index.json\n\n      \n"
  },
  {
    "path": ".github/workflows/sync-docs.yml",
    "content": "name: Documentation\n\non:\n  push:\n    branches:\n      - master\n    paths:\n      - \"docs/**\"\n  workflow_dispatch:\n\njobs:\n  job-sync-docs-to-wiki:\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout Repo\n        uses: actions/checkout@v2\n      - name: Sync docs to wiki\n        uses: newrelic/wiki-sync-action@main\n        with:\n          source: docs\n          destination: wiki\n          token: ${{ secrets.DOCS_TOKEN  }}\n"
  },
  {
    "path": ".gitignore",
    "content": "## Ignore Visual Studio temporary files, build results, and\n## files generated by popular Visual Studio add-ons.\n##\n## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore\n\n# User-specific files\n*.rsuser\n*.suo\n*.user\n*.userosscache\n*.sln.docstates\n\n# User-specific files (MonoDevelop/Xamarin Studio)\n*.userprefs\n\n# Mono auto generated files\nmono_crash.*\n\n# Build results\n[Dd]ebug/\n[Dd]ebugPublic/\n[Rr]elease/\n[Rr]eleases/\nx64/\nx86/\n[Aa][Rr][Mm]/\n[Aa][Rr][Mm]64/\nbld/\n[Bb]in/\n[Oo]bj/\n[Ll]og/\n[Ll]ogs/\n\n# Visual Studio 2015/2017 cache/options directory\n.vs/\n# Uncomment if you have tasks that create the project's static files in wwwroot\n#wwwroot/\n\n# Visual Studio 2017 auto generated files\nGenerated\\ Files/\n\n# MSTest test Results\n[Tt]est[Rr]esult*/\n[Bb]uild[Ll]og.*\n\n# NUnit\n*.VisualState.xml\nTestResult.xml\nnunit-*.xml\n\n# Build Results of an ATL Project\n[Dd]ebugPS/\n[Rr]eleasePS/\ndlldata.c\n\n# Benchmark Results\nBenchmarkDotNet.Artifacts/\n\n# .NET Core\nproject.lock.json\nproject.fragment.lock.json\nartifacts/\n\n# StyleCop\nStyleCopReport.xml\n\n# Files built by Visual Studio\n*_i.c\n*_p.c\n*_h.h\n*.ilk\n*.meta\n*.obj\n*.iobj\n*.pch\n*.pdb\n*.ipdb\n*.pgc\n*.pgd\n*.rsp\n*.sbr\n*.tlb\n*.tli\n*.tlh\n*.tmp\n*.tmp_proj\n*_wpftmp.csproj\n*.log\n*.vspscc\n*.vssscc\n.builds\n*.pidb\n*.svclog\n*.scc\n\n# Chutzpah Test files\n_Chutzpah*\n\n# Visual C++ cache files\nipch/\n*.aps\n*.ncb\n*.opendb\n*.opensdf\n*.sdf\n*.cachefile\n*.VC.db\n*.VC.VC.opendb\n\n# Visual Studio profiler\n*.psess\n*.vsp\n*.vspx\n*.sap\n\n# Visual Studio Trace Files\n*.e2e\n\n# TFS 2012 Local Workspace\n$tf/\n\n# Guidance Automation Toolkit\n*.gpState\n\n# ReSharper is a .NET coding add-in\n_ReSharper*/\n*.[Rr]e[Ss]harper\n*.DotSettings.user\n\n# TeamCity is a build add-in\n_TeamCity*\n\n# DotCover is a Code Coverage Tool\n*.dotCover\n\n# AxoCover is a Code Coverage Tool\n.axoCover/*\n!.axoCover/settings.json\n\n# Visual Studio code coverage results\n*.coverage\n*.coveragexml\n\n# NCrunch\n_NCrunch_*\n.*crunch*.local.xml\nnCrunchTemp_*\n\n# MightyMoose\n*.mm.*\nAutoTest.Net/\n\n# Web workbench (sass)\n.sass-cache/\n\n# Installshield output folder\n[Ee]xpress/\n\n# DocProject is a documentation generator add-in\nDocProject/buildhelp/\nDocProject/Help/*.HxT\nDocProject/Help/*.HxC\nDocProject/Help/*.hhc\nDocProject/Help/*.hhk\nDocProject/Help/*.hhp\nDocProject/Help/Html2\nDocProject/Help/html\n\n# Click-Once directory\npublish/\n\n# Publish Web Output\n*.[Pp]ublish.xml\n*.azurePubxml\n# Note: Comment the next line if you want to checkin your web deploy settings,\n# but database connection strings (with potential passwords) will be unencrypted\n*.pubxml\n*.publishproj\n\n# Microsoft Azure Web App publish settings. Comment the next line if you want to\n# checkin your Azure Web App publish settings, but sensitive information contained\n# in these scripts will be unencrypted\nPublishScripts/\n\n# NuGet Packages\n*.nupkg\n# NuGet Symbol Packages\n*.snupkg\n# The packages folder can be ignored because of Package Restore\n**/[Pp]ackages/*\n# except build/, which is used as an MSBuild target.\n!**/[Pp]ackages/build/\n# Uncomment if necessary however generally it will be regenerated when needed\n#!**/[Pp]ackages/repositories.config\n# NuGet v3's project.json files produces more ignorable files\n*.nuget.props\n*.nuget.targets\n\n# Microsoft Azure Build Output\ncsx/\n*.build.csdef\n\n# Microsoft Azure Emulator\necf/\nrcf/\n\n# Windows Store app package directories and files\nAppPackages/\nBundleArtifacts/\nPackage.StoreAssociation.xml\n_pkginfo.txt\n*.appx\n*.appxbundle\n*.appxupload\n\n# Visual Studio cache files\n# files ending in .cache can be ignored\n*.[Cc]ache\n# but keep track of directories ending in .cache\n!?*.[Cc]ache/\n\n# Others\nClientBin/\n~$*\n*~\n*.dbmdl\n*.dbproj.schemaview\n*.jfm\n*.pfx\n*.publishsettings\norleans.codegen.cs\n\n# Including strong name files can present a security risk\n# (https://github.com/github/gitignore/pull/2483#issue-259490424)\n#*.snk\n\n# Since there are multiple workflows, uncomment next line to ignore bower_components\n# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)\n#bower_components/\n\n# RIA/Silverlight projects\nGenerated_Code/\n\n# Backup & report files from converting an old project file\n# to a newer Visual Studio version. Backup files are not needed,\n# because we have git ;-)\n_UpgradeReport_Files/\nBackup*/\nUpgradeLog*.XML\nUpgradeLog*.htm\nServiceFabricBackup/\n*.rptproj.bak\n\n# SQL Server files\n*.mdf\n*.ldf\n*.ndf\n\n# Business Intelligence projects\n*.rdl.data\n*.bim.layout\n*.bim_*.settings\n*.rptproj.rsuser\n*- [Bb]ackup.rdl\n*- [Bb]ackup ([0-9]).rdl\n*- [Bb]ackup ([0-9][0-9]).rdl\n\n# Microsoft Fakes\nFakesAssemblies/\n\n# GhostDoc plugin setting file\n*.GhostDoc.xml\n\n# Node.js Tools for Visual Studio\n.ntvs_analysis.dat\nnode_modules/\n\n# Visual Studio 6 build log\n*.plg\n\n# Visual Studio 6 workspace options file\n*.opt\n\n# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)\n*.vbw\n\n# Visual Studio LightSwitch build output\n**/*.HTMLClient/GeneratedArtifacts\n**/*.DesktopClient/GeneratedArtifacts\n**/*.DesktopClient/ModelManifest.xml\n**/*.Server/GeneratedArtifacts\n**/*.Server/ModelManifest.xml\n_Pvt_Extensions\n\n# Paket dependency manager\n.paket/paket.exe\npaket-files/\n\n# FAKE - F# Make\n.fake/\n\n# CodeRush personal settings\n.cr/personal\n\n# Python Tools for Visual Studio (PTVS)\n__pycache__/\n*.pyc\n\n# Cake - Uncomment if you are using it\n# tools/**\n# !tools/packages.config\n\n# Tabs Studio\n*.tss\n\n# Telerik's JustMock configuration file\n*.jmconfig\n\n# BizTalk build output\n*.btp.cs\n*.btm.cs\n*.odx.cs\n*.xsd.cs\n\n# OpenCover UI analysis results\nOpenCover/\n\n# Azure Stream Analytics local run output\nASALocalRun/\n\n# MSBuild Binary and Structured Log\n*.binlog\n\n# NVidia Nsight GPU debugger configuration file\n*.nvuser\n\n# MFractors (Xamarin productivity tool) working folder\n.mfractor/\n\n# Local History for Visual Studio\n.localhistory/\n\n# BeatPulse healthcheck temp database\nhealthchecksdb\n\n# Backup folder for Package Reference Convert tool in Visual Studio 2017\nMigrationBackup/\n\n# Ionide (cross platform F# VS Code tools) working folder\n.ionide/\n.idea\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "## Changelog\n\n#### **In development**\n\n> - Breaking Changes:\n> - Features:\n> - Bugfixes:\n\t\n#### **Version 6.6.0**\n\n> - Breaking Changes:\n> - Features:\n>\t- Added InputGroupStyle and OutputGroupStyle to Node (not supported in Avalonia)\n>\t- Added PanWithMouseWheel, PanHorizontalModifierKey and PanVerticalModifierKey to EditorGestures.Editor\n>\t- Added CornerRadius dependency property to LineConnection, CircuitConnection and StepConnection\n>\t- Added EditorGestures.Editor.PushItems gesture used to start pushing ItemContainers vertically or horizontally\n>\t- Added PushedAreaStyle, PushedAreaOrientation and IsPushingItems dependency properties to NodifyEditor\n>\t- Added NodifyEditor.SnapToGrid utility function\n> - Bugfixes:\n>\t- Fixed ItemContainer.BorderBrush and ItemContainer.SelectedBrush not reacting to theme changes\n\n#### **Version 6.5.0**\n\n> - Features:\n>\t- Added SelectedConnection, SelectedConnections, CanSelectMultipleConnections and CanSelectMultipleItems dependency properties to NodifyEditor\n>\t- Added IsSelected and IsSelectable attached dependency properties to BaseConnection\n>\t- Added PrioritizeBaseConnectionForSelection static field to BaseConnection\n>\t- Added EditorGestures.Connection.Selection\n>\t- Added support for ScrollViewer in NodifyEditor (implements IScrollInfo)\n>\t- Added NodifyEditor.ScrollIncrement dependency property\n\n#### **Version 6.4.0**\n\n> - Features:\n>\t- Added OutlineBrush and OutlineThickness dependency properties to BaseConnection to support increasing the selection area without increasing the stroke thickness\n>\t- Added IsAnimatingDirectionalArrows and DirectionalArrowsAnimationDuration dependency properties to BaseConnection to support controlling the animation from XAML\n\n#### **Version 6.3.0**\n\n> - Features:\n>\t- Added a CuttingLine control that removes intersecting connections\n>\t- Added CuttingLineStyle, CuttingStartedCommand, CuttingCompletedCommand, IsCutting, EnableCuttingLinePreview and CuttingConnectionTypes to NodifyEditor\n>\t- Added EditorGestures.Editor.Cutting and EditorGestures.Editor.CancelAction\n> - Bugfixes:\n>\t- Fixed connection styles not inheriting from the BaseConnection style\n\n#### **Version 6.2.0**\n\n> - Features:\n>\t- Added a Minimap control and EditorGestures.Minimap\n>\t- Added ContentContainerStyle, HeaderContainerStyle and FooterContainerStyle dependency properties to Node\n>\t- Added BringIntoView that takes a Rect parameter to NodifyEditor\n>\t- Added the NodifyEditor's DataContext as the parameter of the ItemsSelectStartedCommand, ItemsSelectCompletedCommand, ItemsDragStartedCommand and ItemsDragCompletedCommand commands\n> - Bugfixes:\n>\t- Fixed hover effect and padding of NodeInput and NodeOutput for vertical orientation\n>\t- Fixed ItemContainers being selected sometimes when double clicking the canvas\n\n#### **Version 6.1.0**\n\n> - Features:\n>\t- Added new built-in connection type: StepConnection\n> - Bugfixes:\n>\t- Fixed CircuitConnection directional arrows not interpolating correctly\n>\t- Fixed BaseConnection SplitEvent and DisconnectEvent not being raised if the corresponding command is null\n>\t- Fixed DecoratorContainer scaling with zoom when not referencing a theme in App.xaml\n>\t- Fixed style not applying to the default Connection template outside App.xaml\n\t\n#### **Version 6.0.0**\n\n> - Breaking Changes:\n>\t- Added a parameter for the orientation to DrawArrowGeometry, DrawDefaultArrowhead, DrawRectangleArrowhead and DrawEllipseArrowhead in BaseConnection\n>\t- Added source and target parameters to GetTextPosition in BaseConnection\n>\t- EditorGestures is now a singleton instead of a static class (can be inherited to create custom mappings)\n>\t- Selection gestures for ItemContainer and GroupingNode are now separated from the NodifyEditor selection gestures\n>\t- Renamed EditorGestures.Editor.Zoom to ZoomModifierKey\n> - Features:\n>\t- Added SourceOrientation and TargetOrientation to BaseConnection to support vertical connectors (vertical/mixed connection orientation)\n>\t- Added DirectionalArrowsCount to BaseConnection to allow drawing multipe arrows on a connection flowing in the connection direction\n>\t- Added DrawDirectionalArrowsGeometry and DrawDirectionalArrowheadGeometry to BaseConnection to allow customizing the directional arrows\n>\t- Improved EditorGestures to allow changing input gestures at runtime\n>\t- Added new gesture types: AnyGesture, AllGestures, and InputGestureRef\n>\t- Added Orientation dependency property to NodeInput and NodeOutput\n>\t- Added DirectionalArrowsOffset dependency property to BaseConnection\n>\t- Added StartAnimation and StopAnimation methods to BaseConnection\n> - Bugfixes:\n>\t- Fixed BaseConnection.Text not always displaying in the center of the connection\n>\t- Fixed a bug where the item container would incorrectly transition to the dragging state on mouse over\n\n#### **Version 5.2.0**\n\n> - Features:\n>\t- Added Text to BaseConnection, allowing displaying of text on connections\n>\t- Added Foreground, FontSize, FontWeight, FontStyle, FontStretch and FontFamily to BaseConnection, allowing styling the displaying text\n> - Bugfixes:\n>   - Fixed MouseCapture not being released when EnableStickyConnections is enabled and the PendingConnection is canceled by a key gesture \n\n#### **Version 5.1.0**\n\n> - Features:\n>   - Added ItemContainer.SelectedBorderThickness dependency property\n>   - Added NodifyEditor.GetLocationInsideEditor\n> - Bugfixes:\n>   - Fixed PendingConnection.PreviewTarget not being set to null when there is no actual target\n>   - Fixed PendingConnection.PreviewTarget not being set on Connector.PendingConnectionStartedEvent\n>   - Fixed PendingConnection.PreviewTarget not being set to null on Connector.PendingConnectionCompletedEvent\n>   - Fixed connectors panel not being affected by Node.VerticalAlignment\n>   - Changing BorderThickness causes layout shift when selecting an item container\n>   - Fixed the unintentional movement caused by snapping correction\n\n#### **Version 5.0.2**\n\n> - Bugfixes:\n>   - Fixed NodeOutput content horizontal alignment\n>   - Fixed Connector not opening Context Menu\n\n#### **Version 5.0.1**\n\n> - Bugfixes:\n>   - Returning false from PendingConnection.StartedCommand.CanExecute does not stop the creation of a pending connection\n>   - BaseConnection.ArrowEnds does not display correctly when BaseConnection.Direction is ConnectionDirection.Backward\n\n#### **Version 5.0.0**\n\n> - Breaking Changes:\n>   - Removed BaseConnection.GetArrowHeadPoints\n>   - Removed BaseConnection.OffsetMode\n>   - Changed return type of BaseConnection.DrawLineGeometry to support both arrowheads no matter the number of points on the line\n>   - Changed the default for BaseConnection.SourceOffset and BaseConnection.TargetOffset from Size(0, 0) to Size(14, 0)\n>   - Changed the default for BaseConnection.ArrowSize from Size(7, 6) to Size(8, 8)\n> - Features:\n>   - Added BaseConnection.SourceOffsetMode and BaseConnection.TargetOffsetMode\n>   - Added BaseConnection.ArrowEnds dependency property to allow configurable arrowhead ends\n>   - Added BaseConnection.ArrowShape dependency property to allow configurable arrowhead shape\n>   - Added NodifyEditor.EnableDraggingContainersOptimizations to allow receiving ItemContainer.Location updates in realtime\n>   - Added ConnectionOffsetMode.Static to allow offsetting the source and target points of the connection on the X and the Y axis without revolving around the source or target points\n\n#### **Version 4.1.0**\n\n> - Features:\n>   - Added EditorGestures.Selection.DefaultMouseAction to make it easier to change between mouse buttons for selection\n>   - Added EditorGestures.Selection.Cancel gesture to cancel the selection operation reverting to the previous selection\n>   - Added ItemsSelectStartedCommand and ItemsSelectCompletedCommand dependency properties to NodifyEditor for better undo/redo support\n> - Bugfixes:\n>   - Fixed NodifyEditor.SelectedItems being empty after selection is completed\n>   - Fixed drag canceling when Drag and CancelAction are bound to the same gesture\n\n#### **Version 4.0.1**\n\n> - Bugfixes:\n>   - Fixed DisablePanning not working anymore\n\n#### **Version 4.0.0**\n\n> - Breaking Changes:\n>   - Removed Selection field from NodifyEditor\n>   - Removed InitialMousePosition, CurrentMousePosition, PreviousMousePosition fields from NodifyEditor\n>   - Removed ItemContainer.DraggableHost (use Editor.ItemsHost instead)\n>   - Made SelectionType required in SelectionHelper\n>   - Moved GroupingNode.SwitchMovementModeModifierKey to EditorGestures.GroupingNode\n>   - Pending connections are now restricted to connect only to Connectors or to NodifyEditors and ItemContainers if PendingConnection.AllowOnlyConnectors is false\n> - Features:\n>   - Added Connector.EnableStickyConnections to allow completing pending connections in two steps\n>   - Added editor states which can be overriden by inheriting from NodifyEditor and implementing NodifyEditor.GetInitialState()\n>     - EditorState - base class for all editor states\n>     - EditorDefaultState\n>     - EditorSelectingState\n>     - EditorPanningState\n>   - Added container states which can be overriden by inheriting from ItemContainer and implementing ItemContainer.GetInitialState()\n>     - ContainerState - base class for all container states\n>     - ContainerDefaultState\n>     - ContainerDraggingState\n>   - Added MultiGesture utility that can combine multiple input gestures into one gesture\n>   - Added configurable input gestures for NodifyEditor, ItemContainer, Connector, BaseConnection and GroupingNode to EditorGestures\n>   - Added State, PushState, PopState and PopAllStates to NodifyEditor and ItemContainer\n>   - Changed the default AutoPanSpeed to 15 from 10 pixels per tick\n>   - Allow setting ItemContainer.IsPreviewingLocation from derived classes\n> - Bugfixes:\n>   - Fixed HandleRightClickAfterPanningThreshold not working as expected\n>   - Fixed DisablePanning not disabling auto panning in certain situations\n>   - Fixed GroupingNode selection not working with multiple selection modes\n>   - Fixed PendingConnection connecting cross editors\n\n#### **Version 3.0.0**\n\n> - Breaking Changes:\n>   - Changed Decorators from UIElement collection to IEnumerable\n> - Features:\n>   - Added ItemsExtent and DecoratorsExtent dependency properties to NodifyEditor\n>   - Added DecoratorTemplate dependency property to NodifyEditor\n>   - Added FitToScreenExtentMargin static field to NodifyEditor\n>   - Added Extent dependency property to NodifyCanvas\n> - Bugfixes:\n>   - Selection rectangle and Decorators are no longer scaled with the viewport zoom\n>   - Fixed connector anchor not updating when container size changed\n\n#### **Version 2.0.1**\n\n> - Bugfixes:\n>   - Fixed pending connection default style\n\n#### **Version 2.0.0**\n\n> - Breaking Changes:\n>   - Renamed Offset to ViewportLocation in NodifyEditor\n>   - Renamed Scale to ViewportZoom in NodifyEditor\n>   - Renamed MinScale to MinViewportZoom in NodifyEditor\n>   - Renamed MaxScale to MaxViewportZoom in NodifyEditor\n>   - Renamed AppliedTransform to ViewportTransform in NodifyEditor\n>   - Renamed DirectionalConnection to LineConnection\n>   - Removed BringIntoViewAnimationDuration from NodifyEditor\n>   - Removed Viewport dependency property from NodifyEditor\n>   - Removed ActualSize dependency property from StateNode\n>   - Removed Icon dependency property from Node as the icon can _(and should)_ be added in the HeaderTemplate if necessary\n>   - PART_ItemsHost is now required for NodifyEditor to work\n>   - ItemContainers cannot be used outside a NodifyEditor anymore\n>   - ZoomAtPosition now requires graph space coordinates instead of screen space coordinates\n>   - Removed custom value converters\n>   - Made DependencyObjectExtensions internal\n>   - Removed the <http://miroiu.github.io/winfx/xaml/nodify> xaml prefix\n> - Features:\n>   - Added ResizeStartedEvent routed event to GroupingNode\n>   - Added ViewportSize - **OneWayToSource** dependency property to NodifyEditor\n>   - Added ActualSize - **OneWayToSource** dependency property to ItemContainer\n>   - Added DecoratorContainer and DecoratorContainerStyle dependency properties to NodifyEditor\n>   - Added RemoveConnectionCommand command to NodifyEditor\n>   - Added DisconnectCommand and SplitCommand commands to BaseConnection\n>   - Added ContentBrush dependency property to NodifyEditor\n>   - Added HasFooter dependency property to Node\n>   - Added FitToScreen command to NodifyEditor and EditorCommands\n>   - Added onFinish callback to BringIntoView in NodifyEditor\n>   - Added ArrowSize and Spacing dependency properties to all connections inheriting from BaseConnection\n>   - Added BringIntoViewMaxDuration dependency property to NodifyEditor\n>   - Added BringIntoViewSpeed dependency property to NodifyEditor\n>   - Auto panning speed now scales with the zoom factor\n> - Bugfixes:\n>   - Every public property or method should work with graph space coordinates\n>   - Disable auto panning when panning is disabled\n>   - Min zoom could be set to a very small value\n>   - Bring into view was not disabling all interfering operations\n"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "content": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nIn the interest of fostering an open and welcoming environment, we as\ncontributors and maintainers pledge to making participation in our project and\nour community a harassment-free experience for everyone, regardless of age, body\nsize, disability, ethnicity, sex characteristics, gender identity and expression,\nlevel of experience, education, socio-economic status, nationality, personal\nappearance, race, religion, or sexual identity and orientation.\n\n## Our Standards\n\nExamples of behavior that contributes to creating a positive environment\ninclude:\n\n* Using welcoming and inclusive language\n* Being respectful of differing viewpoints and experiences\n* Gracefully accepting constructive criticism\n* Focusing on what is best for the community\n* Showing empathy towards other community members\n\nExamples of unacceptable behavior by participants include:\n\n* The use of sexualized language or imagery and unwelcome sexual attention or\n advances\n* Trolling, insulting/derogatory comments, and personal or political attacks\n* Public or private harassment\n* Publishing others' private information, such as a physical or electronic\n address, without explicit permission\n* Other conduct which could reasonably be considered inappropriate in a\n professional setting\n\n## Our Responsibilities\n\nProject maintainers are responsible for clarifying the standards of acceptable\nbehavior and are expected to take appropriate and fair corrective action in\nresponse to any instances of unacceptable behavior.\n\nProject maintainers have the right and responsibility to remove, edit, or\nreject comments, commits, code, wiki edits, issues, and other contributions\nthat are not aligned to this Code of Conduct, or to ban temporarily or\npermanently any contributor for other behaviors that they deem inappropriate,\nthreatening, offensive, or harmful.\n\n## Scope\n\nThis Code of Conduct applies both within project spaces and in public spaces\nwhen an individual is representing the project or its community. Examples of\nrepresenting a project or community include using an official project e-mail\naddress, posting via an official social media account, or acting as an appointed\nrepresentative at an online or offline event. Representation of a project may be\nfurther defined and clarified by project maintainers.\n\n## Enforcement\n\nInstances of abusive, harassing, or otherwise unacceptable behavior may be\nreported by contacting the project team at miroiu.emanuel@gmail.com. All\ncomplaints will be reviewed and investigated and will result in a response that\nis deemed necessary and appropriate to the circumstances. The project team is\nobligated to maintain confidentiality with regard to the reporter of an incident.\nFurther details of specific enforcement policies may be posted separately.\n\nProject maintainers who do not follow or enforce the Code of Conduct in good\nfaith may face temporary or permanent repercussions as determined by other\nmembers of the project's leadership.\n\n## Attribution\n\nThis Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,\navailable at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html\n\n[homepage]: https://www.contributor-covenant.org\n\nFor answers to common questions about this code of conduct, see\nhttps://www.contributor-covenant.org/faq\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# 👋 **Welcome to Nodify!** 👋\n\n👍🎉 First off, thanks for taking the time to contribute! Your contributions help make Nodify better for everyone. 👍🎉\n\nIf you find Nodify useful, please consider giving us a ⭐ **star** ⭐ on our GitHub repository!\n\nCode of Conduct: By contributing to Nodify, you agree to uphold our [Code of Conduct](CODE_OF_CONDUCT.md). We expect all contributors to be respectful and inclusive. (Don't worry, it's all common sense 😎)\n\n## How you can contribute\n\n- ❓ [Ask a question](https://github.com/miroiu/nodify/issues/new?assignees=miroiu&labels=question&template=ask-a-question.md&title=%5BQuestion%5D) - If you're unsure about anything related to Nodify, feel free to ask! No question is too small.\n- 🐛 [Create a bug report](https://github.com/miroiu/nodify/issues/new?assignees=miroiu&labels=bug&template=bug_report.md&title=%5BBug%5D) - Noticed something not working as expected? Let us know by creating a bug report. Please provide as much detail as possible to help us address the issue.\n- 🌺 [Suggest an enhancement](https://github.com/miroiu/nodify/issues/new?assignees=miroiu&labels=enhancement&template=feature_request.md&title=%5BFeature%5D) - Have an idea to make Nodify even better? We'd love to hear it! Share your suggestions for new features or improvements.\n- ✨ [Explore example applications](https://github.com/miroiu/nodify/tree/master/Examples) - Check out the example applications provided with Nodify. They're great for learning how to use the library in different scenarios.\n- 🎉 [Showcase your application](https://github.com/miroiu/nodify/issues/56) - Built something cool with Nodify? Share it with the community! We'd love to see what you've created.\n- 📝 [Help with the documentation](https://github.com/miroiu/nodify/wiki) - Documentation is crucial for making Nodify accessible to everyone. If you spot errors or have suggestions for improvement, please let us know or update the docs yourself!\n- 🔧 [Fix a bug](https://github.com/miroiu/nodify/labels/bug) - If you're a developer, you can contribute by fixing bugs in Nodify. Simply locate an open issue tagged as a bug and submit a pull request with your fix.\n- 🔗 [Create a pull request linking to a feature](https://github.com/miroiu/nodify/labels/enhancement) - Implemented a new feature or enhancement? Fantastic! Submit a pull request linking to the relevant feature or enhancement issue.\n\n## Some tips\n\n- Write clear and descriptive issues and try to avoid duplication\n- If you find a **Closed** issue that relates to yours, open a new issue and include a link to the original issue in the body of your new one.\n- The easiest way to update documentation is to navigate [to the docs website](https://github.com/miroiu/nodify/wiki) and click 'Edit this page' which is found at the top right of any page.\n- If you want to create an example application that others can use to learn from, then [create an issue](https://github.com/miroiu/nodify/issues/new?assignees=miroiu&labels=application&template=add_example_app.md&title=%5BApplication%5D) describing what your application is doing and if you need help with anything.\n- The application you showcase can use any license.\n"
  },
  {
    "path": "Directory.Build.props",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project DefaultTargets=\"Build\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n  <PropertyGroup>\n    <AvaloniaVersion>11.1.0</AvaloniaVersion>\n  </PropertyGroup>\n</Project>\n"
  },
  {
    "path": "Examples/Nodify.Calculator/App.xaml",
    "content": "﻿<Application xmlns=\"https://github.com/avaloniaui\"\n             xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n             x:Class=\"Nodify.Calculator.App\"\n             RequestedThemeVariant=\"Dark\">\n    <!-- \"Default\" ThemeVariant follows system theme variant. \"Dark\" or \"Light\" are other available options. -->\n    \n    <Application.Resources>\n        <ResourceDictionary>\n            <ResourceDictionary.MergedDictionaries>\n                <ResourceInclude Source=\"avares://Nodify.Shared/Themes/Icons.xaml\" />\n                <ResourceInclude Source=\"avares://Nodify.Shared/Themes/Generic.xaml\" />\n                <ResourceInclude Source=\"avares://Nodify.Shared/Themes/Dark.xaml\" />\n                <ResourceInclude Source=\"avares://Nodify/Themes/Generic.xaml\" />\n                <ResourceInclude Source=\"avares://Nodify/Themes/Dark.xaml\" />\n            </ResourceDictionary.MergedDictionaries>\n        </ResourceDictionary>\n    </Application.Resources>\n    \n    <Application.Styles>\n        <FluentTheme DensityStyle=\"Compact\"/>\n    </Application.Styles>\n</Application>\n"
  },
  {
    "path": "Examples/Nodify.Calculator/App.xaml.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Configuration;\nusing System.Data;\nusing System.Linq;\nusing System.Threading.Tasks;\nusing System.Windows;\n\nnamespace Nodify.Calculator\n{\n    /// <summary>\n    /// Interaction logic for App.xaml\n    /// </summary>\n    public partial class App : Application\n    {\n        public override void Initialize()\n        {\n            AvaloniaXamlLoader.Load(this);\n        }\n\n        public override void OnFrameworkInitializationCompleted()\n        {\n            if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)\n            {\n                desktop.MainWindow = new MainWindow();\n            }\n\n            base.OnFrameworkInitializationCompleted();\n        }\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.Calculator/ApplicationViewModel.cs",
    "content": "﻿using System;\nusing System.Linq;\nusing System.Windows.Input;\n\nnamespace Nodify.Calculator\n{\n    public class ApplicationViewModel : ObservableObject\n    {\n        public NodifyObservableCollection<EditorViewModel> Editors { get; } = new NodifyObservableCollection<EditorViewModel>();\n\n        public ApplicationViewModel()\n        {\n            AddEditorCommand = new DelegateCommand(() => Editors.Add(new EditorViewModel\n            {\n                Name = $\"Editor {Editors.Count + 1}\"\n            }));\n            CloseEditorCommand = new DelegateCommand<Guid>(\n                id => Editors.RemoveOne(editor => editor.Id == id),\n                _ => Editors.Count > 0 && SelectedEditor != null);\n            Editors.WhenAdded((editor) =>\n            {\n                if (AutoSelectNewEditor || Editors.Count == 1)\n                {\n                    SelectedEditor = editor;\n                }\n                editor.OnOpenInnerCalculator += OnOpenInnerCalculator;\n            })\n            .WhenRemoved((editor) =>\n            {\n                editor.OnOpenInnerCalculator -= OnOpenInnerCalculator;\n                var childEditors = Editors.Where(ed => ed.Parent == editor).ToList();\n                childEditors.ForEach(ed => Editors.Remove(ed));\n            });\n            Editors.Add(new EditorViewModel\n            {\n                Name = $\"Editor {Editors.Count + 1}\"\n            });\n        }\n\n        private void OnOpenInnerCalculator(EditorViewModel parentEditor, CalculatorViewModel calculator)\n        {\n            var editor = Editors.FirstOrDefault(e => e.Calculator == calculator);\n            if (editor != null)\n            {\n                SelectedEditor = editor;\n            }\n            else\n            {\n                var childEditor = new EditorViewModel\n                {\n                    Parent = parentEditor,\n                    Calculator = calculator,\n                    Name = $\"[Inner] Editor {Editors.Count + 1}\"\n                };\n                Editors.Add(childEditor);\n            }\n        }\n\n        public ICommand AddEditorCommand { get; }\n        public ICommand CloseEditorCommand { get; }\n\n        private EditorViewModel? _selectedEditor;\n        public EditorViewModel? SelectedEditor\n        {\n            get => _selectedEditor;\n            set => SetProperty(ref _selectedEditor, value);\n        }\n\n        private bool _autoSelectNewEditor = true;\n        public bool AutoSelectNewEditor\n        {\n            get => _autoSelectNewEditor;\n            set => SetProperty(ref _autoSelectNewEditor , value); \n        }\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.Calculator/AssemblyInfo.cs",
    "content": "using System.Windows;\n\n[assembly: ThemeInfo(\n    ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located\n                                     //(used if a resource is not found in the page,\n                                     // or application resource dictionaries)\n    ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located\n                                              //(used if a resource is not found in the page,\n                                              // app, or any theme specific resource dictionaries)\n)]\n"
  },
  {
    "path": "Examples/Nodify.Calculator/CalculatorInputOperationViewModel.cs",
    "content": "﻿namespace Nodify.Calculator\n{\n    public class CalculatorInputOperationViewModel : OperationViewModel\n    {\n        public CalculatorInputOperationViewModel()\n        {\n            AddOutputCommand = new RequeryCommand(\n                () => Output.Add(new ConnectorViewModel\n                {\n                    Title = $\"In {Output.Count}\"\n                }),\n                () => Output.Count < 10);\n\n            RemoveOutputCommand = new RequeryCommand(\n                () => Output.RemoveAt(Output.Count - 1),\n                () => Output.Count > 1);\n\n            Output.Add(new ConnectorViewModel\n            {\n                Title = $\"In {Output.Count}\"\n            });\n        }\n\n        public new NodifyObservableCollection<ConnectorViewModel> Output { get; set; } =\n            new NodifyObservableCollection<ConnectorViewModel>();\n\n        public INodifyCommand AddOutputCommand { get; }\n        public INodifyCommand RemoveOutputCommand { get; }\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.Calculator/CalculatorOperationViewModel.cs",
    "content": "﻿using System.Windows;\n\nnamespace Nodify.Calculator\n{\n    public class CalculatorOperationViewModel : OperationViewModel\n    {\n        public CalculatorViewModel InnerCalculator { get; } = new CalculatorViewModel();\n\n        private OperationViewModel InnerOutput { get; } = new OperationViewModel\n        {\n            Title = \"Output Parameters\",\n            Input = { new ConnectorViewModel() },\n            Location = new Point(500, 300),\n            IsReadOnly = true\n        };\n\n        private CalculatorInputOperationViewModel InnerInput { get; } = new CalculatorInputOperationViewModel\n        {\n            Title = \"Input Parameters\",\n            Location = new Point(300, 300),\n            IsReadOnly = true\n        };\n\n        public CalculatorOperationViewModel()\n        {\n            InnerCalculator.Operations.Add(InnerInput);\n            InnerCalculator.Operations.Add(InnerOutput);\n\n            Output = new ConnectorViewModel();\n\n            InnerOutput.Input[0].ValueObservers.Add(Output);\n\n            InnerInput.Output.ForEach(x => Input.Add(new ConnectorViewModel\n            {\n                Title = x.Title\n            }));\n\n            InnerInput.Output\n                .WhenAdded(x => Input.Add(new ConnectorViewModel\n                {\n                    Title = x.Title\n                }))\n                .WhenRemoved(x => Input.RemoveOne(i => i.Title == x.Title));\n        }\n\n        protected override void OnInputValueChanged()\n        {\n            for (var i = 0; i < Input.Count; i++)\n            {\n                InnerInput.Output[i].Value = Input[i].Value;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.Calculator/CalculatorViewModel.cs",
    "content": "﻿using System.Linq;\nusing System.Windows;\n\nnamespace Nodify.Calculator\n{\n    public class CalculatorViewModel : ObservableObject\n    {\n        public CalculatorViewModel()\n        {\n            CreateConnectionCommand = new DelegateCommand<ConnectorViewModel>(\n                _ => CreateConnection(PendingConnection.Source, PendingConnection.Target),\n                _ => CanCreateConnection(PendingConnection.Source, PendingConnection.Target));\n            StartConnectionCommand = new DelegateCommand<ConnectorViewModel>(_ => PendingConnection.IsVisible = true, (c) => !(c.IsConnected && c.IsInput));\n            DisconnectConnectorCommand = new DelegateCommand<ConnectorViewModel>(DisconnectConnector);\n            DeleteSelectionCommand = new DelegateCommand(DeleteSelection);\n            GroupSelectionCommand = new DelegateCommand(GroupSelectedOperations, () => SelectedOperations.Count > 0);\n\n            Connections.WhenAdded(c =>\n            {\n                c.Input.IsConnected = true;\n                c.Output.IsConnected = true;\n\n                c.Input.Value = c.Output.Value;\n\n                c.Output.ValueObservers.Add(c.Input);\n            })\n            .WhenRemoved(c =>\n            {\n                var ic = Connections.Count(con => con.Input == c.Input || con.Output == c.Input);\n                var oc = Connections.Count(con => con.Input == c.Output || con.Output == c.Output);\n\n                if (ic == 0)\n                {\n                    c.Input.IsConnected = false;\n                }\n\n                if (oc == 0)\n                {\n                    c.Output.IsConnected = false;\n                }\n\n                c.Output.ValueObservers.Remove(c.Input);\n            });\n\n            Operations.WhenAdded(x =>\n            {\n                x.Input.WhenRemoved(RemoveConnection);\n\n                if (x is CalculatorInputOperationViewModel ci)\n                {\n                    ci.Output.WhenRemoved(RemoveConnection);\n                }\n\n                void RemoveConnection(ConnectorViewModel i)\n                {\n                    var c = Connections.Where(con => con.Input == i || con.Output == i).ToArray();\n                    c.ForEach(con => Connections.Remove(con));\n                }\n            })\n            .WhenRemoved(x =>\n            {\n                foreach (var input in x.Input)\n                {\n                    DisconnectConnector(input);\n                }\n\n                if (x.Output != null)\n                {\n                    DisconnectConnector(x.Output);\n                }\n            });\n\n            OperationsMenu = new OperationsMenuViewModel(this);\n        }\n\n        private NodifyObservableCollection<OperationViewModel> _operations = new NodifyObservableCollection<OperationViewModel>();\n        public NodifyObservableCollection<OperationViewModel> Operations\n        {\n            get => _operations;\n            set => SetProperty(ref _operations, value);\n        }\n\n        private NodifyObservableCollection<OperationViewModel> _selectedOperations = new NodifyObservableCollection<OperationViewModel>();\n        public NodifyObservableCollection<OperationViewModel> SelectedOperations\n        {\n            get => _selectedOperations;\n            set => SetProperty(ref _selectedOperations, value);\n        }\n\n        public NodifyObservableCollection<ConnectionViewModel> Connections { get; } = new NodifyObservableCollection<ConnectionViewModel>();\n        public PendingConnectionViewModel PendingConnection { get; set; } = new PendingConnectionViewModel();\n        public OperationsMenuViewModel OperationsMenu { get; set; }\n\n        public INodifyCommand StartConnectionCommand { get; }\n        public INodifyCommand CreateConnectionCommand { get; }\n        public INodifyCommand DisconnectConnectorCommand { get; }\n        public INodifyCommand DeleteSelectionCommand { get; }\n        public INodifyCommand GroupSelectionCommand { get; }\n\n        private void DisconnectConnector(ConnectorViewModel connector)\n        {\n            var connections = Connections.Where(c => c.Input == connector || c.Output == connector).ToList();\n            connections.ForEach(c => Connections.Remove(c));\n        }\n\n        internal bool CanCreateConnection(ConnectorViewModel source, ConnectorViewModel? target)\n            => target == null || (source != target && source.Operation != target.Operation && source.IsInput != target.IsInput);\n\n        internal void CreateConnection(ConnectorViewModel source, ConnectorViewModel? target)\n        {\n            if (target == null)\n            {\n                PendingConnection.IsVisible = true;\n                OperationsMenu.OpenAt(PendingConnection.TargetLocation);\n                OperationsMenu.Closed += OnOperationsMenuClosed;\n                return;\n            }\n\n            var input = source.IsInput ? source : target;\n            var output = target.IsInput ? source : target;\n\n            PendingConnection.IsVisible = false;\n\n            DisconnectConnector(input);\n\n            Connections.Add(new ConnectionViewModel\n            {\n                Input = input,\n                Output = output\n            });\n        }\n\n        private void OnOperationsMenuClosed()\n        {\n            PendingConnection.IsVisible = false;\n            OperationsMenu.Closed -= OnOperationsMenuClosed;\n        }\n\n        private void DeleteSelection()\n        {\n            var selected = SelectedOperations.ToList();\n            selected.ForEach(o => Operations.Remove(o));\n        }\n\n        private void GroupSelectedOperations()\n        {\n            var selected = SelectedOperations.ToList();\n            var bounding = selected.GetBoundingBox(50);\n\n            Operations.Add(new OperationGroupViewModel\n            {\n                Title = \"Operations\",\n                Location = bounding.Position,\n                GroupSize = new Size(bounding.Width, bounding.Height)\n            });\n        }\n    }\n}\n\n"
  },
  {
    "path": "Examples/Nodify.Calculator/ConnectionViewModel.cs",
    "content": "﻿namespace Nodify.Calculator\n{\n    public class ConnectionViewModel : ObservableObject\n    {\n        private ConnectorViewModel _input = default!;\n        public ConnectorViewModel Input\n        {\n            get => _input;\n            set => SetProperty(ref _input, value);\n        }\n\n        private ConnectorViewModel _output = default!;\n        public ConnectorViewModel Output\n        {\n            get => _output;\n            set => SetProperty(ref _output, value);\n        }\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.Calculator/ConnectorViewModel.cs",
    "content": "﻿using System.Collections.Generic;\nusing System.Windows;\n\nnamespace Nodify.Calculator\n{\n    public class ConnectorViewModel : ObservableObject\n    {\n        private string? _title;\n        public string? Title\n        {\n            get => _title;\n            set => SetProperty(ref _title, value);\n        }\n\n        private double _value;\n        public double Value\n        {\n            get => _value;\n            set => SetProperty(ref _value, value)\n                .Then(() => ValueObservers.ForEach(o => o.Value = value));\n        }\n\n        private bool _isConnected;\n        public bool IsConnected\n        {\n            get => _isConnected;\n            set => SetProperty(ref _isConnected, value);\n        }\n\n        private bool _isInput;\n        public bool IsInput\n        {\n            get => _isInput;\n            set => SetProperty(ref _isInput, value);\n        }\n\n        private Point _anchor;\n        public Point Anchor\n        {\n            get => _anchor;\n            set => SetProperty(ref _anchor, value);\n        }\n\n        private OperationViewModel _operation = default!;\n        public OperationViewModel Operation\n        {\n            get => _operation;\n            set => SetProperty(ref _operation, value);\n        }\n\n        public List<ConnectorViewModel> ValueObservers { get; } = new List<ConnectorViewModel>();\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.Calculator/Converters/ItemToListConverter.cs",
    "content": "﻿using System;\nusing System.Collections;\nusing System.Collections.Generic;\nusing System.Globalization;\nusing System.Windows.Data;\n\nnamespace Nodify.Calculator\n{\n    public class ItemToListConverter : IValueConverter\n    {\n        public object? Convert(object value, Type targetType, object parameter, CultureInfo culture)\n        {\n            if (value != null)\n            {\n                var argType = value.GetType();\n                var listType = typeof(List<>).MakeGenericType(argType);\n                var list = Activator.CreateInstance(listType) as IList;\n                list?.Add(value);\n\n                return list;\n            }\n\n            return value;\n        }\n\n        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)\n        {\n            throw new NotSupportedException();\n        }\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.Calculator/CreateOperationInfoViewModel.cs",
    "content": "﻿using System.Windows;\n\nnamespace Nodify.Calculator\n{\n    public class CreateOperationInfoViewModel\n    {\n        public CreateOperationInfoViewModel(OperationInfoViewModel info, Point location)\n        {\n            Info = info;\n            Location = location;\n        }\n\n        public OperationInfoViewModel Info { get; }\n        public Point Location { get; }\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.Calculator/EditorView.xaml",
    "content": "﻿<UserControl x:Class=\"Nodify.Calculator.EditorView\"\n             xmlns=\"https://github.com/avaloniaui\"\n             xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n             xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\"\n             xmlns:d=\"http://schemas.microsoft.com/expression/blend/2008\"\n             xmlns:local=\"clr-namespace:Nodify.Calculator\"\n             xmlns:nodify=\"https://miroiu.github.io/nodify\"\n             xmlns:shared=\"clr-namespace:Nodify;assembly=Nodify.Shared\"\n             xmlns:sys=\"clr-namespace:System;assembly=System.Runtime\"\n             xmlns:collections=\"clr-namespace:System.Collections;assembly=System.Runtime\"\n             xmlns:compatibility=\"clr-namespace:Nodify.Compatibility;assembly=Nodify\"\n             d:DataContext=\"{d:DesignInstance Type=local:EditorViewModel}\"\n             mc:Ignorable=\"d\"\n             d:DesignHeight=\"450\"\n             d:DesignWidth=\"800\">\n    <UserControl.Resources>\n        <GeometryDrawing x:Key=\"SmallGridGeometry\"\n                         Geometry=\"M0,0 L0,1 0.03,1 0.03,0.03 1,0.03 1,0 Z\"\n                         Brush=\"{DynamicResource GridLinesBrush}\" />\n\n        <GeometryDrawing x:Key=\"LargeGridGeometry\"\n                         Geometry=\"M0,0 L0,1 0.015,1 0.015,0.015 1,0.015 1,0 Z\"\n                         Brush=\"{DynamicResource GridLinesBrush}\" />\n\n        <DrawingBrush x:Key=\"SmallGridLinesDrawingBrush\"\n                      TileMode=\"Tile\"\n                      DestinationRect=\"0 0 15 15\"\n                      TransformOrigin=\"0,0\"\n                      Transform=\"{Binding DpiScaledViewportTransform, ElementName=Editor}\"\n                      Drawing=\"{StaticResource SmallGridGeometry}\" />\n\n        <DrawingBrush x:Key=\"LargeGridLinesDrawingBrush\"\n                      TileMode=\"Tile\"\n                      Opacity=\"0.5\"\n                      DestinationRect=\"0 0 150 150\"\n                      Transform=\"{Binding DpiScaledViewportTransform, ElementName=Editor}\"\n                      Drawing=\"{StaticResource LargeGridGeometry}\" />\n\n        <LinearGradientBrush x:Key=\"BorderBrush\" \n                             StartPoint=\"0%,0%\" \n                             EndPoint=\"100%,0%\">\n            <GradientStop Color=\"#6366f1\"\n                          Offset=\"0\" />\n            <GradientStop Color=\"#a855f7\"\n                          Offset=\"0.5\" />\n            <GradientStop Color=\"#ec4899\"\n                          Offset=\"1\" />\n        </LinearGradientBrush>\n        \n        <Animation x:Key=\"BorderBrushAnimation\"\n            FillMode=\"None\"\n            IterationCount=\"Infinite\"\n            x:SetterTargetType=\"ContentControl\"\n            Duration=\"0:0:8\">\n            <KeyFrame Cue=\"0%\">\n                <Setter Property=\"BorderBrush\">\n                    <Setter.Value>\n                        <LinearGradientBrush StartPoint=\"100%,0%\" EndPoint=\"0%,100%\">\n                            <GradientStop Color=\"#6366f1\" Offset=\"0\" />\n                            <GradientStop Color=\"#a855f7\" Offset=\"0.5\" />\n                            <GradientStop Color=\"#ec4899\" Offset=\"1\" />\n                        </LinearGradientBrush>\n                    </Setter.Value>\n                </Setter>\n            </KeyFrame>\n            <KeyFrame Cue=\"25%\">\n                <Setter Property=\"BorderBrush\">\n                    <Setter.Value>\n                        <LinearGradientBrush StartPoint=\"100%,100%\" EndPoint=\"0%,0%\">\n                            <GradientStop Color=\"#6366f1\" Offset=\"0\" />\n                            <GradientStop Color=\"#a855f7\" Offset=\"0.5\" />\n                            <GradientStop Color=\"#ec4899\" Offset=\"1\" />\n                        </LinearGradientBrush>\n                    </Setter.Value>\n                </Setter>\n            </KeyFrame>\n            <KeyFrame Cue=\"50%\">\n                <Setter Property=\"BorderBrush\">\n                    <Setter.Value>\n                        <LinearGradientBrush StartPoint=\"0%,100%\" EndPoint=\"100%,0%\">\n                            <GradientStop Color=\"#6366f1\" Offset=\"0\" />\n                            <GradientStop Color=\"#a855f7\" Offset=\"0.5\" />\n                            <GradientStop Color=\"#ec4899\" Offset=\"1\" />\n                        </LinearGradientBrush>\n                    </Setter.Value>\n                </Setter>\n            </KeyFrame>\n            <KeyFrame Cue=\"75%\">\n                <Setter Property=\"BorderBrush\">\n                    <Setter.Value>\n                        <LinearGradientBrush StartPoint=\"0%,0%\" EndPoint=\"100%,100%\">\n                            <GradientStop Color=\"#6366f1\" Offset=\"0\" />\n                            <GradientStop Color=\"#a855f7\" Offset=\"0.5\" />\n                            <GradientStop Color=\"#ec4899\" Offset=\"1\" />\n                        </LinearGradientBrush>\n                    </Setter.Value>\n                </Setter>\n            </KeyFrame>\n            <KeyFrame Cue=\"100%\">\n                <Setter Property=\"BorderBrush\">\n                    <Setter.Value>\n                        <LinearGradientBrush StartPoint=\"100%,0%\" EndPoint=\"0%,100%\">\n                            <GradientStop Color=\"#6366f1\" Offset=\"0\" />\n                            <GradientStop Color=\"#a855f7\" Offset=\"0.5\" />\n                            <GradientStop Color=\"#ec4899\" Offset=\"1\" />\n                        </LinearGradientBrush>\n                    </Setter.Value>\n                </Setter>\n            </KeyFrame>\n        </Animation>\n\n        <local:ItemToListConverter x:Key=\"ItemToListConverter\" />\n\n        <DataTemplate x:Key=\"ConnectionTemplate\"\n                      DataType=\"{x:Type local:ConnectionViewModel}\">\n            <nodify:CircuitConnection Source=\"{Binding Output.Anchor}\"\n                                      Target=\"{Binding Input.Anchor}\" />\n        </DataTemplate>\n\n        <DataTemplate x:Key=\"PendingConnectionTemplate\"\n                      DataType=\"{x:Type local:PendingConnectionViewModel}\">\n            <nodify:PendingConnection IsVisible=\"{Binding IsVisible}\"\n                                      Source=\"{Binding Source, Mode=OneWayToSource}\"\n                                      Target=\"{Binding Target, Mode=OneWayToSource}\"\n                                      TargetAnchor=\"{Binding TargetLocation, Mode=OneWayToSource}\"\n                                      StartedCommand=\"{Binding DataContext.StartConnectionCommand, RelativeSource={RelativeSource AncestorType={x:Type nodify:NodifyEditor}}}\"\n                                      CompletedCommand=\"{Binding DataContext.CreateConnectionCommand, RelativeSource={RelativeSource AncestorType={x:Type nodify:NodifyEditor}}}\" />\n        </DataTemplate>\n        \n        <ControlTheme x:Key=\"ItemContainerStyle\"\n               TargetType=\"{x:Type nodify:ItemContainer}\"\n               BasedOn=\"{StaticResource {x:Type nodify:ItemContainer}}\">\n            <Setter Property=\"Location\"\n                    Value=\"{Binding Location}\" />\n            <Setter Property=\"IsSelected\"\n                    Value=\"{Binding IsSelected}\" />\n            <Setter Property=\"ActualSize\"\n                    Value=\"{Binding Size, Mode=OneWayToSource}\" />\n            <Setter Property=\"BorderBrush\"\n                    Value=\"{StaticResource BorderBrush}\" />\n            <Setter Property=\"BorderThickness\"\n                    Value=\"2\" />\n        </ControlTheme>\n    </UserControl.Resources>\n\n    <Grid>\n        <nodify:NodifyEditor DataContext=\"{Binding Calculator}\"\n                             ItemsSource=\"{Binding Operations}\"\n                             Connections=\"{Binding Connections}\"\n                             SelectedItems=\"{Binding SelectedOperations}\"\n                             DisconnectConnectorCommand=\"{Binding DisconnectConnectorCommand}\"\n                             PendingConnection=\"{Binding PendingConnection}\"\n                             PendingConnectionTemplate=\"{StaticResource PendingConnectionTemplate}\"\n                             ConnectionTemplate=\"{StaticResource ConnectionTemplate}\"\n                             Background=\"{StaticResource SmallGridLinesDrawingBrush}\"\n                             ItemContainerTheme=\"{StaticResource ItemContainerStyle}\"\n                             GridCellSize=\"15\"\n                             DragDrop.AllowDrop=\"True\"\n                             x:Name=\"Editor\">\n            <nodify:NodifyEditor.Resources>\n                <ControlTheme TargetType=\"{x:Type nodify:NodeInput}\" x:Key=\"{x:Type nodify:NodeInput}\"\n                       BasedOn=\"{StaticResource {x:Type nodify:NodeInput}}\">\n                    <Setter Property=\"Header\"\n                            Value=\"{Binding}\" />\n                    <Setter Property=\"IsConnected\"\n                            Value=\"{Binding IsConnected}\" />\n                    <Setter Property=\"Anchor\"\n                            Value=\"{Binding Anchor, Mode=OneWayToSource}\" />\n                    <Setter Property=\"(ToolTip.Tip)\"\n                            Value=\"{Binding Value}\" />\n                    <Setter Property=\"HeaderTemplate\">\n                        <Setter.Value>\n                            <DataTemplate DataType=\"{x:Type local:ConnectorViewModel}\">\n                                <StackPanel Orientation=\"Horizontal\">\n                                    <TextBlock Text=\"{Binding Title}\"\n                                               VerticalAlignment=\"Center\"\n                                               Margin=\"0 0 5 0\" />\n                                    <TextBox Text=\"{Binding Value}\"\n                                             IsVisible=\"{Binding !IsConnected}\" />\n                                </StackPanel>\n                            </DataTemplate>\n                        </Setter.Value>\n                    </Setter>\n                </ControlTheme>\n\n                <ControlTheme TargetType=\"{x:Type nodify:NodeOutput}\" x:Key=\"{x:Type nodify:NodeOutput}\"\n                       BasedOn=\"{StaticResource {x:Type nodify:NodeOutput}}\">\n                    <Setter Property=\"Header\"\n                            Value=\"{Binding}\" />\n                    <Setter Property=\"IsConnected\"\n                            Value=\"{Binding IsConnected}\" />\n                    <Setter Property=\"Anchor\"\n                            Value=\"{Binding Anchor, Mode=OneWayToSource}\" />\n                    <Setter Property=\"HeaderTemplate\">\n                        <Setter.Value>\n                            <DataTemplate DataType=\"{x:Type local:ConnectorViewModel}\">\n                                <TextBox Text=\"{Binding Value}\"\n                                         IsEnabled=\"False\" />\n                            </DataTemplate>\n                        </Setter.Value>\n                    </Setter>\n                </ControlTheme>\n            </nodify:NodifyEditor.Resources>\n\n            <nodify:NodifyEditor.DataTemplates>\n                <DataTemplate DataType=\"{x:Type local:OperationGraphViewModel}\">\n                    <nodify:GroupingNode Header=\"{Binding}\"\n                                         CanResize=\"{Binding IsExpanded}\"\n                                         ActualSize=\"{Binding DesiredSize, Mode=TwoWay}\"\n                                         MovementMode=\"Self\">\n                        <nodify:GroupingNode.HeaderTemplate>\n                            <DataTemplate DataType=\"{x:Type local:OperationGraphViewModel}\">\n                                <Grid>\n                                    <Grid.ColumnDefinitions>\n                                        <ColumnDefinition Width=\"*\" />\n                                        <ColumnDefinition Width=\"Auto\" />\n                                    </Grid.ColumnDefinitions>\n\n                                    <TextBlock Text=\"{Binding Title}\" />\n                                    <StackPanel Orientation=\"Horizontal\"\n                                                Margin=\"5 0 0 0\"\n                                                Grid.Column=\"1\">\n                                        <TextBlock Text=\"Expand?\"\n                                                   IsVisible=\"{Binding IsExpanded}\"\n                                                   Margin=\"0 0 5 0\" />\n                                        <CheckBox IsChecked=\"{Binding IsExpanded}\" />\n                                    </StackPanel>\n                                </Grid>\n                            </DataTemplate>\n                        </nodify:GroupingNode.HeaderTemplate>\n                        <Grid>\n                            <ScrollViewer \n                                HorizontalScrollBarVisibility=\"Auto\"\n                                VerticalScrollBarVisibility=\"Auto\">\n                                <nodify:NodifyEditor Tag=\"{Binding DataContext, RelativeSource={RelativeSource Self}}\"\n                                                     DataContext=\"{Binding InnerCalculator}\"\n                                                     ItemsSource=\"{Binding Operations}\"\n                                                     Connections=\"{Binding Connections}\"\n                                                     SelectedItems=\"{Binding SelectedOperations}\"\n                                                     DisconnectConnectorCommand=\"{Binding DisconnectConnectorCommand}\"\n                                                     PendingConnection=\"{Binding PendingConnection}\"\n                                                     PendingConnectionTemplate=\"{StaticResource PendingConnectionTemplate}\"\n                                                     ConnectionTemplate=\"{StaticResource ConnectionTemplate}\"\n                                                     ItemContainerTheme=\"{StaticResource ItemContainerStyle}\"\n                                                     Background=\"Transparent\"\n                                                     GridCellSize=\"15\"\n                                                     DragDrop.AllowDrop=\"True\"\n                                                     IsVisible=\"{Binding DataContext.IsExpanded, RelativeSource={RelativeSource AncestorType=nodify:GroupingNode}, Converter={shared:BooleanToVisibilityConverter}}\">\n\n                                    <nodify:NodifyEditor.KeyBindings>\n                                        <KeyBinding Gesture=\"Delete\"\n                                                    Command=\"{Binding DeleteSelectionCommand}\" />\n                                        <KeyBinding Gesture=\"C\"\n                                                    Command=\"{Binding GroupSelectionCommand}\" />\n                                    </nodify:NodifyEditor.KeyBindings>\n\n                                    <nodify:NodifyEditor.Decorators>\n                                        <collections:ArrayList>\n                                            <nodify:DecoratorContainer DataContext=\"{Binding OperationsMenu}\"\n                                                                       Location=\"{Binding Location}\">\n                                                <local:OperationsMenuView />\n                                            </nodify:DecoratorContainer>\n                                        </collections:ArrayList>\n                                    </nodify:NodifyEditor.Decorators>\n                                </nodify:NodifyEditor>\n                            </ScrollViewer>\n                            \n                            <Grid>\n                                <Grid.ColumnDefinitions>\n                                    <ColumnDefinition Width=\"Auto\" />\n                                    <ColumnDefinition Width=\"*\" />\n                                    <ColumnDefinition Width=\"Auto\" />\n                                </Grid.ColumnDefinitions>\n\n                                <ItemsControl ItemsSource=\"{Binding Input}\">\n                                    <ItemsControl.ItemTemplate>\n                                        <DataTemplate>\n                                            <nodify:NodeInput />\n                                        </DataTemplate>\n                                    </ItemsControl.ItemTemplate>\n                                </ItemsControl>\n\n                                <nodify:NodeOutput DataContext=\"{Binding Output}\"\n                                                   Grid.Column=\"1\"\n                                                   VerticalAlignment=\"Top\"\n                                                   HorizontalAlignment=\"Right\" />\n                            </Grid>\n                        </Grid>\n                    </nodify:GroupingNode>\n                </DataTemplate>\n                \n                <DataTemplate DataType=\"{x:Type local:ExpandoOperationViewModel}\">\n                    <nodify:Node Header=\"{Binding Title}\"\n                                 Content=\"{Binding}\"\n                                 Input=\"{Binding Input}\"\n                                 Output=\"{Binding Output, Converter={StaticResource ItemToListConverter}}\">\n                        <nodify:Node.ContentTemplate>\n                            <DataTemplate DataType=\"{x:Type local:ExpandoOperationViewModel}\">\n                                <StackPanel>\n                                    <Button Theme=\"{StaticResource IconButton}\"\n                                            Content=\"{StaticResource PlusIcon}\"\n                                            Command=\"{Binding AddInputCommand}\" />\n                                    <Button Theme=\"{StaticResource IconButton}\"\n                                            Content=\"{StaticResource RemoveKeyIcon}\"\n                                            Command=\"{Binding RemoveInputCommand}\" />\n                                </StackPanel>\n                            </DataTemplate>\n                        </nodify:Node.ContentTemplate>\n                    </nodify:Node>\n                </DataTemplate>\n\n                <DataTemplate DataType=\"{x:Type local:ExpressionOperationViewModel}\">\n                    <nodify:Node Content=\"{Binding}\"\n                                 Input=\"{Binding Input}\"\n                                 Output=\"{Binding Output, Converter={StaticResource ItemToListConverter}}\">\n                        <nodify:Node.ContentTemplate>\n                            <DataTemplate DataType=\"{x:Type local:ExpressionOperationViewModel}\">\n                                <TextBox Text=\"{Binding Expression}\"\n                                         MinWidth=\"100\"\n                                         Margin=\"5 0 0 0\" />\n                            </DataTemplate>\n                        </nodify:Node.ContentTemplate>\n                    </nodify:Node>\n                </DataTemplate>\n\n                <DataTemplate DataType=\"{x:Type local:CalculatorOperationViewModel}\">\n                    <nodify:Node Header=\"{Binding Title}\"\n                                 Input=\"{Binding Input}\"\n                                 Output=\"{Binding Output, Converter={StaticResource ItemToListConverter}}\"\n                                 ToolTip.Tip=\"Double click to expand\">\n                        <Interaction.Behaviors>\n                            <RoutedEventTriggerBehavior RoutedEvent=\"{x:Static InputElement.DoubleTappedEvent}\">\n                                <InvokeCommandAction Command=\"{Binding DataContext.OpenCalculatorCommand, RelativeSource={RelativeSource AncestorType=UserControl}}\"\n                                                     CommandParameter=\"{Binding InnerCalculator}\" />\n                            </RoutedEventTriggerBehavior>\n                        </Interaction.Behaviors>\n                    </nodify:Node>\n                </DataTemplate>\n\n                <DataTemplate DataType=\"{x:Type local:CalculatorInputOperationViewModel}\">\n                    <nodify:Node Header=\"{Binding Title}\"\n                                 Output=\"{Binding Output}\">\n                        <StackPanel>\n                            <Button Theme=\"{StaticResource IconButton}\"\n                                    Content=\"{StaticResource PlusIcon}\"\n                                    Command=\"{Binding AddOutputCommand}\" />\n                            <Button Theme=\"{StaticResource IconButton}\"\n                                    Content=\"{StaticResource RemoveKeyIcon}\"\n                                    Command=\"{Binding RemoveOutputCommand}\" />\n                        </StackPanel>\n                        \n                        <nodify:Node.Resources>\n                            <ControlTheme TargetType=\"{x:Type nodify:NodeOutput}\" x:Key=\"{x:Type nodify:NodeOutput}\"\n                                          BasedOn=\"{StaticResource {x:Type nodify:NodeOutput}}\">\n                                <Setter Property=\"Header\"\n                                        Value=\"{Binding}\" />\n                                <Setter Property=\"IsConnected\"\n                                        Value=\"{Binding IsConnected}\" />\n                                <Setter Property=\"Anchor\"\n                                        Value=\"{Binding Anchor, Mode=OneWayToSource}\" />\n                                <Setter Property=\"HeaderTemplate\">\n                                    <Setter.Value>\n                                        <DataTemplate DataType=\"{x:Type local:ConnectorViewModel}\">\n                                            <StackPanel Orientation=\"Horizontal\">\n                                                <TextBox Text=\"{Binding Value}\"\n                                                         IsEnabled=\"False\" />\n                                                <TextBlock Text=\"{Binding Title}\"\n                                                           Margin=\"5 0 0 0\" />\n                                            </StackPanel>\n                                        </DataTemplate>\n                                    </Setter.Value>\n                                </Setter>\n                            </ControlTheme>\n                        </nodify:Node.Resources>\n                    </nodify:Node>\n                </DataTemplate>\n\n                <DataTemplate DataType=\"{x:Type local:OperationGroupViewModel}\">\n                    <nodify:GroupingNode Header=\"{Binding Title}\"\n                                         ActualSize=\"{Binding GroupSize, Mode=TwoWay}\" />\n                </DataTemplate>\n\n                <DataTemplate DataType=\"{x:Type local:OperationViewModel}\">\n                    <nodify:Node Content=\"{Binding Title}\"\n                                 Input=\"{Binding Input}\"\n                                 Output=\"{Binding Output, Converter={StaticResource ItemToListConverter}}\" />\n                </DataTemplate>\n            </nodify:NodifyEditor.DataTemplates>\n            \n            <nodify:NodifyEditor.KeyBindings>\n                <KeyBinding Gesture=\"Delete\"\n                            Command=\"{Binding DeleteSelectionCommand}\" />\n                <KeyBinding Gesture=\"C\"\n                            Command=\"{Binding GroupSelectionCommand}\" />\n            </nodify:NodifyEditor.KeyBindings>\n            \n            <nodify:NodifyEditor.Decorators>\n                <collections:ArrayList>\n                    <nodify:DecoratorContainer DataContext=\"{Binding OperationsMenu}\"\n                                               Location=\"{Binding Location}\">\n                        <local:OperationsMenuView />\n                    </nodify:DecoratorContainer>\n                </collections:ArrayList>\n            </nodify:NodifyEditor.Decorators>\n        </nodify:NodifyEditor>\n\n        <Grid Background=\"{StaticResource LargeGridLinesDrawingBrush}\"\n              Panel.ZIndex=\"-2\" />\n\n        <Border HorizontalAlignment=\"Right\"\n                MinWidth=\"200\"\n                MaxWidth=\"300\"\n                Padding=\"7\"\n                Margin=\"10\"\n                CornerRadius=\"3\"\n                BorderThickness=\"2\">\n            <Border.Background>\n                <SolidColorBrush Color=\"{DynamicResource BackgroundColor}\"\n                                 Opacity=\"0.7\" />\n            </Border.Background>\n            <ScrollViewer>\n                <ItemsControl ItemsSource=\"{Binding Calculator.OperationsMenu.AvailableOperations}\">\n                    <ItemsControl.ItemContainerTheme>\n                        <ControlTheme TargetType=\"{x:Type ContentPresenter}\">\n                            <Setter Property=\"Control.Margin\" \n                                    Value=\"5\"/>\n                            <Setter Property=\"Control.HorizontalAlignment\" \n                                    Value=\"Left\"/>\n                            <Setter Property=\"Control.Cursor\" \n                                    Value=\"Hand\"/>\n                            <Setter Property=\"(ToolTip.Tip)\" \n                                    Value=\"Drag and drop into the editor\"/>\n                        </ControlTheme>\n                    </ItemsControl.ItemContainerTheme>\n                    <ItemsControl.ItemTemplate>\n                        <DataTemplate DataType=\"{x:Type local:OperationInfoViewModel}\">\n                            <nodify:Node Content=\"{Binding Title}\"\n                                         Input=\"{Binding Input}\"\n                                         BorderBrush=\"{StaticResource BorderBrush}\"\n                                         BorderThickness=\"2\"\n                                         PointerPressed=\"OnNodePressed\"\n                                         PointerExited=\"OnNodeExited\"\n                                         PointerMoved=\"OnNodeDrag\">\n                                <nodify:Node.Output>\n                                    <collections:ArrayList>\n                                        <sys:String>Output</sys:String>\n                                    </collections:ArrayList>\n                                </nodify:Node.Output>\n                                <!-- <nodify:Node.Styles> -->\n                                <!--     <Style Selector=\"nodify|Node\"> -->\n                                <!--         <Style.Animations> -->\n                                <!--             <StaticResource ResourceKey=\"BorderBrushAnimation\" /> -->\n                                <!--         </Style.Animations> -->\n                                <!--     </Style> -->\n                                <!-- </nodify:Node.Styles> -->\n                            </nodify:Node>\n                        </DataTemplate>\n                    </ItemsControl.ItemTemplate>\n                </ItemsControl>\n            </ScrollViewer>\n        </Border>\n    </Grid>\n</UserControl>\n"
  },
  {
    "path": "Examples/Nodify.Calculator/EditorView.xaml.cs",
    "content": "﻿using System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Input;\n\nnamespace Nodify.Calculator\n{\n    public partial class EditorView : UserControl\n    {\n        public EditorView()\n        {\n            InitializeComponent();\n\n            PointerPressedEvent.AddClassHandler<NodifyEditor>(CloseOperationsMenuPointerPressed);\n            ItemContainer.DragStartedEvent.AddClassHandler<ItemContainer>(CloseOperationsMenu);\n            PointerReleasedEvent.AddClassHandler<NodifyEditor>(OpenOperationsMenu);\n            Editor.AddHandler(DragDrop.DropEvent, OnDropNode);\n        }\n        \n        private void OpenOperationsMenu(object? sender, PointerReleasedEventArgs e)\n        {\n            if (!e.Handled && e.Source is NodifyEditor editor && !editor.IsPanning && editor.DataContext is CalculatorViewModel calculator &&\n                e.InitialPressMouseButton == MouseButton.Right)\n            {\n                e.Handled = true;\n                calculator.OperationsMenu.OpenAt(editor.MouseLocation);\n            }\n        }\n\n        private void CloseOperationsMenuPointerPressed(object? sender, PointerPressedEventArgs e)\n        {\n            if (e.GetCurrentPoint(this).Properties.PointerUpdateKind == PointerUpdateKind.LeftButtonPressed)\n                CloseOperationsMenu(sender, e);\n        }\n        \n        private void CloseOperationsMenu(object? sender, RoutedEventArgs e)\n        {\n            ItemContainer? itemContainer = sender as ItemContainer;\n            NodifyEditor? editor = sender as NodifyEditor ?? itemContainer?.Editor;\n\n            if (!e.Handled && editor?.DataContext is CalculatorViewModel calculator)\n            {\n                calculator.OperationsMenu.Close();\n            }\n        }\n\n        private void OnDropNode(object? sender, DragEventArgs e)\n        {\n            NodifyEditor? editor = (e.Source as NodifyEditor) ?? (e.Source as Control)?.GetLogicalParent() as NodifyEditor;\n            if(editor != null && editor.DataContext is CalculatorViewModel calculator\n                && e.Data.Get(typeof(OperationInfoViewModel).FullName) is OperationInfoViewModel operation)\n            {\n                OperationViewModel op = OperationFactory.GetOperation(operation);\n                op.Location = editor.GetLocationInsideEditor(e);\n                calculator.Operations.Add(op);\n\n                e.Handled = true;\n            }\n        }\n        \n        private void OnNodeDrag(object? sender, MouseEventArgs e)\n        {\n            if(leftButtonPressed && ((Control)sender).DataContext is OperationInfoViewModel operation)\n            {\n                var data = new DataObject();\n                data.Set(typeof(OperationInfoViewModel).FullName, operation);\n                DragDrop.DoDragDrop(e, data, DragDropEffects.Copy);\n            }\n        }\n\n        private void OnNodePressed(object? sender, PointerPressedEventArgs e)\n        {\n            leftButtonPressed = e.GetCurrentPoint(this).Properties.PointerUpdateKind ==\n                                PointerUpdateKind.LeftButtonPressed;\n        }\n\n        private void OnNodeExited(object? sender, PointerEventArgs e)\n        {\n            leftButtonPressed = false;\n        }\n        \n        private bool leftButtonPressed;\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.Calculator/EditorViewModel.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Windows.Input;\n\nnamespace Nodify.Calculator\n{\n    public class EditorViewModel : ObservableObject\n    {\n        public event Action<EditorViewModel, CalculatorViewModel>? OnOpenInnerCalculator;\n\n        public EditorViewModel? Parent { get; set; }\n\n        public EditorViewModel()\n        {\n            Calculator = new CalculatorViewModel();\n            OpenCalculatorCommand = new DelegateCommand<CalculatorViewModel>(calculator =>\n            {\n                OnOpenInnerCalculator?.Invoke(this, calculator);\n            });\n        }\n\n        public INodifyCommand OpenCalculatorCommand { get; }\n\n        public Guid Id { get; } = Guid.NewGuid();\n\n        private CalculatorViewModel _calculator = default!;\n        public CalculatorViewModel Calculator \n        {\n            get => _calculator;\n            set => SetProperty(ref _calculator, value);\n        }\n\n        private string? _name;\n        public string? Name\n        {\n            get => _name;\n            set => SetProperty(ref _name, value);\n        }\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.Calculator/ExpandoOperationViewModel.cs",
    "content": "﻿namespace Nodify.Calculator\n{\n    public class ExpandoOperationViewModel : OperationViewModel\n    {\n        public ExpandoOperationViewModel()\n        {\n            AddInputCommand = new RequeryCommand(\n                () => Input.Add(new ConnectorViewModel()),\n                () => Input.Count < MaxInput);\n\n            RemoveInputCommand = new RequeryCommand(\n                () => Input.RemoveAt(Input.Count - 1),\n                () => Input.Count > MinInput);\n\n            Input.WhenAdded(_ => AddInputCommand.RaiseCanExecuteChanged());\n            Input.WhenRemoved(_ => AddInputCommand.RaiseCanExecuteChanged());\n            Input.WhenAdded(_ => RemoveInputCommand.RaiseCanExecuteChanged());\n            Input.WhenRemoved(_ => RemoveInputCommand.RaiseCanExecuteChanged());\n        }\n\n        public INodifyCommand AddInputCommand { get; }\n        public INodifyCommand RemoveInputCommand { get; }\n\n        private uint _minInput = 0;\n        public uint MinInput\n        {\n            get => _minInput;\n            set => SetProperty(ref _minInput, value);\n        }\n\n        private uint _maxInput = uint.MaxValue;\n        public uint MaxInput\n        {\n            get => _maxInput;\n            set => SetProperty(ref _maxInput, value);\n        }\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.Calculator/ExpressionOperationViewModel.cs",
    "content": "﻿using StringMath;\nusing System.Collections.Generic;\nusing System.Linq;\n\nnamespace Nodify.Calculator\n{\n    public class ExpressionOperationViewModel : OperationViewModel\n    {\n        private MathExpr? _expr;\n        private string? _expression;\n        public string? Expression\n        {\n            get => _expression;\n            set => SetProperty(ref _expression, value)\n                .Then(GenerateInput);\n        }\n\n        private void GenerateInput()\n        {\n            try\n            {\n                _expr = Expression!.ToMathExpr();\n                ConnectorViewModel[]? toRemove = Input.Where(i => !_expr.LocalVariables.Contains(i.Title)).ToArray();\n                toRemove.ForEach(i => Input.Remove(i));\n                HashSet<string> existingVars = Input.Select(s => s.Title).Where(s => s != null).ToHashSet()!;\n\n                foreach (string variable in _expr.LocalVariables.Except(existingVars))\n                {\n                    Input.Add(new ConnectorViewModel\n                    {\n                        Title = variable\n                    });\n                }\n\n                OnInputValueChanged();\n            }\n            catch\n            {\n\n            }\n        }\n\n        protected override void OnInputValueChanged()\n        {\n            if (Output != null && _expr != null)\n            {\n                try\n                {\n                    Input.ForEach(i => _expr.Substitute(i.Title!, i.Value));\n                    Output.Value = _expr.Result;\n                }\n                catch\n                {\n\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.Calculator/GlobalUsings.cs",
    "content": "global using Avalonia.Controls;\nglobal using Avalonia;\nglobal using Avalonia.Data.Converters;\nglobal using Avalonia.Markup.Xaml;\nglobal using System.Windows.Input;\nglobal using Avalonia.Interactivity;\nglobal using Avalonia.Controls.ApplicationLifetimes;\nglobal using Avalonia.Input;\nglobal using Avalonia.LogicalTree;\nglobal using MouseEventArgs = Avalonia.Input.PointerEventArgs;"
  },
  {
    "path": "Examples/Nodify.Calculator/MainWindow.xaml",
    "content": "﻿<Window x:Class=\"Nodify.Calculator.MainWindow\"\n        xmlns=\"https://github.com/avaloniaui\"\n        xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n        xmlns:d=\"http://schemas.microsoft.com/expression/blend/2008\"\n        xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\"\n        xmlns:local=\"clr-namespace:Nodify.Calculator\"\n        xmlns:shared=\"clr-namespace:Nodify;assembly=Nodify.Shared\"\n        Background=\"{DynamicResource NodifyEditor.BackgroundBrush}\"\n        Foreground=\"{DynamicResource ForegroundBrush}\"\n        mc:Ignorable=\"d\"\n        Title=\"MainWindow\"\n        Height=\"650\"\n        Width=\"1200\">\n    <Window.DataContext>\n        <local:ApplicationViewModel />\n    </Window.DataContext>\n    \n    <Window.KeyBindings>\n        <KeyBinding Gesture=\"Ctrl+T\"\n                    Command=\"{Binding Source={x:Static shared:ThemeManager.SetNextThemeCommand}}\" />\n        <KeyBinding Gesture=\"Ctrl+N\"\n                    Command=\"{Binding AddEditorCommand}\" />\n        <KeyBinding Gesture=\"Ctrl+W\"\n                    Command=\"{Binding CloseEditorCommand}\"\n                    CommandParameter=\"{Binding SelectedEditor.Id}\"/>\n    </Window.KeyBindings>\n    \n    <Window.Resources>\n        <shared:BindingProxy x:Key=\"Proxy\" \n                             DataContext=\"{Binding}\"/>\n    </Window.Resources>\n    \n    <Window.DataTemplates>\n        <DataTemplate DataType=\"{x:Type local:EditorViewModel}\">\n            <local:EditorView/>\n        </DataTemplate>\n    </Window.DataTemplates>\n    \n    <Grid>\n        <shared:TabControlEx ItemsSource=\"{Binding Editors}\"\n                             SelectedItem=\"{Binding SelectedEditor}\" \n                             AddTabCommand=\"{Binding AddEditorCommand}\"\n                             AutoScrollToEnd=\"{Binding AutoSelectNewEditor}\">\n            <shared:TabControlEx.ItemContainerTheme>\n                <ControlTheme TargetType=\"{x:Type shared:TabItemEx}\" \n                       BasedOn=\"{StaticResource {x:Type shared:TabItemEx}}\">\n                    <Setter Property=\"Header\" \n                            Value=\"{Binding Name}\"/>\n                    <Setter Property=\"CloseTabCommand\" \n                            Value=\"{Binding DataContext.CloseEditorCommand ,Source={StaticResource Proxy}}\"/>\n                    <Setter Property=\"CloseTabCommandParameter\" \n                            Value=\"{Binding Id}\"/>\n                </ControlTheme>\n            </shared:TabControlEx.ItemContainerTheme>\n        </shared:TabControlEx>\n\n        <Expander Header=\"Click to hide/show\"\n                  IsExpanded=\"True\"\n                  Margin=\"10\"\n                  Padding=\"0\"\n                  HorizontalAlignment=\"Left\"\n                  VerticalAlignment=\"Bottom\">\n            <Border MaxWidth=\"325\"\n                    MaxHeight=\"300\"\n                    CornerRadius=\"3\">\n                <Border.Background>\n                    <SolidColorBrush Color=\"{DynamicResource BackgroundColor}\"\n                                     Opacity=\"0.7\" />\n                </Border.Background>\n                <ScrollViewer HorizontalScrollBarVisibility=\"Disabled\">\n                    <StackPanel Margin=\"10\"\n                                IsHitTestVisible=\"False\">\n                        <StackPanel.Resources>\n                            <ControlTheme TargetType=\"{x:Type TextBlock}\" x:Key=\"{x:Type TextBlock}\">\n                                <Setter Property=\"Margin\"\n                                        Value=\"0 0 0 5\" />\n                            </ControlTheme>\n                        </StackPanel.Resources>\n\n                        <StackPanel Margin=\"0 0 0 20\">\n                            <TextBlock Text=\"(New) Drag and drop nodes from the toolbox\"\n                                   TextWrapping=\"Wrap\"\n                                   Foreground=\"{DynamicResource NodeInput.BorderBrush}\"\n                                   FontWeight=\"Bold\"/>\n                        </StackPanel>\n                        <TextBlock TextWrapping=\"Wrap\">\n                            <Run Foreground=\"Red\"\n                                    FontWeight=\"Bold\">CTRL + N/W</Run>\n                            <Run>: open/close editor</Run>\n                        </TextBlock>\n                        <TextBlock TextWrapping=\"Wrap\">\n                            <Run Foreground=\"Red\"\n                                    FontWeight=\"Bold\">ALT + Click</Run>\n                            <Run>: disconnect connector</Run>\n                        </TextBlock>\n                        <TextBlock TextWrapping=\"Wrap\">\n                            <Run Foreground=\"Red\"\n                                    FontWeight=\"Bold\">Right Click</Run>\n                            <Run>: show operations menu (create nodes)</Run>\n                        </TextBlock>\n                        <TextBlock TextWrapping=\"Wrap\">\n                            <Run Foreground=\"Red\"\n                                    FontWeight=\"Bold\">Delete</Run>\n                            <Run>:  delete selection</Run>\n                        </TextBlock>\n                        <TextBlock TextWrapping=\"Wrap\">\n                            <Run Foreground=\"Red\"\n                                    FontWeight=\"Bold\">CTRL + T</Run>\n                            <Run>: change theme</Run>\n                        </TextBlock>\n                        <TextBlock TextWrapping=\"Wrap\">\n                            <Run Foreground=\"Red\"\n                                    FontWeight=\"Bold\">C</Run>\n                            <Run>: group selection (hold SHIFT and mouse drag the header to move the group node alone)</Run>\n                        </TextBlock>\n                        <TextBlock Text=\"Drag a connection and drop it on the editor\"\n                                TextWrapping=\"Wrap\"\n                                FontWeight=\"Bold\" />\n                        <TextBlock Text=\"Hover over a connector to see its value\"\n                                   TextWrapping=\"Wrap\"\n                                   FontWeight=\"Bold\" />\n                        <TextBlock Text=\"Create a Calculator node and double click it to open\"\n                                   TextWrapping=\"Wrap\"\n                                   FontWeight=\"Bold\" />\n                        <TextBlock Text=\"Create an Operation Graph and add operations to it\"\n                                   TextWrapping=\"Wrap\"\n                                   FontWeight=\"Bold\" />\n                    </StackPanel>\n                </ScrollViewer>\n            </Border>\n        </Expander>\n    </Grid>\n</Window>\n"
  },
  {
    "path": "Examples/Nodify.Calculator/MainWindow.xaml.cs",
    "content": "﻿using System.Windows;\n\nnamespace Nodify.Calculator\n{\n    public partial class MainWindow : Window\n    {\n        public MainWindow()\n        {\n            InitializeComponent();\n\n            EditorGestures.Mappings.Editor.Cutting.Value = MultiGesture.None;\n        }\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.Calculator/Nodify.Calculator.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <OutputType>WinExe</OutputType>\n    <TargetFrameworks>net9</TargetFrameworks>\n    <Nullable>enable</Nullable>\n    <AssemblyOriginatorKeyFile>..\\..\\build\\Nodify.snk</AssemblyOriginatorKeyFile>\n    <SignAssembly>true</SignAssembly>\n  </PropertyGroup>\n  <ItemGroup>\n    <PackageReference Include=\"StringMath\" Version=\"4.1.2\" />\n  </ItemGroup>\n\n  <ItemGroup>\n    <ProjectReference Include=\"..\\..\\Nodify\\Nodify.csproj\" />\n    <ProjectReference Include=\"..\\Nodify.Shared\\Nodify.Shared.csproj\" />\n  </ItemGroup>\n\n  <ItemGroup>\n    <PackageReference Include=\"Avalonia\" Version=\"$(AvaloniaVersion)\"/>\n    <PackageReference Include=\"Avalonia.Desktop\" Version=\"$(AvaloniaVersion)\"/>\n    <PackageReference Include=\"Avalonia.Themes.Fluent\" Version=\"$(AvaloniaVersion)\"/>\n    <PackageReference Include=\"Avalonia.Fonts.Inter\" Version=\"$(AvaloniaVersion)\"/>\n    <PackageReference Include=\"Avalonia.Xaml.Behaviors\" Version=\"11.1.0.4\"/>\n    <!--Condition below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.-->\n    <PackageReference Condition=\"'$(Configuration)' == 'Debug'\" Include=\"Avalonia.Diagnostics\" Version=\"$(AvaloniaVersion)\"/>\n  </ItemGroup>\n\n  <ItemGroup>\n    <AdditionalFiles SourceItemGroup=\"AvaloniaXaml\" Include=\"**/*.xaml\"/>\n    <AvaloniaResource Include=\"**/*.xaml\" />\n  </ItemGroup>\n</Project>\n"
  },
  {
    "path": "Examples/Nodify.Calculator/OperationGraphViewModel.cs",
    "content": "using System.Windows;\n\nnamespace Nodify.Calculator\n{\n    public class OperationGraphViewModel : CalculatorOperationViewModel\n    {\n        private Size _size;\n        public Size DesiredSize\n        {\n            get => _size;\n            set => SetProperty(ref _size, value);\n        }\n        \n        private Size _prevSize;\n\n        private bool _isExpanded = true;\n        public bool IsExpanded\n        {\n            get => _isExpanded;\n            set\n            {\n                if (SetProperty(ref _isExpanded, value))\n                {\n                    if (_isExpanded)\n                    {\n                        DesiredSize = _prevSize;\n                    }\n                    else\n                    {\n                        _prevSize = Size;\n                        // Fit content\n                        DesiredSize = new Size(0,0);\n                    }\n                }\n            }\n        }\n\n        public OperationGraphViewModel()\n        {\n            InnerCalculator.Operations[0].Location = new Point(50, 50);\n            InnerCalculator.Operations[1].Location = new Point(200, 50);\n        }\n    }\n}"
  },
  {
    "path": "Examples/Nodify.Calculator/OperationGroupViewModel.cs",
    "content": "using System.Windows;\n\nnamespace Nodify.Calculator\n{\n    public class OperationGroupViewModel : OperationViewModel\n    {\n        private Size _size;\n        public Size GroupSize\n        {\n            get => _size;\n            set => SetProperty(ref _size, value);\n        }\n    }\n}"
  },
  {
    "path": "Examples/Nodify.Calculator/OperationInfoViewModel.cs",
    "content": "﻿using System.Collections.Generic;\n\nnamespace Nodify.Calculator\n{\n    public enum OperationType\n    {\n        Normal,\n        Expando,\n        Expression,\n        Calculator,\n        Group,\n        Graph\n    }\n\n    public class OperationInfoViewModel\n    {\n        public string? Title { get; set; }\n        public OperationType Type { get; set; }\n        public IOperation? Operation { get; set; }\n        public List<string?> Input { get; } = new List<string?>();\n        public uint MinInput { get; set; }\n        public uint MaxInput { get; set; }\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.Calculator/OperationViewModel.cs",
    "content": "﻿using System.ComponentModel;\nusing System.Linq;\nusing System.Windows;\n\nnamespace Nodify.Calculator\n{\n    public class OperationViewModel : ObservableObject\n    {\n        public OperationViewModel()\n        {\n            Input.WhenAdded(x =>\n            {\n                x.Operation = this;\n                x.IsInput = true;\n                x.PropertyChanged += OnInputValueChanged;\n            })\n            .WhenRemoved(x =>\n            {\n                x.PropertyChanged -= OnInputValueChanged;\n            });\n        }\n\n        private void OnInputValueChanged(object? sender, PropertyChangedEventArgs e)\n        {\n            if (e.PropertyName == nameof(ConnectorViewModel.Value))\n            {\n                OnInputValueChanged();\n            }\n        }\n\n        private Point _location;\n        public Point Location\n        {\n            get => _location;\n            set => SetProperty(ref _location, value);\n        }\n\n        private Size _size;\n        public Size Size\n        {\n            get => _size;\n            set => SetProperty(ref _size, value);\n        }\n\n        private string? _title;\n        public string? Title\n        {\n            get => _title;\n            set => SetProperty(ref _title, value);\n        }\n\n        private bool _isSelected;\n        public bool IsSelected\n        {\n            get => _isSelected;\n            set => SetProperty(ref _isSelected, value);\n        }\n\n        public bool IsReadOnly { get; set; }\n\n        private IOperation? _operation;\n        public IOperation? Operation\n        {\n            get => _operation;\n            set => SetProperty(ref _operation, value)\n                .Then(OnInputValueChanged);\n        }\n\n        public NodifyObservableCollection<ConnectorViewModel> Input { get; } = new NodifyObservableCollection<ConnectorViewModel>();\n\n        private ConnectorViewModel? _output;\n        public ConnectorViewModel? Output\n        {\n            get => _output;\n            set\n            {\n                if (SetProperty(ref _output, value) && _output != null)\n                {\n                    _output.Operation = this;\n                }\n            }\n        }\n\n        protected virtual void OnInputValueChanged()\n        {\n            if (Output != null && Operation != null)\n            {\n                try\n                {\n                    var input = Input.Select(i => i.Value).ToArray();\n                    Output.Value = Operation?.Execute(input) ?? 0;\n                }\n                catch\n                {\n\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.Calculator/Operations/BinaryOperation.cs",
    "content": "﻿using System;\n\nnamespace Nodify.Calculator\n{\n    public class BinaryOperation : IOperation\n    {\n        private readonly Func<double, double, double> _func;\n\n        public BinaryOperation(Func<double, double, double> func) => _func = func;\n\n        public double Execute(params double[] operands)\n            => _func.Invoke(operands[0], operands[1]);\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.Calculator/Operations/IOperation.cs",
    "content": "﻿namespace Nodify.Calculator\n{\n    public interface IOperation\n    {\n        double Execute(params double[] operands);\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.Calculator/Operations/OperationFactory.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Reflection;\nusing System.Windows;\n\nnamespace Nodify.Calculator\n{\n    public static class OperationFactory\n    {\n        public static List<OperationInfoViewModel> GetOperationsInfo(Type container)\n        {\n            List<OperationInfoViewModel> result = new List<OperationInfoViewModel>();\n\n            foreach (var method in container.GetMethods())\n            {\n                if (method.IsStatic)\n                {\n                    OperationInfoViewModel op = new OperationInfoViewModel\n                    {\n                        Title = method.Name\n                    };\n\n                    var attr = method.GetCustomAttribute<OperationAttribute>();\n                    var para = method.GetParameters();\n\n                    bool generateInputNames = true;\n\n                    op.Type = OperationType.Normal;\n\n                    if (para.Length == 2)\n                    {\n                        var delType = typeof(Func<double, double, double>);\n                        var del = (Func<double, double, double>)Delegate.CreateDelegate(delType, method);\n\n                        op.Operation = new BinaryOperation(del);\n                    }\n                    else if (para.Length == 1)\n                    {\n                        if (para[0].ParameterType.IsArray)\n                        {\n                            op.Type = OperationType.Expando;\n\n                            var delType = typeof(Func<double[], double>);\n                            var del = (Func<double[], double>)Delegate.CreateDelegate(delType, method);\n\n                            op.Operation = new ParamsOperation(del);\n                            op.MaxInput = int.MaxValue;\n                        }\n                        else\n                        {\n                            var delType = typeof(Func<double, double>);\n                            var del = (Func<double, double>)Delegate.CreateDelegate(delType, method);\n\n                            op.Operation = new UnaryOperation(del);\n                        }\n                    }\n                    else if (para.Length == 0)\n                    {\n                        var delType = typeof(Func<double>);\n                        var del = (Func<double>)Delegate.CreateDelegate(delType, method);\n\n                        op.Operation = new ValueOperation(del);\n                    }\n\n                    if (attr != null)\n                    {\n                        op.MinInput = attr.MinInput;\n                        op.MaxInput = attr.MaxInput;\n                        generateInputNames = attr.GenerateInputNames;\n                    }\n                    else\n                    {\n                        op.MinInput = (uint)para.Length;\n                        op.MaxInput = (uint)para.Length;\n                    }\n\n                    foreach (var param in para)\n                    {\n                        op.Input.Add(generateInputNames ? param.Name : null);\n                    }\n\n                    for (int i = op.Input.Count; i < op.MinInput; i++)\n                    {\n                        op.Input.Add(null);\n                    }\n\n                    result.Add(op);\n                }\n            }\n\n            return result;\n        }\n\n        public static OperationViewModel GetOperation(OperationInfoViewModel info)\n        {\n            var input = info.Input.Select(i => new ConnectorViewModel\n            {\n                Title = i\n            });\n\n            switch (info.Type)\n            {\n                case OperationType.Expression:\n                    return new ExpressionOperationViewModel\n                    {\n                        Title = info.Title,\n                        Output = new ConnectorViewModel(),\n                        Operation = info.Operation,\n                        Expression = \"1 + sin {a} + cos {b}\"\n                    };\n\n                case OperationType.Calculator:\n                    return new CalculatorOperationViewModel\n                    {\n                        Title = info.Title,\n                        Operation = info.Operation,\n                    };\n\n                case OperationType.Expando:\n                    var o = new ExpandoOperationViewModel\n                    {\n                        MaxInput = info.MaxInput,\n                        MinInput = info.MinInput,\n                        Title = info.Title,\n                        Output = new ConnectorViewModel(),\n                        Operation = info.Operation\n                    };\n\n                    o.Input.AddRange(input);\n                    return o;\n\n                case OperationType.Group:\n                    return new OperationGroupViewModel\n                    {\n                        Title = info.Title,\n                    };\n\n                case OperationType.Graph:\n                    return new OperationGraphViewModel\n                    {\n                        Title = info.Title,\n                        DesiredSize = new Size(420, 250)\n                    };\n\n                default:\n                {\n                    var op = new OperationViewModel\n                    {\n                        Title = info.Title,\n                        Output = new ConnectorViewModel(),\n                        Operation = info.Operation\n                    };\n\n                    op.Input.AddRange(input);\n                    return op;\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.Calculator/Operations/OperationsContainer.cs",
    "content": "﻿using System;\nusing System.Linq;\n\nnamespace Nodify.Calculator\n{\n    public static class OperationsContainer\n    {\n        [Operation(MinInput = 2, MaxInput = 10, GenerateInputNames = false)]\n        public static double Add(params double[] operands)\n            => operands.Sum();\n\n        [Operation(MinInput = 2, MaxInput = 10, GenerateInputNames = false)]\n        public static double Multiply(params double[] operands)\n            => operands.Aggregate((x, y) => x * y);\n\n        public static double Divide(double a, double b)\n            => a / b;\n\n        public static double Subtract(double a, double b)\n            => a - b;\n\n        public static double Pow(double value, double exp)\n            => (double)Math.Pow((double)value, (double)exp);\n\n        [Operation(GenerateInputNames = false)]\n        public static double Abs(double value)\n            => Math.Abs(value);\n\n        public static double PI()\n            => (double)Math.PI;\n    }\n\n    public sealed class OperationAttribute : Attribute\n    {\n        public uint MaxInput { get; set; }\n        public uint MinInput { get; set; }\n        public bool GenerateInputNames { get; set; }\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.Calculator/Operations/ParamsOperation.cs",
    "content": "﻿using System;\n\nnamespace Nodify.Calculator\n{\n    public class ParamsOperation : IOperation\n    {\n        private readonly Func<double[], double> _func;\n\n        public ParamsOperation(Func<double[], double> func) => _func = func;\n\n        public double Execute(params double[] operands)\n            => _func.Invoke(operands);\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.Calculator/Operations/UnaryOperation.cs",
    "content": "﻿using System;\n\nnamespace Nodify.Calculator\n{\n    public class UnaryOperation : IOperation\n    {\n        private readonly Func<double, double> _func;\n\n        public UnaryOperation(Func<double, double> func) => _func = func;\n\n        public double Execute(params double[] operands)\n            => _func.Invoke(operands[0]);\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.Calculator/Operations/ValueOperation.cs",
    "content": "﻿using System;\n\nnamespace Nodify.Calculator\n{\n    public class ValueOperation : IOperation\n    {\n        private readonly Func<double> _func;\n\n        public ValueOperation(Func<double> func) => _func = func;\n\n        public double Execute(params double[] operands)\n            => _func();\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.Calculator/OperationsExtensions.cs",
    "content": "﻿using System.Collections.Generic;\nusing System.Windows;\n\nnamespace Nodify.Calculator\n{\n    public static class OperationsExtensions\n    {\n        public static Rect GetBoundingBox(this IEnumerable<OperationViewModel> nodes, double padding = 0, int gridCellSize = 15)\n        {\n            var minX = double.MaxValue;\n            var minY = double.MaxValue;\n\n            var maxX = double.MinValue;\n            var maxY = double.MinValue;\n\n            const int width = 200; //node.Width\n            const int height = 100; //node.Height\n\n            foreach (var node in nodes)\n            {\n                if (node.Location.X < minX)\n                {\n                    minX = node.Location.X;\n                }\n\n                if (node.Location.Y < minY)\n                {\n                    minY = node.Location.Y;\n                }\n\n                var sizeX = node.Location.X + width;\n                if (sizeX > maxX)\n                {\n                    maxX = sizeX;\n                }\n\n                var sizeY = node.Location.Y + height;\n                if (sizeY > maxY)\n                {\n                    maxY = sizeY;\n                }\n            }\n\n            var result = new Rect(minX - padding, minY - padding, maxX - minX + padding * 2, maxY - minY + padding * 2);\n            result = new Rect((int)result.X / gridCellSize * gridCellSize,\n                (int)result.Y / gridCellSize * gridCellSize,\n                result.Width,\n                result.Height);\n            return result;\n        }\n    }\n}"
  },
  {
    "path": "Examples/Nodify.Calculator/OperationsMenuView.xaml",
    "content": "﻿<UserControl x:Class=\"Nodify.Calculator.OperationsMenuView\"\n             xmlns=\"https://github.com/avaloniaui\"\n             xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n             xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\"\n             xmlns:d=\"http://schemas.microsoft.com/expression/blend/2008\"\n             xmlns:local=\"clr-namespace:Nodify.Calculator\"\n             xmlns:shared=\"clr-namespace:Nodify;assembly=Nodify.Shared\"\n             mc:Ignorable=\"d\"\n             MinWidth=\"250\"\n             d:DesignHeight=\"400\"\n             d:DesignWidth=\"250\"\n             d:DataContext=\"{d:DesignInstance local:OperationsMenuViewModel}\">\n    <UserControl.Resources>\n        <ControlTheme TargetType=\"{x:Type TextBlock}\" x:Key=\"{x:Type TextBlock}\">\n            <Setter Property=\"Foreground\"\n                    Value=\"{DynamicResource ForegroundBrush}\" />\n        </ControlTheme>\n    </UserControl.Resources>\n\n    <Border Padding=\"7\"\n            CornerRadius=\"3\"\n            Background=\"{DynamicResource Node.BackgroundBrush}\"\n            BorderBrush=\"{StaticResource NodifyEditor.SelectionRectangleStrokeBrush}\"\n            BorderThickness=\"2\"\n            IsVisible=\"{Binding IsVisible, Converter={shared:BooleanToVisibilityConverter}}\">\n        <Grid>\n            <Grid.RowDefinitions>\n                <RowDefinition Height=\"Auto\" />\n                <RowDefinition Height=\"*\" />\n            </Grid.RowDefinitions>\n\n            <ItemsControl Grid.Row=\"1\"\n                          ItemsSource=\"{Binding AvailableOperations}\">\n                <ItemsControl.ItemTemplate>\n                    <DataTemplate DataType=\"{x:Type local:OperationInfoViewModel}\">\n                        <Button Content=\"{Binding Title}\"\n                                Command=\"{Binding DataContext.CreateOperationCommand, RelativeSource={RelativeSource AncestorType=UserControl}}\"\n                                CommandParameter=\"{Binding}\"\n                                Background=\"Transparent\"\n                                BorderBrush=\"Transparent\"\n                                Foreground=\"{DynamicResource ForegroundBrush}\"\n                                Padding=\"3\"\n                                Cursor=\"Hand\"\n                                HorizontalContentAlignment=\"Left\">\n                            <Button.Theme>\n                                <ControlTheme TargetType=\"{x:Type Button}\">\n                                    <Setter Property=\"Template\">\n                                        <Setter.Value>\n                                            <ControlTemplate TargetType=\"{x:Type Button}\">\n                                                <Border Name=\"Border\"\n                                                        Background=\"{TemplateBinding Background}\"\n                                                        Padding=\"{TemplateBinding Padding}\">\n                                                    <ContentPresenter Content=\"{TemplateBinding Content}\" ContentTemplate=\"{TemplateBinding ContentTemplate}\" />\n                                                </Border>\n                                            </ControlTemplate>\n                                        </Setter.Value>\n                                    </Setter>\n                                    <Style Selector=\"^:pointerover /template/ Border#Border\">\n                                        <Setter Property=\"Background\"\n                                                Value=\"{DynamicResource NodeInput.BorderBrush}\" />                                        \n                                    </Style>\n                                </ControlTheme>\n                            </Button.Theme>\n                        </Button>\n                    </DataTemplate>\n                </ItemsControl.ItemTemplate>\n            </ItemsControl>\n        </Grid>\n    </Border>\n</UserControl>\n"
  },
  {
    "path": "Examples/Nodify.Calculator/OperationsMenuView.xaml.cs",
    "content": "﻿using System.Windows.Controls;\n\nnamespace Nodify.Calculator\n{\n    public partial class OperationsMenuView : UserControl\n    {\n        public OperationsMenuView()\n        {\n            InitializeComponent();\n        }\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.Calculator/OperationsMenuViewModel.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Windows;\n\nnamespace Nodify.Calculator\n{\n    public class OperationsMenuViewModel : ObservableObject\n    {\n        private bool _isVisible;\n        public bool IsVisible\n        {\n            get => _isVisible;\n            set\n            {\n                SetProperty(ref _isVisible, value);\n                if (!value)\n                {\n                    Closed?.Invoke();\n                }\n            }\n        }\n\n        private Point _location;\n        public Point Location\n        {\n            get => _location;\n            set => SetProperty(ref _location, value);\n        }\n\n        public event Action? Closed;\n\n        public void OpenAt(Point targetLocation)\n        {\n            Close();\n            Location = targetLocation;\n            IsVisible = true;\n        }\n\n        public void Close()\n        {\n            IsVisible = false;\n        }\n\n        public NodifyObservableCollection<OperationInfoViewModel> AvailableOperations { get; }\n        public INodifyCommand CreateOperationCommand { get; }\n        private readonly CalculatorViewModel _calculator;\n\n        public OperationsMenuViewModel(CalculatorViewModel calculator)\n        {\n            _calculator = calculator;\n            List<OperationInfoViewModel> operations = new List<OperationInfoViewModel>\n            {\n                new OperationInfoViewModel\n                {\n                    Type = OperationType.Graph,\n                    Title = \"Operation Graph\",\n                },\n                new OperationInfoViewModel\n                {\n                    Type = OperationType.Calculator,\n                    Title = \"Calculator\"\n                },\n                new OperationInfoViewModel\n                {\n                    Type = OperationType.Expression,\n                    Title = \"Custom\",\n                }\n            };\n            operations.AddRange(OperationFactory.GetOperationsInfo(typeof(OperationsContainer)));\n\n            AvailableOperations = new NodifyObservableCollection<OperationInfoViewModel>(operations);\n            CreateOperationCommand = new DelegateCommand<OperationInfoViewModel>(CreateOperation);\n        }\n\n        private void CreateOperation(OperationInfoViewModel operationInfo)\n        {\n            OperationViewModel op = OperationFactory.GetOperation(operationInfo);\n            op.Location = Location;\n\n            _calculator.Operations.Add(op);\n\n            var pending = _calculator.PendingConnection;\n            if (pending.IsVisible)\n            {\n                var connector = pending.Source.IsInput ? op.Output : op.Input.FirstOrDefault();\n                if (connector != null && _calculator.CanCreateConnection(pending.Source, connector))\n                {\n                    _calculator.CreateConnection(pending.Source, connector);\n                }\n            }\n            Close();\n        }\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.Calculator/PendingConnectionViewModel.cs",
    "content": "﻿using System.Windows;\n\nnamespace Nodify.Calculator\n{\n    public class PendingConnectionViewModel : ObservableObject\n    {\n        private ConnectorViewModel _source = default!;\n        public ConnectorViewModel Source\n        {\n            get => _source;\n            set => SetProperty(ref _source, value);\n        }\n\n        private ConnectorViewModel? _target;\n        public ConnectorViewModel? Target\n        {\n            get => _target;\n            set => SetProperty(ref _target, value);\n        }\n\n        private bool _isVisible;\n        public bool IsVisible\n        {\n            get => _isVisible;\n            set => SetProperty(ref _isVisible, value);\n        }\n\n        private Point _targetLocation;\n\n        public Point TargetLocation\n        {\n            get => _targetLocation;\n            set => SetProperty(ref _targetLocation, value);\n        }\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.Calculator/Program.cs",
    "content": "﻿using System;\nusing Avalonia;\n\nnamespace Nodify.Calculator;\n\nclass Program\n{\n    // Initialization code. Don't use any Avalonia, third-party APIs or any\n    // SynchronizationContext-reliant code before AppMain is called: things aren't initialized\n    // yet and stuff might break.\n    [STAThread]\n    public static void Main(string[] args) => BuildAvaloniaApp()\n        .StartWithClassicDesktopLifetime(args);\n\n    // Avalonia configuration, don't remove; also used by visual designer.\n    public static AppBuilder BuildAvaloniaApp()\n        => AppBuilder.Configure<App>()\n            .UsePlatformDetect()\n            .WithInterFont()\n            .LogToTrace();\n}"
  },
  {
    "path": "Examples/Nodify.Calculator/Themes/Dark.xaml",
    "content": "﻿<ResourceDictionary xmlns=\"https://github.com/avaloniaui\"\n                    xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\">\n\n</ResourceDictionary>"
  },
  {
    "path": "Examples/Nodify.Calculator/Themes/Light.xaml",
    "content": "﻿<ResourceDictionary xmlns=\"https://github.com/avaloniaui\"\n                    xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\">\n\n</ResourceDictionary>"
  },
  {
    "path": "Examples/Nodify.Calculator/Themes/Nodify.xaml",
    "content": "﻿<ResourceDictionary xmlns=\"https://github.com/avaloniaui\"\n                    xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\">\n\n</ResourceDictionary>"
  },
  {
    "path": "Examples/Nodify.Playground/App.xaml",
    "content": "﻿<Application x:Class=\"Nodify.Playground.App\"\n             xmlns=\"https://github.com/avaloniaui\"\n             xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n             RequestedThemeVariant=\"Dark\">\n    <Application.Resources>\n        <ResourceDictionary>\n            <ResourceDictionary.MergedDictionaries>\n                <ResourceInclude Source=\"avares://Nodify/Themes/Generic.xaml\" />\n                <ResourceInclude Source=\"avares://Nodify/Themes/Nodify.xaml\" />\n                <ResourceInclude Source=\"avares://Nodify.Shared/Themes/Icons.xaml\" />\n                <ResourceInclude Source=\"avares://Nodify.Shared/Themes/Generic.xaml\" />\n                <ResourceInclude Source=\"avares://Nodify.Shared/Themes/Nodify.xaml\" />\n                <ResourceInclude Source=\"avares://Nodify.Playground/Themes/Nodify.xaml\" />\n            </ResourceDictionary.MergedDictionaries>\n        </ResourceDictionary>\n    </Application.Resources>\n    \n    <Application.Styles>\n        <FluentTheme DensityStyle=\"Compact\"/>\n    </Application.Styles>\n    \n    <Application.DataTemplates>\n        <DataTemplate x:DataType=\"DrawingBrush\">\n            <Rectangle Fill=\"{Binding .}\" Width=\"16\" Height=\"16\" />\n        </DataTemplate>\n    </Application.DataTemplates>\n</Application>\n"
  },
  {
    "path": "Examples/Nodify.Playground/App.xaml.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Configuration;\nusing System.Data;\nusing System.Linq;\nusing System.Threading.Tasks;\nusing System.Windows;\n\nnamespace Nodify.Playground\n{\n    /// <summary>\n    /// Interaction logic for App.xaml\n    /// </summary>\n    public partial class App : Application\n    {\n        public override void Initialize()\n        {\n            AvaloniaXamlLoader.Load(this);\n        }\n\n        public override void OnFrameworkInitializationCompleted()\n        {\n            if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)\n            {\n                desktop.MainWindow = new MainWindow();\n            }\n\n            base.OnFrameworkInitializationCompleted();\n        }\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.Playground/AssemblyInfo.cs",
    "content": "using System.Windows;\n\n[assembly: ThemeInfo(\n    ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located\n                                     //(used if a resource is not found in the page,\n                                     // or application resource dictionaries)\n    ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located\n                                              //(used if a resource is not found in the page,\n                                              // app, or any theme specific resource dictionaries)\n)]\n"
  },
  {
    "path": "Examples/Nodify.Playground/BaseSettingViewModel.cs",
    "content": "﻿using System;\n\nnamespace Nodify.Playground\n{\n    public class BaseSettingViewModel<T> : ObservableObject, ISettingViewModel\n    {\n        public string Name { get; }\n        public string? Description { get; }\n\n        private object? _value;\n\n        object? ISettingViewModel.Value\n        {\n            get => _value;\n            set => SetProperty(ref _value, value);\n        }\n\n        public SettingsType Type { get;}\n\n        public T Value\n        {\n            get => (T)((ISettingViewModel)this).Value!;\n            set => ((ISettingViewModel)this).Value = value;\n        }\n\n        public BaseSettingViewModel(string name, string? description = default)\n        {\n            Name = name;\n            Description = description;\n            Type = typeof(T) switch\n            {\n                { } t when t == typeof(string) => SettingsType.Text,\n                { } t when t == typeof(bool) => SettingsType.Boolean,\n                { } t when t == typeof(uint) || t == typeof(double) => SettingsType.Number,\n                { } t when t == typeof(PointEditor) => SettingsType.Point,\n                { IsEnum: true } => SettingsType.Option,\n                _ => throw new InvalidOperationException($\"Type {typeof(T).Name} does not have a matching {nameof(SettingsType)}.\")\n            };\n        }\n    }\n}"
  },
  {
    "path": "Examples/Nodify.Playground/Converters/FlowToConnectorPositionConverter.cs",
    "content": "﻿using System;\nusing System.Globalization;\nusing System.Windows.Controls;\nusing System.Windows.Data;\n\nnamespace Nodify.Playground\n{\n    public class FlowToConnectorPositionConverter : IValueConverter\n    {\n        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)\n        {\n            if (value is ConnectionViewModel connection)\n            {\n                var connector = parameter is \"Input\" ? connection.Input : connection.Output;\n\n                if (connector.Node is KnotNodeViewModel)\n                {\n                    var otherConnector = connection.Input == connector ? connection.Output : connection.Input;\n\n                    if (otherConnector.Node is KnotNodeViewModel)\n                    {\n                        return ToPosition(connector == connection.Input ? ConnectorFlow.Input : ConnectorFlow.Output, connector.Node.Orientation);\n                    }\n\n                    return ToPosition(otherConnector.Flow == ConnectorFlow.Output ? ConnectorFlow.Input : ConnectorFlow.Output, connector.Node.Orientation);\n                }\n\n                return ToPosition(connector.Flow, connector.Node.Orientation);\n            }\n\n            return value;\n        }\n\n        private ConnectorPosition ToPosition(ConnectorFlow flow, Orientation orientation)\n        {\n            if (orientation == Orientation.Horizontal)\n            {\n                return flow == ConnectorFlow.Output\n                    ? ConnectorPosition.Right\n                    : ConnectorPosition.Left;\n            }\n\n            return flow == ConnectorFlow.Output\n                ? ConnectorPosition.Bottom\n                : ConnectorPosition.Top;\n        }\n\n        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)\n        {\n            throw new NotImplementedException();\n        }\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.Playground/Converters/FlowToDirectionConverter.cs",
    "content": "﻿using System;\nusing System.Globalization;\nusing System.Windows.Data;\n\nnamespace Nodify.Playground\n{\n    public class FlowToDirectionConverter : IValueConverter\n    {\n        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)\n        {\n            if (value is ConnectorFlow flow)\n            {\n                return flow == ConnectorFlow.Output ? ConnectionDirection.Forward : ConnectionDirection.Backward;\n            }\n\n            return value;\n        }\n\n        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)\n        {\n            if (value is ConnectionDirection dir)\n            {\n                return dir == ConnectionDirection.Forward ? ConnectorFlow.Output : ConnectorFlow.Input;\n            }\n\n            return value;\n        }\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.Playground/Converters/UIntToRectConverter.cs",
    "content": "﻿using System;\nusing System.Globalization;\nusing System.Windows;\nusing System.Windows.Data;\nusing System.Windows.Markup;\n\nnamespace Nodify.Playground\n{\n    public class UIntToRectConverter : MarkupExtension, IValueConverter\n    {\n        public uint Multiplier { get; set; } = 1;\n\n        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)\n        {\n            uint size = System.Convert.ToUInt32(value) * Multiplier;\n            return new Rect(0d, 0d, size, size);\n        }\n\n        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)\n        {\n            throw new NotSupportedException();\n        }\n\n        public override object ProvideValue(IServiceProvider serviceProvider)\n            => this;\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.Playground/Converters/UIntToRelativeRectConverter.cs",
    "content": "﻿using System;\nusing System.Globalization;\nusing System.Windows;\nusing System.Windows.Data;\nusing System.Windows.Markup;\n\nnamespace Nodify.Playground\n{\n    public class UIntToRelativeRectConverter : MarkupExtension, IValueConverter\n    {\n        public static UIntToRelativeRectConverter Absolute { get; } = new UIntToRelativeRectConverter() { Unit = RelativeUnit.Absolute };\n        \n        public static UIntToRelativeRectConverter Relative { get; } = new UIntToRelativeRectConverter() { Unit = RelativeUnit.Relative };\n        \n        public uint Multiplier { get; set; } = 1;\n        \n        public RelativeUnit Unit { get; set; } = RelativeUnit.Absolute;\n\n        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)\n        {\n            uint size = System.Convert.ToUInt32(value) * Multiplier;\n            return new RelativeRect(0d, 0d, size, size, Unit);\n        }\n\n        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)\n        {\n            throw new NotSupportedException();\n        }\n\n        public override object ProvideValue(IServiceProvider serviceProvider)\n            => this;\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.Playground/Editor/CommentNodeViewModel.cs",
    "content": "﻿using System.Windows;\n\nnamespace Nodify.Playground\n{\n    public class CommentNodeViewModel : NodeViewModel\n    {\n        private string? _title;\n        public string? Title\n        {\n            get => _title;\n            set => SetProperty(ref _title, value);\n        }\n\n        private Size _size;\n        public Size Size\n        {\n            get => _size;\n            set => SetProperty(ref _size, value);\n        }\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.Playground/Editor/ConnectionViewModel.cs",
    "content": "﻿using System.Windows;\nusing System.Windows.Input;\n\nnamespace Nodify.Playground\n{\n    public class ConnectionViewModel : ObservableObject\n    {\n        private NodifyEditorViewModel _graph = default!;\n        public NodifyEditorViewModel Graph\n        {\n            get => _graph;\n            internal set => SetProperty(ref _graph, value);\n        }\n\n        private ConnectorViewModel _input = default!;\n        public ConnectorViewModel Input\n        {\n            get => _input;\n            set => SetProperty(ref _input, value);\n        }\n\n        private ConnectorViewModel _output = default!;\n        public ConnectorViewModel Output\n        {\n            get => _output;\n            set => SetProperty(ref _output, value);\n        }\n\n        private bool _isSelected;\n        public bool IsSelected\n        {\n            get => _isSelected;\n            set => SetProperty(ref _isSelected, value);\n        }\n\n        public ICommand SplitCommand { get; }\n        public ICommand DisconnectCommand { get; }\n\n        public ConnectionViewModel()\n        {\n            SplitCommand = new DelegateCommand<Point>(Split);\n            DisconnectCommand = new DelegateCommand(Remove);\n        }\n\n        public void Split(Point point)\n            => Graph.Schema.SplitConnection(this, point);\n\n        public void Remove()\n            => Graph.Connections.Remove(this);\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.Playground/Editor/ConnectorViewModel.cs",
    "content": "﻿using System.Linq;\nusing System.Windows;\n\nnamespace Nodify.Playground\n{\n    public enum ConnectorFlow\n    {\n        Input,\n        Output\n    }\n\n    public enum ConnectorShape\n    {\n        Circle,\n        Triangle,\n        Square,\n    }\n\n    public class ConnectorViewModel : ObservableObject\n    {\n        private string? _title;\n        public string? Title\n        {\n            get => _title;\n            set => SetProperty(ref _title, value);\n        }\n\n        private bool _isConnected;\n        public bool IsConnected\n        {\n            get => _isConnected;\n            set => SetProperty(ref _isConnected, value);\n        }\n\n        private Point _anchor;\n        public Point Anchor\n        {\n            get => _anchor;\n            set => SetProperty(ref _anchor, value);\n        }\n\n        private NodeViewModel _node = default!;\n        public NodeViewModel Node\n        {\n            get => _node;\n            internal set\n            {\n                if (SetProperty(ref _node, value))\n                {\n                    OnNodeChanged();\n                }\n            }\n        }\n\n        private ConnectorShape _shape;\n        public ConnectorShape Shape\n        {\n            get => _shape;\n            set => SetProperty(ref _shape, value);\n        }\n\n        public ConnectorFlow Flow { get; private set; }\n\n        public int MaxConnections { get; set; } = 2;\n\n        public NodifyObservableCollection<ConnectionViewModel> Connections { get; } = new NodifyObservableCollection<ConnectionViewModel>();\n\n        public ConnectorViewModel()\n        {\n            Connections.WhenAdded(c =>\n            {\n                c.Input.IsConnected = true;\n                c.Output.IsConnected = true;\n            }).WhenRemoved(c =>\n            {\n                if (c.Input.Connections.Count == 0)\n                {\n                    c.Input.IsConnected = false;\n                }\n\n                if (c.Output.Connections.Count == 0)\n                {\n                    c.Output.IsConnected = false;\n                }\n            });\n        }\n\n        protected virtual void OnNodeChanged()\n        {\n            if (Node is FlowNodeViewModel flow)\n            {\n                Flow = flow.Input.Contains(this) ? ConnectorFlow.Input : ConnectorFlow.Output;\n            }\n            else if (Node is KnotNodeViewModel knot)\n            {\n                Flow = knot.Flow;\n            }\n        }\n\n        public bool IsConnectedTo(ConnectorViewModel con)\n            => Connections.Any(c => c.Input == con || c.Output == con);\n\n        public virtual bool AllowsNewConnections()\n            => Connections.Count < MaxConnections;\n\n        public void Disconnect()\n            => Node.Graph.Schema.DisconnectConnector(this);\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.Playground/Editor/FlowNodeViewModel.cs",
    "content": "﻿using System.Windows.Controls;\n\nnamespace Nodify.Playground\n{\n    public class FlowNodeViewModel : NodeViewModel\n    {\n        private string? _title;\n        public string? Title\n        {\n            get => _title;\n            set => SetProperty(ref _title, value);\n        }\n\n        public NodifyObservableCollection<ConnectorViewModel> Input { get; } = new NodifyObservableCollection<ConnectorViewModel>();\n        public NodifyObservableCollection<ConnectorViewModel> Output { get; } = new NodifyObservableCollection<ConnectorViewModel>();\n\n        public FlowNodeViewModel()\n        {\n            Orientation = Orientation.Horizontal;\n\n            Input.WhenAdded(c => c.Node = this)\n                 .WhenRemoved(c => c.Disconnect());\n\n            Output.WhenAdded(c => c.Node = this)\n                 .WhenRemoved(c => c.Disconnect());\n        }\n\n        public void Disconnect()\n        {\n            Input.Clear();\n            Output.Clear();\n        }\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.Playground/Editor/GraphSchema.cs",
    "content": "﻿using System.Collections.Generic;\nusing System.Linq;\nusing System.Windows;\n\nnamespace Nodify.Playground\n{\n    public class GraphSchema\n    {\n        #region Add Connection\n\n        public bool CanAddConnection(ConnectorViewModel source, object target)\n        {\n            if (target is ConnectorViewModel con)\n            {\n                return source != con\n                    && source.Node != con.Node\n                    && source.Node.Graph == con.Node.Graph\n                    && source.Shape == con.Shape\n                    && source.AllowsNewConnections()\n                    && con.AllowsNewConnections()\n                    && (source.Flow != con.Flow || con.Node is KnotNodeViewModel)\n                    && !source.IsConnectedTo(con);\n            }\n            else if (source.AllowsNewConnections() && target is FlowNodeViewModel node)\n            {\n                var allConnectors = source.Flow == ConnectorFlow.Input ? node.Output : node.Input;\n                return allConnectors.Any(c => c.AllowsNewConnections());\n            }\n\n            return false;\n        }\n\n        public bool TryAddConnection(ConnectorViewModel source, object? target)\n        {\n            if (target != null && CanAddConnection(source, target))\n            {\n                if (target is ConnectorViewModel connector)\n                {\n                    AddConnection(source, connector);\n                    return true;\n                }\n                else if (target is FlowNodeViewModel node)\n                {\n                    AddConnection(source, node);\n                    return true;\n                }\n            }\n\n            return false;\n        }\n\n        private void AddConnection(ConnectorViewModel source, ConnectorViewModel target)\n        {\n            var sourceIsInput = source.Flow == ConnectorFlow.Input;\n\n            source.Node.Graph.Connections.Add(new ConnectionViewModel\n            {\n                Input = sourceIsInput ? source : target,\n                Output = sourceIsInput ? target : source\n            });\n        }\n\n        private void AddConnection(ConnectorViewModel source, FlowNodeViewModel target)\n        {\n            var allConnectors = source.Flow == ConnectorFlow.Input ? target.Output : target.Input;\n            var connector = allConnectors.First(c => c.AllowsNewConnections());\n\n            AddConnection(source, connector);\n        }\n\n        #endregion\n\n        public void DisconnectConnector(ConnectorViewModel connector)\n        {\n            var graph = connector.Node.Graph;\n            var connections = connector.Connections.ToList();\n            connections.ForEach(c => graph.Connections.Remove(c));\n        }\n\n        public void SplitConnection(ConnectionViewModel connection, Point location)\n        {\n            var knot = new KnotNodeViewModel(connection.Output.Node.Orientation)\n            {\n                Location = location,\n                Flow = connection.Output.Flow,\n                Connector = new ConnectorViewModel\n                {\n                    MaxConnections = connection.Output.MaxConnections + connection.Input.MaxConnections,\n                    Shape = connection.Input.Shape\n                }\n            };\n            connection.Graph.Nodes.Add(knot);\n\n            AddConnection(connection.Output, knot.Connector);\n            AddConnection(knot.Connector, connection.Input);\n\n            connection.Remove();\n        }\n\n        public void AddCommentAroundNodes(IList<NodeViewModel> nodes, string? text = default)\n        {\n            var rect = nodes.GetBoundingBox(50);\n            var comment = new CommentNodeViewModel\n            {\n                Location = rect.Position,\n                Size = rect.Size,\n                Title = text ?? \"New comment\"\n            };\n\n            nodes[0].Graph.Nodes.Add(comment);\n        }\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.Playground/Editor/KnotNodeViewModel.cs",
    "content": "﻿using System.Windows.Controls;\n\nnamespace Nodify.Playground\n{\n    public class KnotNodeViewModel : NodeViewModel\n    {\n        public KnotNodeViewModel(Orientation orientation)\n        {\n            Orientation = orientation;\n        }\n\n        public KnotNodeViewModel() : this(Orientation.Horizontal)\n        {\n        }\n\n        private ConnectorViewModel _connector = default!;\n        public ConnectorViewModel Connector\n        {\n            get => _connector;\n            set\n            {\n                if (SetProperty(ref _connector, value))\n                {\n                    _connector.Node = this;\n                }\n            }\n        }\n\n        public ConnectorFlow Flow { get; set; }\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.Playground/Editor/NodeViewModel.cs",
    "content": "﻿using System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Input;\n\nnamespace Nodify.Playground\n{\n    public abstract class NodeViewModel : ObservableObject\n    {\n        private NodifyEditorViewModel _graph = default!;\n        public NodifyEditorViewModel Graph\n        {\n            get => _graph;\n            internal set => SetProperty(ref _graph, value);\n        }\n\n        private Point _location;\n        public Point Location\n        {\n            get => _location;\n            set => SetProperty(ref _location, value);\n        }\n\n        public Orientation Orientation { get; protected set; }\n\n        public ICommand DeleteCommand { get; }\n\n        public NodeViewModel()\n        {\n            DeleteCommand = new DelegateCommand(() => Graph.Nodes.Remove(this));\n        }\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.Playground/Editor/NodifyEditorView.xaml",
    "content": "﻿<UserControl x:Class=\"Nodify.Playground.NodifyEditorView\"\n             xmlns=\"https://github.com/avaloniaui\"\n             xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n             xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\"\n             xmlns:d=\"http://schemas.microsoft.com/expression/blend/2008\"\n             xmlns:local=\"clr-namespace:Nodify.Playground\"\n             xmlns:nodify=\"https://miroiu.github.io/nodify\"\n             xmlns:shared=\"clr-namespace:Nodify;assembly=Nodify.Shared\"\n             mc:Ignorable=\"d\"\n             Background=\"{DynamicResource NodifyEditor.BackgroundBrush}\"\n             d:DesignHeight=\"450\"\n             d:DesignWidth=\"800\">\n\n    <UserControl.DataContext>\n        <local:NodifyEditorViewModel />\n    </UserControl.DataContext>\n\n    <UserControl.Resources>\n        <shared:RandomBrushConverter x:Key=\"RandomBrushConverter\" />\n        <local:FlowToDirectionConverter x:Key=\"FlowToDirectionConverter\" />\n        <local:FlowToConnectorPositionConverter x:Key=\"FlowToConnectorPositionConverter\" />\n\n        <GeometryDrawing x:Key=\"SmallGridGeometry\"\n                         Geometry=\"M0,0 L0,1 0.03,1 0.03,0.03 1,0.03 1,0 Z\"\n                         Brush=\"{DynamicResource GridLinesBrush}\" />\n\n        <GeometryDrawing x:Key=\"LargeGridGeometry\"\n                         Geometry=\"M0,0 L0,1 0.015,1 0.015,0.015 1,0.015 1,0 Z\"\n                         Brush=\"{DynamicResource GridLinesBrush}\" />\n\n        <DrawingBrush x:Key=\"SmallGridLinesDrawingBrush\"\n                      TileMode=\"Tile\"\n                      DestinationRect=\"{Binding GridSpacing, Source={x:Static local:EditorSettings.Instance}, Converter={local:UIntToRelativeRectConverter}}\"\n                      Transform=\"{Binding DpiScaledViewportTransform, ElementName=Editor}\"\n                      Drawing=\"{StaticResource SmallGridGeometry}\" />\n\n        <DrawingBrush x:Key=\"LargeGridLinesDrawingBrush\"\n                      TileMode=\"Tile\"\n                      Opacity=\"0.5\"\n                      DestinationRect=\"{Binding GridSpacing, Source={x:Static local:EditorSettings.Instance}, Converter={local:UIntToRelativeRectConverter Multiplier=10}}\"\n                      Transform=\"{Binding DpiScaledViewportTransform, ElementName=Editor}\"\n                      Drawing=\"{StaticResource LargeGridGeometry}\" />\n\n        <SolidColorBrush x:Key=\"SquareConnectorColor\"\n                         Color=\"MediumSlateBlue\" />\n        <SolidColorBrush x:Key=\"TriangleConnectorColor\"\n                         Color=\"MediumVioletRed\" />\n        <SolidColorBrush x:Key=\"SquareConnectorOutline\"\n                         Color=\"MediumSlateBlue\"\n                         Opacity=\"0.15\" />\n        <SolidColorBrush x:Key=\"TriangleConnectorOutline\"\n                         Color=\"MediumVioletRed\"\n                         Opacity=\"0.15\" />\n\n        <!-- <UIElement x:Key=\"ConnectionAnimationPlaceholder\" -->\n        <!--            Opacity=\"1\" /> -->\n\n        <!-- <Storyboard x:Key=\"HighlightConnection\"> -->\n        <!--     <DoubleAnimation Storyboard.Target=\"{StaticResource ConnectionAnimationPlaceholder}\" -->\n        <!--                      Storyboard.TargetProperty=\"(UIElement.Opacity)\" -->\n        <!--                      Duration=\"0:0:0.3\" -->\n        <!--                      From=\"1\" -->\n        <!--                      To=\"0.3\" /> -->\n        <!-- </Storyboard> -->\n\n        <ControlTheme x:Key=\"ConnectionStyle\"\n               TargetType=\"{x:Type nodify:BaseConnection}\"\n               BasedOn=\"{StaticResource {x:Type nodify:BaseConnection}}\">\n            <Setter Property=\"(nodify:BindableStyleClasses.Classes)\" Value=\"{Binding Input.Shape}\" />\n            <Setter Property=\"(Interaction.Behaviors)\">\n                <BehaviorCollectionTemplate>\n                    <BehaviorCollection>\n                        <DataTrigger Property=\"Input.Shape\" \n                                     Value=\"{x:Static local:ConnectorShape.Square}\">\n                            <PropertySetter Property=\"Stroke\"\n                                    Value=\"{StaticResource SquareConnectorColor}\" />\n                            <PropertySetter Property=\"Fill\"\n                                    Value=\"{StaticResource SquareConnectorColor}\" />\n                        </DataTrigger>\n                        <DataTrigger Property=\"Input.Shape\" \n                                     Value=\"{x:Static local:ConnectorShape.Triangle}\">\n                            <PropertySetter Property=\"Stroke\"\n                                    Value=\"{StaticResource TriangleConnectorColor}\" />\n                            <PropertySetter Property=\"Fill\"\n                                    Value=\"{StaticResource TriangleConnectorColor}\" />\n                        </DataTrigger>\n                        <DataTrigger Property=\"IsPointerOver\" Value=\"True\">\n                            <!-- <DataTrigger.EnterActions> -->\n                            <!--     <BeginStoryboard Name=\"HighlightConnection\" Storyboard=\"{StaticResource HighlightConnection}\" /> -->\n                            <!-- </DataTrigger.EnterActions> -->\n                            <!-- <DataTrigger.ExitActions> -->\n                            <!--     <RemoveStoryboard BeginStoryboardName=\"HighlightConnection\" /> -->\n                            <!-- </DataTrigger.ExitActions> -->\n                            <PropertySetter Property=\"Opacity\"\n                                    Value=\"1\" />\n                        </DataTrigger>\n                        <DataTrigger Property=\"IsPointerOver\" Value=\"False\">\n                            <PropertySetter Property=\"OutlineBrush\"\n                                            Value=\"Transparent\" />\n                        </DataTrigger>\n                        <DataTrigger Property=\"IsSelectable\"\n                                 Value=\"True\">\n                            <PropertySetter Property=\"Cursor\"\n                                    Value=\"Hand\" />\n                        </DataTrigger>\n                    </BehaviorCollection>\n                </BehaviorCollectionTemplate>\n            </Setter>\n            <Style Selector=\"^.Triangle\">\n                <Setter Property=\"OutlineBrush\"\n                        Value=\"{StaticResource TriangleConnectorOutline}\" />\n            </Style>\n            <Style Selector=\"^.Square\">\n                <Setter Property=\"OutlineBrush\"\n                        Value=\"{StaticResource SquareConnectorOutline}\" />\n            </Style>\n            <Style Selector=\"^[(nodify|BaseConnection.IsSelected)=False]\">\n                <Style Selector=\"^[IsPointerOver=False]\">\n                    <Setter Property=\"OutlineBrush\"\n                            Value=\"Transparent\" />\n                </Style>\n            </Style>\n            <!-- <Setter Property=\"Opacity\" \n                Value=\"{Binding Source={StaticResource ConnectionAnimationPlaceholder}, Path=Opacity}\" /> -->\n            <Setter Property=\"StrokeThickness\" \n                    Value=\"3\"/>\n            <Setter Property=\"Stroke\" \n                    Value=\"{DynamicResource Connection.StrokeBrush}\"/>\n            <Setter Property=\"Fill\" \n                    Value=\"{DynamicResource Connection.StrokeBrush}\"/>\n            <Setter Property=\"OutlineBrush\">\n                <Setter.Value>\n                    <SolidColorBrush Color=\"{DynamicResource Connection.StrokeColor}\"\n                                     Opacity=\"0.15\" />\n                </Setter.Value>\n            </Setter>\n            <Setter Property=\"(ToolTip.Tip)\"\n                    Value=\"Double click to split\" />\n            <Setter Property=\"Source\"\n                    Value=\"{Binding Output.Anchor}\" />\n            <Setter Property=\"Target\"\n                    Value=\"{Binding Input.Anchor}\" />\n            <Setter Property=\"SplitCommand\"\n                    Value=\"{Binding SplitCommand}\" />\n            <Setter Property=\"DisconnectCommand\"\n                    Value=\"{Binding DisconnectCommand}\" />\n            <Setter Property=\"SourceOffsetMode\"\n                    Value=\"{Binding ConnectionSourceOffsetMode, Source={x:Static local:EditorSettings.Instance}}\" />\n            <Setter Property=\"TargetOffsetMode\"\n                    Value=\"{Binding ConnectionTargetOffsetMode, Source={x:Static local:EditorSettings.Instance}}\" />\n            <Setter Property=\"SourceOffset\"\n                    Value=\"{Binding ConnectionSourceOffset.Size, Source={x:Static local:EditorSettings.Instance}}\" />\n            <Setter Property=\"TargetOffset\"\n                    Value=\"{Binding ConnectionTargetOffset.Size, Source={x:Static local:EditorSettings.Instance}}\" />\n            <Setter Property=\"ArrowSize\"\n                    Value=\"{Binding ConnectionArrowSize.Size, Source={x:Static local:EditorSettings.Instance}}\" />\n            <Setter Property=\"ArrowEnds\"\n                    Value=\"{Binding ArrowHeadEnds, Source={x:Static local:EditorSettings.Instance}}\" />\n            <Setter Property=\"ArrowShape\"\n                    Value=\"{Binding ArrowHeadShape, Source={x:Static local:EditorSettings.Instance}}\" />\n            <Setter Property=\"Spacing\"\n                    Value=\"{Binding ConnectionSpacing, Source={x:Static local:EditorSettings.Instance}}\" />\n            <Setter Property=\"Direction\"\n                    Value=\"{Binding Output.Flow, Converter={StaticResource FlowToDirectionConverter}}\" />\n            <Setter Property=\"SourceOrientation\"\n                    Value=\"{Binding Output.Node.Orientation}\" />\n            <Setter Property=\"TargetOrientation\"\n                    Value=\"{Binding Input.Node.Orientation}\" />\n            <Setter Property=\"DirectionalArrowsCount\"\n                    Value=\"{Binding DirectionalArrowsCount, Source={x:Static local:EditorSettings.Instance}}\" />\n            <Setter Property=\"DirectionalArrowsOffset\"\n                    Value=\"{Binding DirectionalArrowsOffset, Source={x:Static local:EditorSettings.Instance}}\" />\n            <Setter Property=\"IsAnimatingDirectionalArrows\"\n                    Value=\"{Binding IsAnimatingConnections, Source={x:Static local:EditorSettings.Instance}}\" />\n            <Setter Property=\"DirectionalArrowsAnimationDuration\"\n                    Value=\"{Binding DirectionalArrowsAnimationDuration, Source={x:Static local:EditorSettings.Instance}}\" />\n            <Setter Property=\"Text\"\n                    Value=\"{Binding ConnectionText, Source={x:Static local:EditorSettings.Instance}}\" />\n            <Setter Property=\"IsSelectable\"\n                    Value=\"{Binding SelectableConnections, Source={x:Static local:EditorSettings.Instance}}\" />\n            <Setter Property=\"IsSelected\"\n                    Value=\"{Binding IsSelected}\" />\n        </ControlTheme>\n\n        <DataTemplate x:Key=\"CircuitConnectionTemplate\">\n            <nodify:CircuitConnection Theme=\"{StaticResource ConnectionStyle}\"\n                                      Angle=\"{Binding CircuitConnectionAngle, Source={x:Static local:EditorSettings.Instance}}\"\n                                      CornerRadius=\"{Binding ConnectionCornerRadius, Source={x:Static local:EditorSettings.Instance}}\" />\n        </DataTemplate>\n\n        <DataTemplate x:Key=\"StepConnectionTemplate\">\n            <nodify:StepConnection Theme=\"{StaticResource ConnectionStyle}\"\n                                   CornerRadius=\"{Binding ConnectionCornerRadius, Source={x:Static local:EditorSettings.Instance}}\"\n                                   SourcePosition=\"{Binding ., Converter={StaticResource FlowToConnectorPositionConverter}, ConverterParameter=Output}\"\n                                   TargetPosition=\"{Binding ., Converter={StaticResource FlowToConnectorPositionConverter}, ConverterParameter=Input}\" />\n        </DataTemplate>\n\n        <DataTemplate x:Key=\"LineConnectionTemplate\">\n            <nodify:LineConnection Theme=\"{StaticResource ConnectionStyle}\"\n                                   CornerRadius=\"{Binding ConnectionCornerRadius, Source={x:Static local:EditorSettings.Instance}}\" />\n        </DataTemplate>\n\n        <DataTemplate x:Key=\"ConnectionTemplate\">\n            <nodify:Connection Theme=\"{StaticResource ConnectionStyle}\" />\n        </DataTemplate>\n\n        <ControlTemplate x:Key=\"SquareConnector\" \n                         TargetType=\"TemplatedControl\">\n            <Rectangle Width=\"14\"\n                       Height=\"14\"\n                       StrokeJoin=\"Round\"\n                       StrokeLineCap=\"Round\"\n                       Stroke=\"{TemplateBinding BorderBrush}\"\n                       Fill=\"{TemplateBinding Background}\"\n                       StrokeThickness=\"2\" />\n        </ControlTemplate>\n\n        <ControlTemplate x:Key=\"TriangleConnector\" \n                         TargetType=\"TemplatedControl\">\n            <Polygon Width=\"14\"\n                     Height=\"14\"\n                     Points=\"1,13 13,13 7,1\"\n                     StrokeLineCap=\"Round\"\n                     StrokeJoin=\"Round\"\n                     Stroke=\"{TemplateBinding BorderBrush}\"\n                     Fill=\"{TemplateBinding Background}\"\n                     StrokeThickness=\"2\" />\n        </ControlTemplate>\n\n        <!-- <Storyboard x:Key=\"MarchingAnts\"> -->\n        <!--     <DoubleAnimation RepeatBehavior=\"Forever\" -->\n        <!--                      Storyboard.TargetProperty=\"StrokeDashOffset\"  -->\n        <!--                      BeginTime=\"00:00:00\" -->\n        <!--                      Duration=\"0:3:0\" -->\n        <!--                      From=\"1000\" -->\n        <!--                      To=\"0\"/> -->\n        <!-- </Storyboard> -->\n\n        <ControlTheme x:Key=\"SelectionRectangleStyle\"\n               TargetType=\"Rectangle\"\n               BasedOn=\"{StaticResource NodifyEditor.SelectionRectangleStyle}\">\n            <Setter Property=\"StrokeDashArray\"\n                    Value=\"4,4\" />\n            <Setter Property=\"StrokeThickness\"\n                    Value=\"2\" />\n            <Setter Property=\"(Interaction.Behaviors)\">\n                <BehaviorCollectionTemplate>\n                    <BehaviorCollection>\n                        <!-- <EventTrigger RoutedEvent=\"Control.Loaded\"> -->\n                        <!--     <BeginStoryboard Storyboard=\"{StaticResource MarchingAnts}\" /> -->\n                        <!-- </EventTrigger> -->\n                    </BehaviorCollection>\n                </BehaviorCollectionTemplate>\n            </Setter>\n        </ControlTheme>\n\n        <ControlTheme x:Key=\"CuttingLineStyle\"\n               TargetType=\"{x:Type nodify:CuttingLine}\"\n               BasedOn=\"{StaticResource {x:Type nodify:CuttingLine}}\">\n            <Setter Property=\"StrokeDashArray\"\n                    Value=\"1,1\" />\n            <Setter Property=\"StrokeThickness\"\n                    Value=\"2\" />\n        </ControlTheme>\n    </UserControl.Resources>\n\n    <Grid>\n        <nodify:NodifyEditor x:Name=\"Editor\"\n                             ItemsSource=\"{Binding Nodes}\"\n                             SelectedItem=\"{Binding SelectedNode}\"\n                             SelectedItems=\"{Binding SelectedNodes}\"\n                             CanSelectMultipleItems=\"{Binding CanSelectMultipleNodes, Source={x:Static local:EditorSettings.Instance}}\"\n                             Connections=\"{Binding Connections}\"\n                             SelectedConnection=\"{Binding SelectedConnection}\"\n                             SelectedConnections=\"{Binding SelectedConnections}\"\n                             CanSelectMultipleConnections=\"{Binding CanSelectMultipleConnections, Source={x:Static local:EditorSettings.Instance}}\"\n                             PendingConnection=\"{Binding PendingConnection}\"\n                             DisconnectConnectorCommand=\"{Binding DisconnectConnectorCommand}\"\n                             ViewportLocation=\"{Binding Location.Value, Source={x:Static local:EditorSettings.Instance}}\"\n                             ViewportSize=\"{Binding ViewportSize, Mode=OneWayToSource}\"\n                             ViewportZoom=\"{Binding Zoom, Source={x:Static local:EditorSettings.Instance}}\"\n                             MinViewportZoom=\"{Binding MinZoom, Source={x:Static local:EditorSettings.Instance}}\"\n                             MaxViewportZoom=\"{Binding MaxZoom, Source={x:Static local:EditorSettings.Instance}}\"\n                             AutoPanSpeed=\"{Binding AutoPanningSpeed, Source={x:Static local:EditorSettings.Instance}}\"\n                             AutoPanEdgeDistance=\"{Binding AutoPanningEdgeDistance, Source={x:Static local:EditorSettings.Instance}}\"\n                             GridCellSize=\"{Binding GridSpacing, Source={x:Static local:EditorSettings.Instance}}\"\n                             EnableRealtimeSelection=\"{Binding EnableRealtimeSelection, Source={x:Static local:EditorSettings.Instance}}\"\n                             DisableAutoPanning=\"{Binding DisableAutoPanning, Source={x:Static local:EditorSettings.Instance}}\"\n                             DisablePanning=\"{Binding DisablePanning, Source={x:Static local:EditorSettings.Instance}}\"\n                             DisableZooming=\"{Binding DisableZooming, Source={x:Static local:EditorSettings.Instance}}\"\n                             DisplayConnectionsOnTop=\"{Binding DisplayConnectionsOnTop, Source={x:Static local:EditorSettings.Instance}}\"\n                             BringIntoViewSpeed=\"{Binding BringIntoViewSpeed, Source={x:Static local:EditorSettings.Instance}}\"\n                             BringIntoViewMaxDuration=\"{Binding BringIntoViewMaxDuration, Source={x:Static local:EditorSettings.Instance}}\"\n                             SelectionRectangleStyle=\"{StaticResource SelectionRectangleStyle}\"\n                             CuttingLineStyle=\"{StaticResource CuttingLineStyle}\">\n            <nodify:NodifyEditor.Theme>\n                <ControlTheme TargetType=\"{x:Type nodify:NodifyEditor}\"\n                       BasedOn=\"{StaticResource {x:Type nodify:NodifyEditor}}\">\n                    <Setter Property=\"ConnectionTemplate\"\n                            Value=\"{StaticResource ConnectionTemplate}\" />\n                    <Setter Property=\"(Interaction.Behaviors)\">\n                        <BehaviorCollectionTemplate>\n                            <BehaviorCollection>\n                                <DataTrigger Property=\"ShowGridLines\" Source=\"{x:Static local:PlaygroundSettings.Instance}\"\n                                             Value=\"True\">\n                                    <PropertySetter Property=\"Background\"\n                                            Value=\"{StaticResource SmallGridLinesDrawingBrush}\" />\n                                </DataTrigger>\n                                <DataTrigger Property=\"ConnectionStyle\" Source=\"{x:Static local:EditorSettings.Instance}\"\n                                                     Value=\"Line\">\n                                    <PropertySetter Property=\"ConnectionTemplate\"\n                                                    Value=\"{StaticResource LineConnectionTemplate}\" />\n                                </DataTrigger>\n                                <DataTrigger Property=\"ConnectionStyle\" Source=\"{x:Static local:EditorSettings.Instance}\"\n                                                     Value=\"Circuit\">\n                                    <PropertySetter Property=\"ConnectionTemplate\"\n                                                    Value=\"{StaticResource CircuitConnectionTemplate}\" />\n                                </DataTrigger>\n                                <DataTrigger Property=\"ConnectionStyle\" Source=\"{x:Static local:EditorSettings.Instance}\"\n                                                     Value=\"Step\">\n                                    <PropertySetter Property=\"ConnectionTemplate\"\n                                                    Value=\"{StaticResource StepConnectionTemplate}\" />\n                                </DataTrigger>\n                            </BehaviorCollection>\n                        </BehaviorCollectionTemplate>\n                    </Setter>\n                </ControlTheme>\n            </nodify:NodifyEditor.Theme>\n\n            <nodify:NodifyEditor.KeyBindings>\n                <KeyBinding Gesture=\"Delete\"\n                            Command=\"{Binding DeleteSelectionCommand}\" />\n                <KeyBinding Gesture=\"C\"\n                            Command=\"{Binding CommentSelectionCommand}\" />\n            </nodify:NodifyEditor.KeyBindings>\n\n            <nodify:NodifyEditor.Resources>\n                <ControlTheme TargetType=\"{x:Type nodify:PendingConnection}\" x:Key=\"{x:Type nodify:PendingConnection}\"\n                       BasedOn=\"{StaticResource {x:Type nodify:PendingConnection}}\">\n                    <Setter Property=\"CompletedCommand\"\n                            Value=\"{Binding Graph.CreateConnectionCommand}\" />\n                    <Setter Property=\"Source\"\n                            Value=\"{Binding Source, Mode=OneWayToSource}\" />\n                    <Setter Property=\"Target\"\n                            Value=\"{Binding PreviewTarget, Mode=OneWayToSource}\" />\n                    <Setter Property=\"PreviewTarget\"\n                            Value=\"{Binding PreviewTarget, Mode=OneWayToSource}\" />\n                    <Setter Property=\"Content\"\n                            Value=\"{Binding PreviewText}\" />\n                    <Setter Property=\"EnablePreview\"\n                            Value=\"{Binding EnablePendingConnectionPreview, Source={x:Static local:EditorSettings.Instance}}\" />\n                    <Setter Property=\"EnableSnapping\"\n                            Value=\"{Binding EnablePendingConnectionSnapping, Source={x:Static local:EditorSettings.Instance}}\" />\n                    <Setter Property=\"AllowOnlyConnectors\"\n                            Value=\"{Binding AllowConnectingToConnectorsOnly, Source={x:Static local:EditorSettings.Instance}}\" />\n                    <Setter Property=\"Direction\"\n                            Value=\"{Binding Source.Flow, Converter={StaticResource FlowToDirectionConverter}}\" />\n                    <Setter Property=\"Template\">\n                        <Setter.Value>\n                            <ControlTemplate TargetType=\"{x:Type nodify:PendingConnection}\">\n                                <Canvas>\n                                    <nodify:Connection Source=\"{TemplateBinding SourceAnchor}\"\n                                                       Target=\"{TemplateBinding TargetAnchor}\"\n                                                       Direction=\"{TemplateBinding Direction}\"\n                                                       SourceOrientation=\"{Binding Source.Node.Orientation}\"\n                                                       TargetOrientation=\"{Binding TargetOrientation}\"\n                                                       DirectionalArrowsCount=\"{Binding DirectionalArrowsCount, Source={x:Static local:EditorSettings.Instance}}\"\n                                                       StrokeThickness=\"{TemplateBinding StrokeThickness}\"\n                                                       SourceOffset=\"{Binding ConnectionSourceOffset.Size, Source={x:Static local:EditorSettings.Instance}}\"\n                                                       TargetOffset=\"{Binding ConnectionTargetOffset.Size, Source={x:Static local:EditorSettings.Instance}}\"\n                                                       SourceOffsetMode=\"{Binding ConnectionSourceOffsetMode, Source={x:Static local:EditorSettings.Instance}}\"\n                                                       TargetOffsetMode=\"None\"\n                                                       ArrowSize=\"{Binding ConnectionArrowSize.Size, Source={x:Static local:EditorSettings.Instance}}\"\n                                                       ArrowEnds=\"{Binding ArrowHeadEnds, Source={x:Static local:EditorSettings.Instance}}\"\n                                                       ArrowShape=\"{Binding ArrowHeadShape, Source={x:Static local:EditorSettings.Instance}}\"\n                                                       Spacing=\"{Binding ConnectionSpacing, Source={x:Static local:EditorSettings.Instance}}\">\n                                        <nodify:Connection.Theme>\n                                            <ControlTheme TargetType=\"nodify:Connection\"\n                                                   BasedOn=\"{StaticResource {x:Type nodify:Connection}}\">\n                                                <Setter Property=\"Stroke\"\n                                                        Value=\"{DynamicResource Connection.StrokeBrush}\" />\n                                                <Setter Property=\"Fill\"\n                                                        Value=\"{DynamicResource Connection.StrokeBrush}\" />\n                                                <Setter Property=\"(Interaction.Behaviors)\">\n                                                    <BehaviorCollectionTemplate>\n                                                        <BehaviorCollection>\n                                                            <DataTrigger Property=\"Source.Shape\" \n                                                                         Value=\"{x:Static local:ConnectorShape.Square}\">\n                                                                <PropertySetter Property=\"Stroke\" \n                                                                                Value=\"{StaticResource SquareConnectorColor}\"/>\n                                                                <PropertySetter Property=\"Fill\" \n                                                                                Value=\"{StaticResource SquareConnectorColor}\"/>\n                                                            </DataTrigger>\n                                                            <DataTrigger Property=\"Source.Shape\" \n                                                                         Value=\"{x:Static local:ConnectorShape.Triangle}\">\n                                                                <PropertySetter Property=\"Stroke\" \n                                                                                Value=\"{StaticResource TriangleConnectorColor}\"/>\n                                                                <PropertySetter Property=\"Fill\" \n                                                                                Value=\"{StaticResource TriangleConnectorColor}\"/>\n                                                            </DataTrigger>\n                                                        </BehaviorCollection>\n                                                    </BehaviorCollectionTemplate>\n                                                </Setter>\n                                            </ControlTheme>\n                                        </nodify:Connection.Theme>\n                                    </nodify:Connection>\n                                    <Border Background=\"{TemplateBinding Background}\"\n                                            Canvas.Left=\"{Binding TargetAnchor.X, RelativeSource={RelativeSource TemplatedParent}}\"\n                                            Canvas.Top=\"{Binding TargetAnchor.Y, RelativeSource={RelativeSource TemplatedParent}}\"\n                                            IsVisible=\"{Binding PreviewText, Converter={shared:StringToVisibilityConverter}}\"\n                                            Padding=\"{TemplateBinding Padding}\"\n                                            BorderThickness=\"{TemplateBinding BorderThickness}\"\n                                            BorderBrush=\"{TemplateBinding BorderBrush}\"\n                                            CornerRadius=\"3\"\n                                            Margin=\"15\">\n                                        <ContentPresenter Content=\"{TemplateBinding Content}\" ContentTemplate=\"{TemplateBinding ContentTemplate}\" />\n                                    </Border>\n                                </Canvas>\n                            </ControlTemplate>\n                        </Setter.Value>\n                    </Setter>\n                </ControlTheme>\n\n                <ControlTheme TargetType=\"{x:Type nodify:Connector}\" x:Key=\"{x:Type nodify:Connector}\"\n                       BasedOn=\"{StaticResource {x:Type nodify:Connector}}\">\n                    <Setter Property=\"Anchor\"\n                            Value=\"{Binding Anchor, Mode=OneWayToSource}\" />\n                    <Setter Property=\"IsConnected\"\n                            Value=\"{Binding IsConnected}\" />\n                </ControlTheme>\n\n                <ControlTheme TargetType=\"{x:Type nodify:NodeInput}\" x:Key=\"{x:Type nodify:NodeInput}\"\n                       BasedOn=\"{StaticResource {x:Type nodify:NodeInput}}\">\n                    <Setter Property=\"(Interaction.Behaviors)\">\n                        <BehaviorCollectionTemplate>\n                            <BehaviorCollection>\n                                <DataTrigger Property=\"Shape\" \n                                             Value=\"{x:Static local:ConnectorShape.Square}\">\n                                    <PropertySetter Property=\"ConnectorTemplate\" \n                                                    Value=\"{StaticResource SquareConnector}\" />\n                                    <PropertySetter Property=\"BorderBrush\" \n                                                    Value=\"{StaticResource SquareConnectorColor}\"/>\n                                    <PropertySetter Property=\"HeaderTemplate\">\n                                        <PropertySetter.Value>\n                                            <DataTemplate DataType=\"{x:Type local:ConnectorViewModel}\">\n                                                <StackPanel Orientation=\"Horizontal\">\n                                                    <TextBlock Text=\"{Binding Title}\" \n                                                               Margin=\"0 0 5 0\" />\n                                                    <TextBox Text=\"{Binding MaxConnections}\" \n                                                             MinWidth=\"30\" />\n                                                </StackPanel>\n                                            </DataTemplate>\n                                        </PropertySetter.Value>\n                                    </PropertySetter>\n                                </DataTrigger>\n                                <DataTrigger Property=\"Shape\" \n                                             Value=\"{x:Static local:ConnectorShape.Triangle}\">\n                                    <PropertySetter Property=\"ConnectorTemplate\" \n                                                    Value=\"{StaticResource TriangleConnector}\" />\n                                    <PropertySetter Property=\"BorderBrush\" \n                                                    Value=\"{StaticResource TriangleConnectorColor}\"/>\n                                    <PropertySetter Property=\"HeaderTemplate\">\n                                        <PropertySetter.Value>\n                                            <DataTemplate DataType=\"{x:Type local:ConnectorViewModel}\">\n                                                <StackPanel Orientation=\"Horizontal\">\n                                                    <TextBlock Text=\"{Binding Title}\" \n                                                               Margin=\"0 0 5 0\" \n                                                               VerticalAlignment=\"Center\" />\n                                                    <CheckBox />\n                                                </StackPanel>\n                                            </DataTemplate>\n                                        </PropertySetter.Value>\n                                    </PropertySetter>\n                                </DataTrigger>\n                            </BehaviorCollection>\n                        </BehaviorCollectionTemplate>\n                    </Setter>\n                    <Setter Property=\"HeaderTemplate\">\n                        <Setter.Value>\n                            <DataTemplate DataType=\"{x:Type local:ConnectorViewModel}\">\n                                <TextBlock Text=\"{Binding Title}\" />\n                            </DataTemplate>\n                        </Setter.Value>\n                    </Setter>\n                    <Setter Property=\"Header\"\n                            Value=\"{Binding}\" />\n                    <Setter Property=\"Anchor\"\n                            Value=\"{Binding Anchor, Mode=OneWayToSource}\" />\n                    <Setter Property=\"IsConnected\"\n                            Value=\"{Binding IsConnected}\" />\n                    <Setter Property=\"Background\"\n                            Value=\"Transparent\" />\n                </ControlTheme>\n\n                <ControlTheme TargetType=\"{x:Type nodify:NodeOutput}\" x:Key=\"{x:Type nodify:NodeOutput}\"\n                       BasedOn=\"{StaticResource {x:Type nodify:NodeOutput}}\">\n                    <Setter Property=\"(Interaction.Behaviors)\">\n                        <BehaviorCollectionTemplate>\n                            <BehaviorCollection>\n                                <DataTrigger Property=\"Shape\" \n                                             Value=\"{x:Static local:ConnectorShape.Square}\">\n                                    <PropertySetter Property=\"ConnectorTemplate\" \n                                                    Value=\"{StaticResource SquareConnector}\" />\n                                    <PropertySetter Property=\"BorderBrush\" \n                                                    Value=\"{StaticResource SquareConnectorColor}\"/>\n                                </DataTrigger>\n                                <DataTrigger Property=\"Shape\" \n                                             Value=\"{x:Static local:ConnectorShape.Triangle}\">\n                                    <PropertySetter Property=\"ConnectorTemplate\" \n                                                    Value=\"{StaticResource TriangleConnector}\" />\n                                    <PropertySetter Property=\"BorderBrush\" \n                                                    Value=\"{StaticResource TriangleConnectorColor}\"/>\n                                </DataTrigger>\n                            </BehaviorCollection>\n                        </BehaviorCollectionTemplate>\n                    </Setter>\n                    <Setter Property=\"Header\"\n                            Value=\"{Binding Title}\" />\n                    <Setter Property=\"Anchor\"\n                            Value=\"{Binding Anchor, Mode=OneWayToSource}\" />\n                    <Setter Property=\"IsConnected\"\n                            Value=\"{Binding IsConnected}\" />\n                    <Setter Property=\"Background\"\n                            Value=\"Transparent\" />\n                </ControlTheme>\n            </nodify:NodifyEditor.Resources>\n\n            <nodify:NodifyEditor.DataTemplates>\n                <DataTemplate DataType=\"{x:Type local:KnotNodeViewModel}\">\n                    <nodify:KnotNode Content=\"{Binding Connector}\" />\n                </DataTemplate>\n\n                <DataTemplate DataType=\"{x:Type local:CommentNodeViewModel}\">\n                    <nodify:GroupingNode ActualSize=\"{Binding Size}\"\n                                         Header=\"{Binding Title}\"\n                                         MovementMode=\"{Binding GroupingNodeMovement, Mode=TwoWay, Source={x:Static local:EditorSettings.Instance}}\" />\n                </DataTemplate>\n\n                <DataTemplate DataType=\"{x:Type local:VerticalNodeViewModel}\">\n                    <nodify:Node Header=\"{Binding Input}\"\n                                 Footer=\"{Binding Output}\"\n                                 Content=\"{Binding Title}\">\n                        <nodify:Node.ContentTemplate>\n                            <DataTemplate>\n                                <TextBlock Text=\"{Binding}\"\n                                           Margin=\"5\" />\n                            </DataTemplate>\n                        </nodify:Node.ContentTemplate>\n                        <nodify:Node.HeaderTemplate>\n                            <DataTemplate>\n                                <ItemsControl ItemsSource=\"{Binding}\">\n                                    <ItemsControl.ItemTemplate>\n                                        <DataTemplate DataType=\"{x:Type local:ConnectorViewModel}\">\n                                            <nodify:NodeInput Orientation=\"Vertical\" />\n                                        </DataTemplate>\n                                    </ItemsControl.ItemTemplate>\n                                    <ItemsControl.ItemsPanel>\n                                        <ItemsPanelTemplate>\n                                            <StackPanel Orientation=\"Horizontal\"\n                                                        HorizontalAlignment=\"Center\" />\n                                        </ItemsPanelTemplate>\n                                    </ItemsControl.ItemsPanel>\n                                </ItemsControl>\n                            </DataTemplate>\n                        </nodify:Node.HeaderTemplate>\n                        <nodify:Node.FooterTemplate>\n                            <DataTemplate>\n                                <ItemsControl ItemsSource=\"{Binding}\">\n                                    <ItemsControl.ItemTemplate>\n                                        <DataTemplate DataType=\"{x:Type local:ConnectorViewModel}\">\n                                            <nodify:NodeOutput Orientation=\"Vertical\" />\n                                        </DataTemplate>\n                                    </ItemsControl.ItemTemplate>\n                                    <ItemsControl.ItemsPanel>\n                                        <ItemsPanelTemplate>\n                                            <StackPanel Orientation=\"Horizontal\"\n                                                        HorizontalAlignment=\"Center\" />\n                                        </ItemsPanelTemplate>\n                                    </ItemsControl.ItemsPanel>\n                                </ItemsControl>\n                            </DataTemplate>\n                        </nodify:Node.FooterTemplate>\n                    </nodify:Node>\n                </DataTemplate>\n\n                <DataTemplate DataType=\"{x:Type local:FlowNodeViewModel}\">\n                    <nodify:Node Input=\"{Binding Input}\"\n                                 Output=\"{Binding Output}\"\n                                 Header=\"{Binding Title}\" />\n                </DataTemplate>\n            </nodify:NodifyEditor.DataTemplates>\n            \n            <nodify:NodifyEditor.ItemContainerTheme>\n                <ControlTheme TargetType=\"{x:Type nodify:ItemContainer}\"\n                       BasedOn=\"{StaticResource {x:Type nodify:ItemContainer}}\">\n                    <Setter Property=\"BorderThickness\"\n                            Value=\"2\" />\n                    <Setter Property=\"SelectedBorderThickness\"\n                            Value=\"4\" />\n                    <Setter Property=\"IsSelectable\"\n                            Value=\"{Binding SelectableNodes, Source={x:Static local:EditorSettings.Instance}}\" />\n                    <Setter Property=\"IsDraggable\"\n                            Value=\"{Binding DraggableNodes, Source={x:Static local:EditorSettings.Instance}}\" />                            \n                    <!-- <Setter Property=\"CacheMode\"> -->\n                    <!--     <Setter.Value> -->\n                    <!--         <BitmapCache RenderAtScale=\"{Binding MaxZoom, Source={x:Static local:EditorSettings.Instance}}\" EnableClearType=\"True\" /> -->\n                    <!--     </Setter.Value> -->\n                    <!-- </Setter> -->\n                    <Setter Property=\"Location\"\n                            Value=\"{Binding Location}\" />\n                    <Setter Property=\"(Interaction.Behaviors)\">\n                        <BehaviorCollectionTemplate>\n                            <BehaviorCollection>\n                                <DataTrigger Property=\"IsSelected\" \n                                             Value=\"True\">\n                                    <PropertySetter Property=\"Panel.ZIndex\" \n                                                    Value=\"1\" />\n                                </DataTrigger>\n                            </BehaviorCollection>\n                        </BehaviorCollectionTemplate>\n                    </Setter>\n                </ControlTheme>\n            </nodify:NodifyEditor.ItemContainerTheme>\n        </nodify:NodifyEditor>\n\n        <Grid Background=\"{StaticResource LargeGridLinesDrawingBrush}\"\n              IsVisible=\"{Binding ShowGridLines, Source={x:Static local:PlaygroundSettings.Instance}, Converter={shared:BooleanToVisibilityConverter}}\"\n              Panel.ZIndex=\"-2\" />\n\n        <nodify:Minimap ItemsSource=\"{Binding ItemsSource, ElementName=Editor}\"\n                        ViewportSize=\"{Binding ViewportSize, ElementName=Editor}\"\n                        ViewportLocation=\"{Binding ViewportLocation, ElementName=Editor}\"\n                        IsVisible=\"{Binding ShowMinimap, Source={x:Static local:PlaygroundSettings.Instance}, Converter={shared:BooleanToVisibilityConverter}}\"\n                        IsReadOnly=\"{Binding DisableMinimapControls, Source={x:Static local:PlaygroundSettings.Instance}}\"\n                        ResizeToViewport=\"{Binding ResizeToViewport, Source={x:Static local:PlaygroundSettings.Instance}}\"\n                        MaxViewportOffset=\"{Binding MinimapMaxViewportOffset.Size, Source={x:Static local:PlaygroundSettings.Instance}}\"\n                        Zoom=\"Minimap_Zoom\"\n                        HorizontalAlignment=\"Right\"\n                        VerticalAlignment=\"Bottom\"\n                        Width=\"300\"\n                        Height=\"200\"\n                        Margin=\"5 40\">\n            <nodify:Minimap.ItemTemplate>\n                <DataTemplate DataType=\"{x:Type local:NodeViewModel}\">\n                    <Grid />\n                </DataTemplate>\n            </nodify:Minimap.ItemTemplate>\n            <nodify:Minimap.ItemContainerTheme>\n                <ControlTheme TargetType=\"{x:Type nodify:MinimapItem}\"\n                       BasedOn=\"{StaticResource {x:Type nodify:MinimapItem}}\">\n                    <Setter Property=\"Location\"\n                            Value=\"{Binding Location}\" />\n                    <Setter Property=\"Width\"\n                            Value=\"150\" />\n                    <Setter Property=\"Height\"\n                            Value=\"130\" />\n                </ControlTheme>\n            </nodify:Minimap.ItemContainerTheme>\n        </nodify:Minimap>\n\n        <StackPanel HorizontalAlignment=\"Right\"\n                    VerticalAlignment=\"Top\"\n                    Margin=\"5 60\"\n                    Width=\"250\">\n            <Border CornerRadius=\"3\"\n                    IsVisible=\"{Binding SelectedConnection, Converter={shared:BooleanToVisibilityConverter}}\"\n                    Background=\"{DynamicResource PanelBackgroundBrush}\"\n                    BorderThickness=\"1\"\n                    BorderBrush=\"{DynamicResource BorderBrush}\"\n                    Margin=\"0 0 0 10\">\n                <StackPanel Margin=\"10\">\n                    <StackPanel.Styles>\n                        <Style Selector=\"TextBlock\">\n                            <Setter Property=\"Margin\"\n                                    Value=\"0 0 0 5\" />\n                        </Style>\n                    </StackPanel.Styles>\n\n                    <StackPanel Margin=\"0 0 0 14\">\n                        <TextBlock Text=\"Selected connection\"\n                                   Foreground=\"{DynamicResource Node.ForegroundBrush}\"\n                                   FontWeight=\"Bold\" />\n                    </StackPanel>\n\n                    <TextBlock TextWrapping=\"Wrap\"\n                               Margin=\"0 0 0 14\"\n                               Foreground=\"{DynamicResource Node.ForegroundBrush}\">\n                        <Run>From</Run>\n                        <Run Text=\"{Binding SelectedConnection.Output.Node.Title}\"\n                             Foreground=\"Red\" />\n                        <Run> - </Run>\n                        <Run Text=\"{Binding SelectedConnection.Output.Title}\"\n                             Foreground=\"Red\" />\n                        <Run>to</Run>\n                        <Run Text=\"{Binding SelectedConnection.Input.Node.Title}\"\n                             Foreground=\"Red\" />\n                        <Run> - </Run>\n                        <Run Text=\"{Binding SelectedConnection.Input.Title}\"\n                             Foreground=\"Red\" />\n                    </TextBlock>\n\n                    <Button Command=\"{Binding SelectedConnection.DisconnectCommand}\"\n                            HorizontalAlignment=\"Left\"\n                            Theme=\"{StaticResource HollowButton}\"\n                            Content=\"Delete\" />\n                </StackPanel>\n            </Border>\n            <Border CornerRadius=\"3\"\n                    IsVisible=\"{Binding SelectedNode, Converter={shared:BooleanToVisibilityConverter}}\"\n                    Background=\"{DynamicResource PanelBackgroundBrush}\"\n                    BorderThickness=\"1\"\n                    BorderBrush=\"{DynamicResource BorderBrush}\">\n                <StackPanel Margin=\"10\">\n                    <StackPanel.Styles>\n                        <Style Selector=\"TextBlock\">\n                            <Setter Property=\"Margin\"\n                                    Value=\"0 0 0 5\" />\n                        </Style>\n                    </StackPanel.Styles>\n\n                    <StackPanel Margin=\"0 0 0 14\">\n                        <TextBlock Text=\"Selected node\"\n                                   Foreground=\"{DynamicResource Node.ForegroundBrush}\"\n                                   FontWeight=\"Bold\" />\n                    </StackPanel>\n\n                    <TextBlock TextWrapping=\"Wrap\"\n                               Margin=\"0 0 0 14\"\n                               Foreground=\"{DynamicResource Node.ForegroundBrush}\">\n                        <Run>Title: </Run>\n                        <Run Text=\"{Binding SelectedNode.Title}\"\n                             Foreground=\"Red\" />\n                    </TextBlock>\n                    <TextBlock TextWrapping=\"Wrap\"\n                               Margin=\"0 0 0 14\"\n                               Foreground=\"{DynamicResource Node.ForegroundBrush}\">\n                        <Run>Location: </Run>\n                        <Run Text=\"{Binding SelectedNode.Location}\"\n                             Foreground=\"Red\" />\n                    </TextBlock>\n\n                    <Button Command=\"{Binding SelectedNode.DeleteCommand}\"\n                            HorizontalAlignment=\"Left\"\n                            Theme=\"{StaticResource HollowButton}\"\n                            Content=\"Delete\" />\n                </StackPanel>\n            </Border>\n        </StackPanel>\n    </Grid>\n\n</UserControl>\n"
  },
  {
    "path": "Examples/Nodify.Playground/Editor/NodifyEditorView.xaml.cs",
    "content": "﻿using System.Windows.Controls;\n\nnamespace Nodify.Playground\n{\n    public partial class NodifyEditorView : UserControl\n    {\n        public NodifyEditor EditorInstance => Editor;\n\n        public NodifyEditorView()\n        {\n            InitializeComponent();\n        }\n\n        private void Minimap_Zoom(object sender, ZoomEventArgs e)\n        {\n            EditorInstance.ZoomAtPosition(e.Zoom, e.Location);\n        }\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.Playground/Editor/NodifyEditorViewModel.cs",
    "content": "﻿using System.Linq;\nusing System.Windows;\nusing System.Windows.Input;\n\nnamespace Nodify.Playground\n{\n    public class NodifyEditorViewModel : ObservableObject\n    {\n        public NodifyEditorViewModel()\n        {\n            Schema = new GraphSchema();\n\n            PendingConnection = new PendingConnectionViewModel\n            {\n                Graph = this\n            };\n\n            DeleteSelectionCommand = new DelegateCommand(DeleteSelection, () => SelectedNodes.Count > 0 || SelectedConnections.Count > 0);\n            CommentSelectionCommand = new RequeryCommand(() => Schema.AddCommentAroundNodes(SelectedNodes, \"New comment\"), () => SelectedNodes.Count > 0);\n            DisconnectConnectorCommand = new DelegateCommand<ConnectorViewModel>(c => c.Disconnect());\n            CreateConnectionCommand = new DelegateCommand<object>(target => Schema.TryAddConnection(PendingConnection.Source!, target), target => PendingConnection.Source != null && target != null);\n\n            Connections.WhenAdded(c =>\n            {\n                c.Graph = this;\n                c.Input.Connections.Add(c);\n                c.Output.Connections.Add(c);\n            })\n            // Called when the collection is cleared\n            .WhenRemoved(c =>\n            {\n                c.Input.Connections.Remove(c);\n                c.Output.Connections.Remove(c);\n            });\n\n            Nodes.WhenAdded(x => x.Graph = this)\n                 // Not called when the collection is cleared\n                 .WhenRemoved(x =>\n                 {\n                     if (x is FlowNodeViewModel flow)\n                     {\n                         flow.Disconnect();\n                     }\n                     else if (x is KnotNodeViewModel knot)\n                     {\n                         knot.Connector.Disconnect();\n                     }\n                 })\n                 .WhenCleared(x => Connections.Clear());\n        }\n\n        private NodifyObservableCollection<NodeViewModel> _nodes = new NodifyObservableCollection<NodeViewModel>();\n        public NodifyObservableCollection<NodeViewModel> Nodes\n        {\n            get => _nodes;\n            set => SetProperty(ref _nodes, value);\n        }\n\n        private NodifyObservableCollection<NodeViewModel> _selectedNodes = new NodifyObservableCollection<NodeViewModel>();\n        public NodifyObservableCollection<NodeViewModel> SelectedNodes\n        {\n            get => _selectedNodes;\n            set => SetProperty(ref _selectedNodes, value);\n        }\n\n        private NodifyObservableCollection<ConnectionViewModel> _selectedConnections = new NodifyObservableCollection<ConnectionViewModel>();\n        public NodifyObservableCollection<ConnectionViewModel> SelectedConnections\n        {\n            get => _selectedConnections;\n            set => SetProperty(ref _selectedConnections, value);\n        }\n\n        private NodifyObservableCollection<ConnectionViewModel> _connections = new NodifyObservableCollection<ConnectionViewModel>();\n        public NodifyObservableCollection<ConnectionViewModel> Connections\n        {\n            get => _connections;\n            set => SetProperty(ref _connections, value);\n        }\n\n        private Size _viewportSize;\n        public Size ViewportSize\n        {\n            get => _viewportSize;\n            set => SetProperty(ref _viewportSize, value);\n        }\n\n        public PendingConnectionViewModel PendingConnection { get; }\n\n        private ConnectionViewModel? _selectedConnection;\n        public ConnectionViewModel? SelectedConnection\n        {\n            get => _selectedConnection;\n            set => SetProperty(ref _selectedConnection, value);\n        }\n\n        private NodeViewModel? _selectedNode;\n        public NodeViewModel? SelectedNode\n        {\n            get => _selectedNode;\n            set => SetProperty(ref _selectedNode, value);\n        }\n\n        public GraphSchema Schema { get; }\n\n        public ICommand DeleteSelectionCommand { get; }\n        public ICommand DisconnectConnectorCommand { get; }\n        public ICommand CreateConnectionCommand { get; }\n        public ICommand CommentSelectionCommand { get; }\n\n        private void DeleteSelection()\n        {\n            foreach (var connection in SelectedConnections.ToList())\n            {\n                connection.Remove();\n            }\n\n            var selected = SelectedNodes.ToList();\n\n            for (int i = 0; i < selected.Count; i++)\n            {\n                Nodes.Remove(selected[i]);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.Playground/Editor/PendingConnectionViewModel.cs",
    "content": "﻿using System.Windows.Controls;\n\nnamespace Nodify.Playground\n{\n    public class PendingConnectionViewModel : ObservableObject\n    {\n        private NodifyEditorViewModel _graph = default!;\n        public NodifyEditorViewModel Graph\n        {\n            get => _graph;\n            internal set => SetProperty(ref _graph, value);\n        }\n\n        private ConnectorViewModel? _source;\n        public ConnectorViewModel? Source\n        {\n            get => _source;\n            set\n            {\n                if(SetProperty(ref _source, value))\n                {\n                    SetTargetOrientation();\n                }\n            }\n        }\n\n        private object? _previewTarget;\n        public object? PreviewTarget\n        {\n            get => _previewTarget;\n            set\n            {\n                if (SetProperty(ref _previewTarget, value))\n                {\n                    OnPreviewTargetChanged();\n                }\n            }\n        }\n\n        private string? _previewText;\n        public string? PreviewText\n        {\n            get => _previewText;\n            set => SetProperty(ref _previewText, value);\n        }\n\n        private Orientation _targetOrientation;\n        public Orientation TargetOrientation\n        {\n            get => _targetOrientation;\n            set => SetProperty(ref _targetOrientation, value);\n        }\n\n        protected virtual void OnPreviewTargetChanged()\n        {\n            bool canConnect = PreviewTarget != null && Graph.Schema.CanAddConnection(Source!, PreviewTarget);\n            PreviewText = PreviewTarget switch\n            {\n                ConnectorViewModel con when con == Source => $\"Can't connect to self\",\n                ConnectorViewModel con => $\"{(canConnect ? \"Connect\" : \"Can't connect\")} to {con.Title ?? \"pin\"}\",\n                FlowNodeViewModel flow => $\"{(canConnect ? \"Connect\" : \"Can't connect\")} to {flow.Title ?? \"node\"}\",\n                _ => null\n            };\n\n            SetTargetOrientation();\n        }\n\n        private void SetTargetOrientation()\n        {\n            TargetOrientation = PreviewTarget switch\n            {\n                ConnectorViewModel con when con.Node is FlowNodeViewModel flow => flow.Orientation,\n                FlowNodeViewModel flow => flow.Orientation,\n                NodifyEditorViewModel editor when Source?.Node is FlowNodeViewModel flow => flow.Orientation,\n                _ => Orientation.Horizontal,\n            };\n        }\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.Playground/Editor/VerticalNodeViewModel.cs",
    "content": "﻿using System.Windows.Controls;\n\nnamespace Nodify.Playground\n{\n    public class VerticalNodeViewModel : FlowNodeViewModel\n    {\n        public VerticalNodeViewModel()\n        {\n            Orientation = Orientation.Vertical;\n        }\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.Playground/Editor/WpfComboBox.cs",
    "content": "using System;\nusing Avalonia.Controls.Primitives;\nusing Avalonia.Input;\nusing Avalonia.VisualTree;\n\nnamespace Nodify.Playground;\n\n/// <summary>\n/// WPF's ComboBox opens popup on pointer pressed.\n/// Avalonia's ComboBox opens popup on pointer released.\n/// Avalonia behavior interferes with NodeInput which handles pointer pressed to start dragging.\n/// In order to keep the same behavior as WPF (only in the demo!), we need to override the pointer pressed and released events\n/// so that the popup is opened on pointer pressed.\n/// </summary>\npublic class WpfComboBox : ComboBox\n{\n    protected override Type StyleKeyOverride => typeof(ComboBox);\n\n    protected override void OnPointerPressed(PointerPressedEventArgs e)\n    {\n        base.OnPointerPressed(e);\n        if (!e.Handled)\n        {\n            SetCurrentValue(IsDropDownOpenProperty, !IsDropDownOpen);\n            e.Handled = true;\n        }\n    }\n    \n    protected override void OnPointerReleased(PointerReleasedEventArgs e)\n    {\n        if ((e.Source as Control)?.GetVisualRoot() is not PopupRoot)\n            e.Handled = true;\n        base.OnPointerReleased(e);\n    }\n}"
  },
  {
    "path": "Examples/Nodify.Playground/EditorInputMode.cs",
    "content": "﻿using System.Windows.Input;\n\nnamespace Nodify.Playground\n{\n    public enum EditorInputMode\n    {\n        Default,\n        PanOnly,\n        SelectOnly,\n        CutOnly\n    }\n\n    public enum EditorGesturesMappings\n    {\n        Default,\n        Custom\n    }\n\n    public static class EditorInputModeExtensions\n    {\n        public static void Apply(this EditorGestures mappings, EditorInputMode inputMode)\n        {\n            mappings.Apply(PlaygroundSettings.Instance.EditorGesturesMappings.ToGesturesMappings());\n\n            switch (inputMode)\n            {\n                case EditorInputMode.PanOnly:\n                    mappings.Editor.Selection.Apply(EditorGestures.SelectionGestures.None);\n                    mappings.Editor.Cutting.Value = MultiGesture.None;\n                    mappings.ItemContainer.Selection.Apply(EditorGestures.SelectionGestures.None);\n                    mappings.ItemContainer.Drag.Value = MultiGesture.None;\n                    mappings.Connector.Connect.Value = MultiGesture.None;\n                    break;\n                case EditorInputMode.SelectOnly:\n                    mappings.Editor.Pan.Value = MultiGesture.None;\n                    mappings.Editor.Cutting.Value = MultiGesture.None;\n                    mappings.ItemContainer.Drag.Value = MultiGesture.None;\n                    mappings.Connector.Connect.Value = MultiGesture.None;\n                    break;\n                case EditorInputMode.CutOnly:\n                    mappings.Editor.Cutting.Value = new MouseGesture(MouseAction.LeftClick);\n                    mappings.Editor.Selection.Apply(EditorGestures.SelectionGestures.None);\n                    mappings.Editor.Pan.Value = MultiGesture.None;\n                    mappings.ItemContainer.Selection.Apply(EditorGestures.SelectionGestures.None);\n                    mappings.ItemContainer.Drag.Value = MultiGesture.None;\n                    mappings.Connector.Connect.Value = MultiGesture.None;\n                    break;\n                case EditorInputMode.Default:\n                    break;\n            }\n        }\n\n        public static void Apply(this EditorGestures value, EditorGesturesMappings mappings)\n        {\n            var newMappings = mappings.ToGesturesMappings();\n            value.Apply(newMappings);\n        }\n\n        public static EditorGestures ToGesturesMappings(this EditorGesturesMappings mappings)\n        {\n            return mappings switch\n            {\n                EditorGesturesMappings.Custom => new CustomGesturesMappings(),\n                _ => new EditorGestures()\n            };\n        }\n    }\n\n    public class CustomGesturesMappings : EditorGestures\n    {\n        public CustomGesturesMappings()\n        {\n            Editor.Pan.Value = new AnyGesture(new MouseGesture(MouseAction.LeftClick), new MouseGesture(MouseAction.MiddleClick));\n            Editor.ZoomModifierKey = ModifierKeys.Control;\n            Editor.Selection.Apply(new SelectionGestures(MouseAction.RightClick));\n            // comment to drag with right click - we copy the default gestures of the item container which uses left click for selection\n            ItemContainer.Drag.Value = new AnyGesture(ItemContainer.Selection.Replace.Value, ItemContainer.Selection.Remove.Value, ItemContainer.Selection.Append.Value, ItemContainer.Selection.Invert.Value);\n            ItemContainer.Selection.Apply(Editor.Selection);\n        }\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.Playground/EditorSettings.cs",
    "content": "﻿using System.Collections.Generic;\nusing System.Windows;\n\nnamespace Nodify.Playground\n{\n    public enum ConnectionStyle\n    {\n        Default,\n        Line,\n        Circuit,\n        Step\n    }\n\n    public class EditorSettings : ObservableObject\n    {\n        private readonly IReadOnlyCollection<ISettingViewModel> _settings;\n        public IEnumerable<ISettingViewModel> Settings => PlaygroundSettings.Instance.FilterAndSort(_settings);\n\n        private readonly IReadOnlyCollection<ISettingViewModel> _advancedSettings;\n        public IEnumerable<ISettingViewModel> AdvancedSettings => PlaygroundSettings.Instance.FilterAndSort(_advancedSettings);\n\n        private EditorSettings()\n        {\n            PlaygroundSettings.Instance.PropertyChanged += OnSearchTextChanged;\n\n            _settings = new List<ISettingViewModel>()\n            {\n                new ProxySettingViewModel<bool>(\n                    () => Instance.EnableRealtimeSelection,\n                    val => Instance.EnableRealtimeSelection = val,\n                    \"Realtime selection: \",\n                    \"Selects items when finished if disabled.\"),\n                new ProxySettingViewModel<bool>(\n                    () => Instance.SelectableNodes,\n                    val => Instance.SelectableNodes = val,\n                    \"Selectable nodes: \",\n                    \"Whether nodes can be selected.\"),\n                new ProxySettingViewModel<bool>(\n                    () => Instance.DraggableNodes,\n                    val => Instance.DraggableNodes= val,\n                    \"Draggable nodes: \",\n                    \"Whether nodes can be dragged.\"),\n                new ProxySettingViewModel<bool>(\n                    () => Instance.CanSelectMultipleNodes,\n                    val => Instance.CanSelectMultipleNodes = val,\n                    \"Can select multiple nodes: \"),\n                new ProxySettingViewModel<bool>(\n                    () => Instance.EnablePendingConnectionSnapping,\n                    val => Instance.EnablePendingConnectionSnapping = val,\n                    \"Pending connection snapping: \",\n                    \"Whether to snap the pending connection to connectors\"),\n                new ProxySettingViewModel<bool>(\n                    () => Instance.EnablePendingConnectionPreview,\n                    val => Instance.EnablePendingConnectionPreview = val,\n                    \"Pending connection preview: \",\n                    \"Show information about the pending connection.\"),\n                new ProxySettingViewModel<bool>(\n                    () => Instance.AllowConnectingToConnectorsOnly,\n                    val => Instance.AllowConnectingToConnectorsOnly = val,\n                    \"Disable drop connection on node: \",\n                    \"Can connect directly to nodes if enabled\"),\n                new ProxySettingViewModel<bool>(\n                    () => Instance.DisableAutoPanning,\n                    val => Instance.DisableAutoPanning = val,\n                    \"Disable auto panning: \"),\n                new ProxySettingViewModel<bool>(\n                    () => Instance.DisablePanning,\n                    val => Instance.DisablePanning = val,\n                    \"Disable panning: \"),\n                new ProxySettingViewModel<bool>(\n                    () => Instance.DisableZooming,\n                    val => Instance.DisableZooming = val,\n                    \"Disable zooming: \"),\n                new ProxySettingViewModel<uint>(\n                    () => Instance.GridSpacing,\n                    val => Instance.GridSpacing = val,\n                    \"Grid spacing: \",\n                    \"Snapping value in pixels\"),\n                new ProxySettingViewModel<PointEditor>(\n                    () => Instance.Location,\n                    val => Instance.Location = val,\n                    \"Location: \",\n                    \"The viewport's location.\"),\n                new ProxySettingViewModel<double>(\n                    () => Instance.Zoom,\n                    val => Instance.Zoom = val,\n                    \"Zoom: \",\n                    \"The viewport's zoom. Not accurate when trying to zoom outside the MinViewportZoom and MaxViewportZoom because of dependency property coercion not updating the binding with the final result.\"),\n                new ProxySettingViewModel<double>(\n                    () => Instance.MinZoom,\n                    val => Instance.MinZoom = val,\n                    \"Min zoom: \"),\n                new ProxySettingViewModel<double>(\n                    () => Instance.MaxZoom,\n                    val => Instance.MaxZoom = val,\n                    \"Max zoom: \"),\n                new ProxySettingViewModel<double>(\n                    () => Instance.AutoPanningSpeed,\n                    val => Instance.AutoPanningSpeed = val,\n                    \"Auto panning speed: \",\n                    \"Speed value in pixels per tick\"),\n                new ProxySettingViewModel<double>(\n                    () => Instance.AutoPanningEdgeDistance,\n                    val => Instance.AutoPanningEdgeDistance = val,\n                    \"Auto panning edge distance: \",\n                    \"Distance from edge to trigger auto panning\"),\n                new ProxySettingViewModel<bool>(\n                    () => Instance.SelectableConnections,\n                    val => Instance.SelectableConnections = val,\n                    \"Selectable connections: \",\n                    \"Whether connections can be selected.\"),\n                new ProxySettingViewModel<bool>(\n                    () => Instance.CanSelectMultipleConnections,\n                    val => Instance.CanSelectMultipleConnections = val,\n                    \"Can select multiple connections: \"),\n                new ProxySettingViewModel<ConnectionStyle>(\n                    () => Instance.ConnectionStyle,\n                    val => Instance.ConnectionStyle = val,\n                    \"Connection style: \"),\n                new ProxySettingViewModel<string?>(\n                    () => Instance.ConnectionText,\n                    val => Instance.ConnectionText = val,\n                    \"Connection text: \"),\n                new ProxySettingViewModel<double>(\n                    () => Instance.CircuitConnectionAngle,\n                    val => Instance.CircuitConnectionAngle = val,\n                    \"Connection angle: \",\n                    \"Applies to circuit connection style\"),\n                new ProxySettingViewModel<double>(\n                    () => Instance.ConnectionCornerRadius,\n                    val => Instance.ConnectionCornerRadius = val,\n                    \"Connection corner radius: \",\n                    \"The corner radius between the line segments.\"),\n                new ProxySettingViewModel<double>(\n                    () => Instance.ConnectionSpacing,\n                    val => Instance.ConnectionSpacing = val,\n                    \"Connection spacing: \",\n                    \"The distance between the start point and the where the angle breaks\"),\n                new ProxySettingViewModel<PointEditor>(\n                    () => Instance.ConnectionArrowSize,\n                    val => Instance.ConnectionArrowSize = val,\n                    \"Connection arrowhead size: \",\n                    \"The size of the arrowhead.\"),\n                new ProxySettingViewModel<uint>(\n                    () => Instance.DirectionalArrowsCount,\n                    val => Instance.DirectionalArrowsCount = val,\n                    \"Directional arrows count: \",\n                    \"The number of arrowheads to draw on the line flowing in the direction of the connection.\"),\n                new ProxySettingViewModel<double>(\n                    () => Instance.DirectionalArrowsOffset,\n                    val => Instance.DirectionalArrowsOffset = val,\n                    \"Directional arrows offset: \",\n                    \"Used to animate the directional arrowheads flowing in the direction of the connection (value is between 0 and 1).\"),\n                new ProxySettingViewModel<bool>(\n                    () => Instance.IsAnimatingConnections,\n                    val => Instance.IsAnimatingConnections = val,\n                    \"Animate directional arrows: \",\n                    \"Used to animate the directional arrowheads by animating the DirectionalArrowsOffset value\"),\n                new ProxySettingViewModel<double>(\n                    () => Instance.DirectionalArrowsAnimationDuration,\n                    val => Instance.DirectionalArrowsAnimationDuration = val,\n                    \"Arrows animation duration: \",\n                    \"The duration in seconds of a directional arrowhead flowing from start to end.\"),\n                new ProxySettingViewModel<ArrowHeadEnds>(\n                    () => Instance.ArrowHeadEnds,\n                    val => Instance.ArrowHeadEnds = val,\n                    \"Connection arrowhead end: \",\n                    \"The location of the arrowhead.\"),\n                new ProxySettingViewModel<ArrowHeadShape>(\n                    () => Instance.ArrowHeadShape,\n                    val => Instance.ArrowHeadShape = val,\n                    \"Connection arrowhead shape: \",\n                    \"The shape of the arrow head.\"),\n                new ProxySettingViewModel<ConnectionOffsetMode>(\n                    () => Instance.ConnectionSourceOffsetMode,\n                    val => Instance.ConnectionSourceOffsetMode = val,\n                    \"Connection source offset mode: \"),\n                new ProxySettingViewModel<ConnectionOffsetMode>(\n                    () => Instance.ConnectionTargetOffsetMode,\n                    val => Instance.ConnectionTargetOffsetMode = val,\n                    \"Connection target offset mode: \"),\n                new ProxySettingViewModel<PointEditor>(\n                    () => Instance.ConnectionSourceOffset,\n                    val => Instance.ConnectionSourceOffset = val,\n                    \"Connection source offset: \"),\n                new ProxySettingViewModel<PointEditor>(\n                    () => Instance.ConnectionTargetOffset,\n                    val => Instance.ConnectionTargetOffset = val,\n                    \"Connection target offset: \"),\n                new ProxySettingViewModel<bool>(\n                    () => Instance.DisplayConnectionsOnTop,\n                    val => Instance.DisplayConnectionsOnTop = val,\n                    \"Display connections on top: \"),\n                new ProxySettingViewModel<double>(\n                    () => Instance.BringIntoViewSpeed,\n                    val => Instance.BringIntoViewSpeed = val,\n                    \"Bring into view speed: \",\n                    \"Bring location into view animation speed in pixels per second\"),\n                new ProxySettingViewModel<double>(\n                    () => Instance.BringIntoViewMaxDuration,\n                    val => Instance.BringIntoViewMaxDuration = val,\n                    \"Bring into view max duration: \",\n                    \"Bring location into view max animation duration\"),\n                new ProxySettingViewModel<GroupingMovementMode>(\n                    () => Instance.GroupingNodeMovement,\n                    val => Instance.GroupingNodeMovement = val,\n                    \"Grouping node movement: \",\n                    \"Whether the grouping node is sticky or not\"),\n            };\n\n            _advancedSettings = new List<ISettingViewModel>()\n            {\n                new ProxySettingViewModel<double>(\n                    () => Instance.HandleRightClickAfterPanningThreshold,\n                    val => Instance.HandleRightClickAfterPanningThreshold = val,\n                    \"Disable context menu after panning: \",\n                    \"Disable after mouse moved this far\"),\n                new ProxySettingViewModel<double>(\n                    () => Instance.AutoPanningTickRate,\n                    val => Instance.AutoPanningTickRate = val,\n                    \"Auto panning tick rate: \",\n                    \"How often is the new position calculated in milliseconds. Disable and enable auto panning for this to have effect.\"),\n                new ProxySettingViewModel<bool>(\n                    () => Instance.AllowCuttingCancellation,\n                    val => Instance.AllowCuttingCancellation = val,\n                    \"Allow cutting cancellation: \",\n                    \"Right click to cancel cutting.\"),\n                new ProxySettingViewModel<bool>(\n                    () => Instance.AllowDraggingCancellation,\n                    val => Instance.AllowDraggingCancellation = val,\n                    \"Allow dragging cancellation: \",\n                    \"Right click to cancel dragging.\"),\n                new ProxySettingViewModel<bool>(\n                    () => Instance.AllowPendingConnectionCancellation,\n                    val => Instance.AllowPendingConnectionCancellation = val,\n                    \"Allow pending connection cancellation: \",\n                    \"Right click to cancel pending connection.\"),\n                new ProxySettingViewModel<bool>(\n                    () => Instance.EnableSnappingCorrection,\n                    val => Instance.EnableSnappingCorrection = val,\n                    \"Enable snapping correction: \",\n                    \"Correct the final position when moving a selection\"),\n                new ProxySettingViewModel<bool>(\n                    () => Instance.EnableCuttingLinePreview,\n                    val => Instance.EnableCuttingLinePreview = val,\n                    \"Enable cutting line preview: \",\n                    \"Applies custom connection style on intersection (hurts performance due to hit testing).\"),\n                new ProxySettingViewModel<bool>(\n                    () => Instance.EnableConnectorOptimizations,\n                    val => Instance.EnableConnectorOptimizations = val,\n                    \"Enable connector optimizations: \",\n                    \"Enables optimizations for connectors based on viewport distance and minimum selected nodes.\"),\n                new ProxySettingViewModel<double>(\n                    () => Instance.OptimizeSafeZone,\n                    val => Instance.OptimizeSafeZone = val,\n                    \"Optimize connectors safe zone: \",\n                    \"The minimum distance from the viewport where connectors will start optimizing\"),\n                new ProxySettingViewModel<uint>(\n                    () => Instance.OptimizeMinimumSelectedItems,\n                    val => Instance.OptimizeMinimumSelectedItems = val,\n                    \"Optimize connectors minimum selection: \",\n                    \"The minimum selected items needed to start optimizing when outside the safe zone.\"),\n                new ProxySettingViewModel<bool>(\n                    () => Instance.EnableRenderingOptimizations,\n                    val => Instance.EnableRenderingOptimizations = val,\n                    \"Enable nodes rendering optimization: \",\n                    \"Enables rendering optimizations for nodes based on zoom out percent and nodes count. (zoom in/out to apply optimization)\"),\n                new ProxySettingViewModel<double>(\n                    () => Instance.OptimizeRenderingZoomOutPercent,\n                    val => Instance.OptimizeRenderingZoomOutPercent = val,\n                    \"Rendering optimization zoom out percent: \",\n                    \"The zoom out percent that triggers the optimization. (percent of x = 1 - MinViewportZoom)\"),\n                new ProxySettingViewModel<uint>(\n                    () => Instance.OptimizeRenderingMinimumNodes,\n                    val => Instance.OptimizeRenderingMinimumNodes = val,\n                    \"Rendering optimization minimum nodes: \",\n                    \"The minimum nodes needed to start optimizing when zoom out percent is met.\"),\n                new ProxySettingViewModel<bool>(\n                    () => Instance.EnableDraggingOptimizations,\n                    val => Instance.EnableDraggingOptimizations = val,\n                    \"Enable nodes dragging optimizations: \",\n                    \"Simulates dragging visually but only commits the changes at the end.\"),\n                new ProxySettingViewModel<double>(\n                    () => Instance.FitToScreenExtentMargin,\n                    val => Instance.FitToScreenExtentMargin = val,\n                    \"Fit to screen extent margin: \",\n                    \"Adds some margin to the nodes extent when fit to screen\"),\n                new ProxySettingViewModel<bool>(\n                    () => Instance.EnableStickyConnectors,\n                    val => Instance.EnableStickyConnectors = val,\n                    \"Enable sticky connectors: \",\n                    \"The connection can be completed in two steps (e.g. click to create pending connection, click to connect)\"),\n            };\n\n            EnableCuttingLinePreview = true;\n        }\n\n        private void OnSearchTextChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)\n        {\n            if (e.PropertyName == nameof(PlaygroundSettings.SearchText))\n            {\n                OnPropertyChanged(nameof(Settings));\n                OnPropertyChanged(nameof(AdvancedSettings));\n            }\n        }\n\n        public static EditorSettings Instance { get; } = new EditorSettings();\n\n        #region Default settings\n\n        private bool _enablePendingConnectionSnapping = true;\n        public bool EnablePendingConnectionSnapping\n        {\n            get => _enablePendingConnectionSnapping;\n            set => SetProperty(ref _enablePendingConnectionSnapping, value);\n        }\n\n        private bool _enablePendingConnectionPreview = true;\n        public bool EnablePendingConnectionPreview\n        {\n            get => _enablePendingConnectionPreview;\n            set => SetProperty(ref _enablePendingConnectionPreview, value);\n        }\n\n        private bool _allowConnectingToConnectorsOnly;\n        public bool AllowConnectingToConnectorsOnly\n        {\n            get => _allowConnectingToConnectorsOnly;\n            set => SetProperty(ref _allowConnectingToConnectorsOnly, value);\n        }\n\n        private bool _realtimeSelection = true;\n        public bool EnableRealtimeSelection\n        {\n            get => _realtimeSelection;\n            set => SetProperty(ref _realtimeSelection, value);\n        }\n\n        private bool _disableAutoPanning = false;\n        public bool DisableAutoPanning\n        {\n            get => _disableAutoPanning;\n            set => SetProperty(ref _disableAutoPanning, value);\n        }\n\n        private double _autoPanningSpeed = 15d;\n        public double AutoPanningSpeed\n        {\n            get => _autoPanningSpeed;\n            set => SetProperty(ref _autoPanningSpeed, value);\n        }\n\n        private double _autoPanningEdgeDistance = 15d;\n        public double AutoPanningEdgeDistance\n        {\n            get => _autoPanningEdgeDistance;\n            set => SetProperty(ref _autoPanningEdgeDistance, value);\n        }\n\n        private bool _disablePanning = false;\n        public bool DisablePanning\n        {\n            get => _disablePanning;\n            set => SetProperty(ref _disablePanning, value);\n        }\n\n        private bool _disableZooming = false;\n        public bool DisableZooming\n        {\n            get => _disableZooming;\n            set => SetProperty(ref _disableZooming, value);\n        }\n\n        private uint _gridSpacing = 15u;\n        public uint GridSpacing\n        {\n            get => _gridSpacing;\n            set => SetProperty(ref _gridSpacing, value);\n        }\n\n        private double _minZoom = 0.1;\n        public double MinZoom\n        {\n            get => _minZoom;\n            set => SetProperty(ref _minZoom, value);\n        }\n\n        private double _maxZoom = 2;\n        public double MaxZoom\n        {\n            get => _maxZoom;\n            set => SetProperty(ref _maxZoom, value);\n        }\n\n        private double _zoom = 1;\n        public double Zoom\n        {\n            get => _zoom;\n            set => SetProperty(ref _zoom, value);\n        }\n\n        private PointEditor _location = new PointEditor();\n        public PointEditor Location\n        {\n            get => _location;\n            set => SetProperty(ref _location, value);\n        }\n\n        private bool _selectableConnections = true;\n        public bool SelectableConnections\n        {\n            get => _selectableConnections;\n            set => SetProperty(ref _selectableConnections, value);\n        }\n\n        private bool _canSelectMultipleConnections = true;\n        public bool CanSelectMultipleConnections\n        {\n            get => _canSelectMultipleConnections;\n            set => SetProperty(ref _canSelectMultipleConnections, value);\n        }\n\n        private bool _draggableNodes = true;\n        public bool DraggableNodes\n        {\n            get => _draggableNodes;\n            set => SetProperty(ref _draggableNodes, value);\n        }\n\n        private bool _selectableNodes = true;\n        public bool SelectableNodes\n        {\n            get => _selectableNodes;\n            set => SetProperty(ref _selectableNodes, value);\n        }\n\n        private bool _canSelectMultipleNodes = true;\n        public bool CanSelectMultipleNodes\n        {\n            get => _canSelectMultipleNodes;\n            set => SetProperty(ref _canSelectMultipleNodes, value);\n        }\n\n        private ConnectionStyle _connectionStyle;\n        public ConnectionStyle ConnectionStyle\n        {\n            get => _connectionStyle;\n            set => SetProperty(ref _connectionStyle, value);\n        }\n\n        private string? _connectionText;\n        public string? ConnectionText\n        {\n            get => _connectionText;\n            set => SetProperty(ref _connectionText, value);\n        }\n\n        private double _circuitConnectionAngle = 45;\n        public double CircuitConnectionAngle\n        {\n            get => _circuitConnectionAngle;\n            set => SetProperty(ref _circuitConnectionAngle, value);\n        }\n\n        private double _connectionCornerRadius = 10;\n        public double ConnectionCornerRadius\n        {\n            get => _connectionCornerRadius;\n            set => SetProperty(ref _connectionCornerRadius, value);\n        }\n\n        private double _connectionSpacing = 20;\n        public double ConnectionSpacing\n        {\n            get => _connectionSpacing;\n            set => SetProperty(ref _connectionSpacing, value);\n        }\n\n        private ConnectionOffsetMode _srcConnectionOffsetMode = ConnectionOffsetMode.Static;\n        public ConnectionOffsetMode ConnectionSourceOffsetMode\n        {\n            get => _srcConnectionOffsetMode;\n            set => SetProperty(ref _srcConnectionOffsetMode, value);\n        }\n\n        private ConnectionOffsetMode _targetConnectionOffsetMode = ConnectionOffsetMode.Static;\n        public ConnectionOffsetMode ConnectionTargetOffsetMode\n        {\n            get => _targetConnectionOffsetMode;\n            set => SetProperty(ref _targetConnectionOffsetMode, value);\n        }\n\n        private ArrowHeadEnds _arrowHeadEnds = ArrowHeadEnds.End;\n        public ArrowHeadEnds ArrowHeadEnds\n        {\n            get => _arrowHeadEnds;\n            set => SetProperty(ref _arrowHeadEnds, value);\n        }\n\n        private ArrowHeadShape _arrowHeadShape = ArrowHeadShape.Arrowhead;\n        public ArrowHeadShape ArrowHeadShape\n        {\n            get => _arrowHeadShape;\n            set => SetProperty(ref _arrowHeadShape, value);\n        }\n\n        private PointEditor _connectionSourceOffset = new Size(14, 0);\n        public PointEditor ConnectionSourceOffset\n        {\n            get => _connectionSourceOffset;\n            set => SetProperty(ref _connectionSourceOffset, value);\n        }\n\n        private PointEditor _connectionTargetOffset = new Size(14, 0);\n        public PointEditor ConnectionTargetOffset\n        {\n            get => _connectionTargetOffset;\n            set => SetProperty(ref _connectionTargetOffset, value);\n        }\n\n        private uint _directionalArrowsCount = 3;\n        public uint DirectionalArrowsCount\n        {\n            get => _directionalArrowsCount;\n            set => SetProperty(ref _directionalArrowsCount, value);\n        }\n\n        private double _directionalArrowsOffset;\n        public double DirectionalArrowsOffset\n        {\n            get => _directionalArrowsOffset;\n            set => SetProperty(ref _directionalArrowsOffset, value);\n        }\n\n        private bool _isAnimatingConnections;\n        public bool IsAnimatingConnections\n        {\n            get => _isAnimatingConnections;\n            set => SetProperty(ref _isAnimatingConnections, value);\n        }\n\n        private double _directionalArrowsAnimationDuration = 2.0;\n        public double DirectionalArrowsAnimationDuration\n        {\n            get => _directionalArrowsAnimationDuration;\n            set => SetProperty(ref _directionalArrowsAnimationDuration, value);\n        }\n\n        private PointEditor _connectionArrowSize = new Size(8, 8);\n        public PointEditor ConnectionArrowSize\n        {\n            get => _connectionArrowSize;\n            set => SetProperty(ref _connectionArrowSize, value);\n        }\n\n        private bool _displayConnectionsOnTop;\n        public bool DisplayConnectionsOnTop\n        {\n            get => _displayConnectionsOnTop;\n            set => SetProperty(ref _displayConnectionsOnTop, value);\n        }\n\n        private double _bringIntoViewSpeed = 1000;\n        public double BringIntoViewSpeed\n        {\n            get => _bringIntoViewSpeed;\n            set => SetProperty(ref _bringIntoViewSpeed, value);\n        }\n\n        private double _bringIntoViewMaxDuration = 1;\n        public double BringIntoViewMaxDuration\n        {\n            get => _bringIntoViewMaxDuration;\n            set => SetProperty(ref _bringIntoViewMaxDuration, value);\n        }\n\n        private GroupingMovementMode _groupingNodeMovement;\n        public GroupingMovementMode GroupingNodeMovement\n        {\n            get => _groupingNodeMovement;\n            set => SetProperty(ref _groupingNodeMovement, value);\n        }\n\n        #endregion\n\n        #region Advanced settings\n\n        public double HandleRightClickAfterPanningThreshold\n        {\n            get => NodifyEditor.HandleRightClickAfterPanningThreshold;\n            set => NodifyEditor.HandleRightClickAfterPanningThreshold = value;\n        }\n\n        public double AutoPanningTickRate\n        {\n            get => NodifyEditor.AutoPanningTickRate;\n            set => NodifyEditor.AutoPanningTickRate = value;\n        }\n\n        public bool AllowCuttingCancellation\n        {\n            get => CuttingLine.AllowCuttingCancellation;\n            set => CuttingLine.AllowCuttingCancellation = value;\n        }\n\n        public bool AllowDraggingCancellation\n        {\n            get => ItemContainer.AllowDraggingCancellation;\n            set => ItemContainer.AllowDraggingCancellation = value;\n        }\n\n        public bool AllowPendingConnectionCancellation\n        {\n            get => Connector.AllowPendingConnectionCancellation;\n            set => Connector.AllowPendingConnectionCancellation = value;\n        }\n\n        public bool EnableSnappingCorrection\n        {\n            get => NodifyEditor.EnableSnappingCorrection;\n            set => NodifyEditor.EnableSnappingCorrection = value;\n        }\n\n        public bool EnableCuttingLinePreview\n        {\n            get => NodifyEditor.EnableCuttingLinePreview;\n            set => NodifyEditor.EnableCuttingLinePreview = value;\n        }\n\n        public bool EnableConnectorOptimizations\n        {\n            get => Connector.EnableOptimizations;\n            set => Connector.EnableOptimizations = value;\n        }\n\n        public double OptimizeSafeZone\n        {\n            get => Connector.OptimizeSafeZone;\n            set => Connector.OptimizeSafeZone = value;\n        }\n\n        public uint OptimizeMinimumSelectedItems\n        {\n            get => Connector.OptimizeMinimumSelectedItems;\n            set => Connector.OptimizeMinimumSelectedItems = value;\n        }\n\n        public bool EnableRenderingOptimizations\n        {\n            get => NodifyEditor.EnableRenderingContainersOptimizations;\n            set => NodifyEditor.EnableRenderingContainersOptimizations = value;\n        }\n\n        public uint OptimizeRenderingMinimumNodes\n        {\n            get => NodifyEditor.OptimizeRenderingMinimumContainers;\n            set => NodifyEditor.OptimizeRenderingMinimumContainers = value;\n        }\n\n        public double OptimizeRenderingZoomOutPercent\n        {\n            get => NodifyEditor.OptimizeRenderingZoomOutPercent;\n            set => NodifyEditor.OptimizeRenderingZoomOutPercent = value;\n        }\n\n        public double FitToScreenExtentMargin\n        {\n            get => NodifyEditor.FitToScreenExtentMargin;\n            set => NodifyEditor.FitToScreenExtentMargin = value;\n        }\n\n        public bool EnableDraggingOptimizations\n        {\n            get => NodifyEditor.EnableDraggingContainersOptimizations;\n            set => NodifyEditor.EnableDraggingContainersOptimizations = value;\n        }\n\n        public bool EnableStickyConnectors\n        {\n            get => Connector.EnableStickyConnections;\n            set => Connector.EnableStickyConnections = value;\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.Playground/EditorSettingsView.xaml",
    "content": "﻿<UserControl x:Class=\"Nodify.Playground.EditorSettingsView\"\n             xmlns=\"https://github.com/avaloniaui\"\n             xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n             xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\"\n             xmlns:d=\"http://schemas.microsoft.com/expression/blend/2008\"\n             xmlns:local=\"clr-namespace:Nodify.Playground\"\n             xmlns:shared=\"clr-namespace:Nodify;assembly=Nodify.Shared\"\n             d:Foreground=\"{DynamicResource ForegroundBrush}\"\n             d:Background=\"{DynamicResource PanelBackgroundBrush}\"\n             mc:Ignorable=\"d\">\n\n    <StackPanel>\n        <Border BorderThickness=\"1\"\n                Padding=\"10\"\n                HorizontalAlignment=\"Stretch\">\n            <local:SettingsView Items=\"{Binding Source={x:Static local:EditorSettings.Instance}, Path=Settings}\"/>\n        </Border>\n        <Expander\n            Header=\"Advanced\"\n            Padding=\"0 5 0 0\"\n            BorderThickness=\"0 0 0 1\"\n            IsExpanded=\"True\"\n            BorderBrush=\"{DynamicResource BackgroundBrush}\">\n            <Expander.Theme>\n                <ControlTheme TargetType=\"{x:Type Expander}\"\n                       BasedOn=\"{StaticResource {x:Type Expander}}\">\n                    <Setter Property=\"Tag\"\n                            Value=\"{StaticResource ExpandRightIcon}\" />\n                    <Setter Property=\"(Interaction.Behaviors)\">\n                        <BehaviorCollectionTemplate>\n                            <BehaviorCollection>\n                                <DataTrigger Property=\"IsExpanded\"\n                                             UseDataContext=\"False\"\n                                             Value=\"True\">\n                                    <PropertySetter Property=\"Tag\"\n                                                    Value=\"{StaticResource ExpandDownIcon}\" />\n                                </DataTrigger>\n                            </BehaviorCollection>\n                        </BehaviorCollectionTemplate>\n                    </Setter>\n                </ControlTheme>\n            </Expander.Theme>\n\n            <Border BorderThickness=\"1\"\n                    Padding=\"10\"\n                    HorizontalAlignment=\"Stretch\">\n                <local:SettingsView Items=\"{Binding Source={x:Static local:EditorSettings.Instance}, Path=AdvancedSettings}\"/>\n            </Border>\n        </Expander>\n    </StackPanel>\n\n</UserControl>"
  },
  {
    "path": "Examples/Nodify.Playground/EditorSettingsView.xaml.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Text;\nusing System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Data;\nusing System.Windows.Documents;\nusing System.Windows.Input;\nusing System.Windows.Media;\nusing System.Windows.Media.Imaging;\nusing System.Windows.Navigation;\nusing System.Windows.Shapes;\n\nnamespace Nodify.Playground\n{\n    /// <summary>\n    /// Interaction logic for EditorSettingsView.xaml\n    /// </summary>\n    public partial class EditorSettingsView : UserControl\n    {\n        public EditorSettingsView()\n        {\n            InitializeComponent();\n        }\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.Playground/GlobalUsings.cs",
    "content": "global using Avalonia.Controls;\nglobal using Avalonia;\nglobal using Avalonia.Data.Converters;\nglobal using Avalonia.Markup.Xaml;\nglobal using System.Windows.Input;\nglobal using Avalonia.Interactivity;\nglobal using Avalonia.Controls.ApplicationLifetimes;\nglobal using Avalonia.Media;\nglobal using Avalonia.Threading;\nglobal using Avalonia.Layout;\nglobal using ModifierKeys = Avalonia.Input.KeyModifiers;\nglobal using Nodify.Compatibility;"
  },
  {
    "path": "Examples/Nodify.Playground/Helpers/NodeViewModelExtensions.cs",
    "content": "﻿using System.Collections.Generic;\nusing System.Windows;\n\nnamespace Nodify.Playground\n{\n    public static class NodeViewModelExtensions\n    {\n        public static Rect GetBoundingBox(this IList<NodeViewModel> nodes, double padding = 0, int gridCellSize = 15)\n        {\n            double minX = double.MaxValue;\n            double minY = double.MaxValue;\n\n            double maxX = double.MinValue;\n            double maxY = double.MinValue;\n\n            for (int i = 0; i < nodes.Count; i++)\n            {\n                var node = nodes[i];\n                var width = 200;    //node.Width\n                var height = 200;   //node.Height\n\n                if (node.Location.X < minX)\n                {\n                    minX = node.Location.X;\n                }\n\n                if (node.Location.Y < minY)\n                {\n                    minY = node.Location.Y;\n                }\n\n                var sizeX = node.Location.X + width;\n                if (sizeX > maxX)\n                {\n                    maxX = sizeX;\n                }\n\n                var sizeY = node.Location.Y + height;\n                if (sizeY > maxY)\n                {\n                    maxY = sizeY;\n                }\n            }\n\n            var result = new Rect(minX - padding, minY - padding, maxX - minX + padding * 2, maxY - minY + padding * 2);\n            result = new Rect(\n                (int)result.X / gridCellSize * gridCellSize,\n                (int)result.Y / gridCellSize * gridCellSize,\n                result.Width,\n                result.Height\n            );\n            return result;\n        }\n\n        public static void AddRange<T>(this ICollection<T> col, IEnumerable<T> items)\n        {\n            if (items is IList<T> itemsCol)\n            {\n                for (int i = 0; i < itemsCol.Count; i++)\n                {\n                    col.Add(itemsCol[i]);\n                }\n            }\n            else if (items is T[] itemsArr)\n            {\n                for (int i = 0; i < itemsArr.Length; i++)\n                {\n                    col.Add(itemsArr[i]);\n                }\n            }\n            else\n            {\n                foreach (var item in items)\n                {\n                    col.Add(item);\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.Playground/Helpers/RandomNodesGenerator.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Windows;\n\nnamespace Nodify.Playground\n{\n    public struct NodesGeneratorSettings\n    {\n        private static readonly Random _rand = new Random();\n\n        public NodesGeneratorSettings(uint count)\n        {\n            GridSnap = 15;\n            MinNodesCount = MaxNodesCount = count;\n            MinInputCount = MinOutputCount = 0;\n            MaxInputCount = MaxOutputCount = 7;\n\n            ConnectorNameGenerator = (s, i) => $\"{new string('C', (int)i % 5)} {i}\";\n            NodeNameGenerator = (s, i) => $\"Node {i}\";\n            NodeLocationGenerator = (s, i) =>\n            {\n                static double EaseOut(double percent, double increment, double start, double end, double total)\n                    => -end * (increment /= total) * (increment - 2) + start;\n\n                var xDistanceBetweenNodes = _rand.Next(150, 350);\n                var yDistanceBetweenNodes = _rand.Next(200, 350);\n                var randSignX = _rand.Next(0, 100) > 50 ? 1 : -1;\n                var randSignY = _rand.Next(0, 100) > 50 ? 1 : -1;\n                var gridOffsetX = i * xDistanceBetweenNodes;\n                var gridOffsetY = i * yDistanceBetweenNodes;\n\n                var x = gridOffsetX * Math.Sin(xDistanceBetweenNodes * randSignX / (i + 1));\n                var y = gridOffsetY * Math.Sin(yDistanceBetweenNodes * randSignY / (i + 1));\n                var easeX = x * EaseOut(i / count, i, 1, 0.01, count);\n                var easeY = y * EaseOut(i / count, i, 1, 0.01, count);\n\n                x = s.Snap((int)easeX);\n                y = s.Snap((int)easeY);\n\n                return new Point(x, y);\n            };\n        }\n\n        public uint GridSnap;\n        public uint MinNodesCount;\n        public uint MaxNodesCount;\n        public uint MinInputCount;\n        public uint MaxInputCount;\n        public uint MinOutputCount;\n        public uint MaxOutputCount;\n\n        public Func<NodesGeneratorSettings, uint, string?> ConnectorNameGenerator;\n        public Func<NodesGeneratorSettings, uint, string?> NodeNameGenerator;\n        public Func<NodesGeneratorSettings, uint, Point> NodeLocationGenerator;\n\n        public int Snap(int x)\n            => x / (int)GridSnap * (int)GridSnap;\n    }\n\n    public static class RandomNodesGenerator\n    {\n        private static readonly Random _rand = new Random();\n\n        public static List<T> GenerateNodes<T>(NodesGeneratorSettings settings)\n            where T : FlowNodeViewModel, new()\n        {\n            var nodes = new List<T>();\n            var count = _rand.Next((int)settings.MinNodesCount, (int)settings.MaxNodesCount + 1);\n\n            for (uint i = 0; i < count; i++)\n            {\n                var node = new T\n                {\n                    Title = settings.NodeNameGenerator(settings, i),\n                    Location = settings.NodeLocationGenerator(settings, i)\n                };\n\n                nodes.Add(node);\n                node.Input.AddRange(GenerateConnectors(settings, _rand.Next((int)settings.MinInputCount, (int)settings.MaxInputCount + 1)));\n                node.Output.AddRange(GenerateConnectors(settings, _rand.Next((int)settings.MinOutputCount, (int)settings.MaxOutputCount + 1)));\n            }\n\n            return nodes;\n        }\n\n        public static List<ConnectionViewModel> GenerateConnections(IList<NodeViewModel> nodes)\n        {\n            HashSet<NodeViewModel> visited = new HashSet<NodeViewModel>(nodes.Count);\n            List<ConnectionViewModel> connections = new List<ConnectionViewModel>(nodes.Count);\n\n            for (uint i = 0; i < nodes.Count; i++)\n            {\n                var n1 = nodes[_rand.Next(0, nodes.Count)];\n                var n2 = nodes[_rand.Next(0, nodes.Count)];\n\n                if (n1 == n2 && !(visited.Add(n1) && visited.Add(n2)))\n                {\n                    continue;\n                }\n\n                List<ConnectorViewModel> input = n1 is FlowNodeViewModel flow ? flow.Input.ToList() :\n                                                 n1 is KnotNodeViewModel knot ? new List<ConnectorViewModel> { knot.Connector } : new List<ConnectorViewModel>();\n\n                List<ConnectorViewModel> output = n2 is FlowNodeViewModel flow2 ? flow2.Output.ToList() :\n                                                  n2 is KnotNodeViewModel knot2 ? new List<ConnectorViewModel> { knot2.Connector } : new List<ConnectorViewModel>();\n\n                connections.AddRange(ConnectPins(input, output));\n            }\n\n            return connections;\n        }\n\n        public static List<ConnectionViewModel> ConnectPins(IList<ConnectorViewModel> source, IList<ConnectorViewModel> target)\n        {\n            Dictionary<ConnectorViewModel, List<ConnectorViewModel>> connections = new Dictionary<ConnectorViewModel, List<ConnectorViewModel>>();\n            List<ConnectionViewModel> result = new List<ConnectionViewModel>();\n\n            for (int di = 0; di < target.Count; di++)\n            {\n                if (source.Count > 1 && target.Count > 1 && _rand.Next() % 2 == 0)\n                {\n                    continue;\n                }\n\n                var outP = target[di];\n\n                if (!connections.TryGetValue(outP, out var outConns))\n                {\n                    var newList = new List<ConnectorViewModel>();\n                    connections.Add(outP, newList);\n                    outConns = newList;\n                }\n\n                var conNum = _rand.Next(0, source.Count + 1);\n                for (uint ci = 0; ci < conNum; ci++)\n                {\n                    var inP = source[_rand.Next(0, conNum)];\n\n                    if (!connections.TryGetValue(inP, out var inConns))\n                    {\n                        var newList = new List<ConnectorViewModel>();\n                        connections.Add(inP, newList);\n                        inConns = newList;\n                    }\n\n                    if (!connections[inP].Contains(outP) && !connections[outP].Contains(inP))\n                    {\n                        var isInput = inP.Flow == ConnectorFlow.Input;\n\n                        var connection = new ConnectionViewModel\n                        {\n                            Input = isInput ? inP : outP,\n                            Output = isInput ? outP : inP\n                        };\n                        result.Add(connection);\n\n                        inConns.Add(outP);\n                        outConns.Add(inP);\n                    }\n                }\n            }\n\n            return result;\n        }\n\n        public static List<ConnectorViewModel> GenerateConnectors(NodesGeneratorSettings settings, int count)\n        {\n            var list = new List<ConnectorViewModel>(count);\n\n            for (uint i = 0; i < count; i++)\n            {\n                int shapeVal = _rand.Next() % 3;\n                var connector = new ConnectorViewModel\n                {\n                    Title = settings.ConnectorNameGenerator(settings, i),\n                    Shape = PlaygroundSettings.Instance.UseCustomConnectors ? (ConnectorShape)shapeVal : ConnectorShape.Circle\n                };\n\n                list.Add(connector);\n            }\n\n            return list;\n        }\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.Playground/ISettingViewModel.cs",
    "content": "﻿namespace Nodify.Playground\n{\n    public enum SettingsType\n    {\n        Boolean,\n        Number,\n        Option,\n        Point,\n        Text\n    }\n\n    public interface ISettingViewModel\n    {\n        string Name { get; }\n        \n        /// <summary>\n        /// Represents the content within the tooltip.\n        /// </summary>\n        string? Description { get; }\n        object? Value { get; set; }\n\n        SettingsType Type { get;}\n    }\n}"
  },
  {
    "path": "Examples/Nodify.Playground/MainWindow.xaml",
    "content": "﻿<Window x:Class=\"Nodify.Playground.MainWindow\"\n        xmlns=\"https://github.com/avaloniaui\"\n        xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n        xmlns:d=\"http://schemas.microsoft.com/expression/blend/2008\"\n        xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\"\n        xmlns:local=\"clr-namespace:Nodify.Playground\"\n        xmlns:shared=\"clr-namespace:Nodify;assembly=Nodify.Shared\"\n        xmlns:nodify=\"https://miroiu.github.io/nodify\"\n        mc:Ignorable=\"d\"\n        Title=\"MainWindow\"\n        Height=\"700\"\n        Width=\"1300\">\n\n    <Window.Resources>\n        <shared:DebugConverter x:Key=\"DebugConverter\" />\n        <shared:ToStringConverter x:Key=\"ToStringConverter\" />\n    </Window.Resources>\n\n    <Window.DataContext>\n        <local:PlaygroundViewModel />\n    </Window.DataContext>\n\n    <Grid>\n        <Grid.RowDefinitions>\n            <RowDefinition Height=\"Auto\" />\n            <RowDefinition />\n            <RowDefinition Height=\"Auto\" />\n        </Grid.RowDefinitions>\n\n        <local:NodifyEditorView x:Name=\"EditorView\"\n                                DataContext=\"{Binding GraphViewModel}\"\n                                Grid.RowSpan=\"3\" />\n\n        <!--ACTIONS-->\n        <Border VerticalAlignment=\"Top\"\n                Background=\"{DynamicResource PanelBackgroundBrush}\"\n                Padding=\"10\">\n            <Grid>\n                <Grid.ColumnDefinitions>\n                    <ColumnDefinition />\n                    <ColumnDefinition Width=\"Auto\" />\n                </Grid.ColumnDefinitions>\n                <StackPanel Orientation=\"Horizontal\">\n                    <Button Command=\"{Binding GenerateRandomNodesCommand}\"\n                            Content=\"GENERATE NODES\"\n                            ToolTip.Tip=\"Generate nodes using the specified settings.\"\n                            Theme=\"{StaticResource HollowButton}\" />\n                    <Button Command=\"{Binding ToggleConnectionsCommand}\"\n                            Content=\"{Binding ConnectNodesText}\"\n                            ToolTip.Tip=\"Will add new connections if Connect Nodes is checked, otherwise it will disconnect nodes.\"\n                            Theme=\"{StaticResource HollowButton}\" />\n                    <Button Command=\"{Binding PerformanceTestCommand}\"\n                            Content=\"PERFORMANCE TEST\"\n                            ToolTip.Tip=\"You will encounter rendering performance issues. Try disabling the connections to see the difference.\"\n                            Theme=\"{StaticResource HollowButton}\" />\n                    <Button Command=\"{Binding ResetCommand}\"\n                            Content=\"RESET PLAYGROUND\"\n                            ToolTip.Tip=\"Reset the Location, Zoom, Nodes and Connections.\"\n                            Theme=\"{StaticResource HollowButton}\" />\n                    <Button Click=\"BringIntoView_Click\"\n                            Content=\"BRING INTO VIEW\"\n                            ToolTip.Tip=\"Bring a random node into view.\"\n                            Theme=\"{StaticResource HollowButton}\" />\n                    <WpfBtn Command=\"{x:Static nodify:EditorCommands.FitToScreen}\"\n                            Content=\"FIT TO SCREEN\"\n                            CommandTarget=\"{Binding EditorInstance, ElementName=EditorView}\"\n                            ToolTip.Tip=\"Scales the viewport to fit all nodes if that's possible.\"\n                            Theme=\"{StaticResource HollowButton}\" />\n                    <Button Command=\"{Binding GraphViewModel.CommentSelectionCommand}\"\n                            Content=\"COMMENT SELECTION\"\n                            ToolTip.Tip=\"Creates a comment node containing the selected nodes.\"\n                            Theme=\"{StaticResource HollowButton}\" />\n                    <Button Click=\"AnimateConnections_Click\"\n                            Content=\"TOGGLE CONNECTIONS ANIMATION\"\n                            ToolTip.Tip=\"Starts or stops animating the directional arrows on all connections (see DirectionalArrowsCount)\"\n                            Theme=\"{StaticResource HollowButton}\" />\n                </StackPanel>\n\n                <Button Theme=\"{StaticResource IconButton}\"\n                        Content=\"{StaticResource ThemeIcon}\"\n                        Command=\"{Binding Source={x:Static shared:ThemeManager.SetNextThemeCommand}}\"\n                        ToolTip.Tip=\"Change theme\"\n                        Grid.Column=\"1\" />\n            </Grid>\n        </Border>\n\n        <!--SETTINGS-->\n        <Expander Grid.Row=\"1\"\n                  HorizontalContentAlignment=\"Left\"\n                  VerticalContentAlignment=\"Center\"\n                  HorizontalAlignment=\"Left\"\n                  Background=\"{DynamicResource PanelBackgroundBrush}\"\n                  IsExpanded=\"True\"\n                  ExpandDirection=\"Left\"\n                  Padding=\"0 1 4 3\">\n            <Expander.Theme>\n                <ControlTheme TargetType=\"{x:Type Expander}\"\n                       BasedOn=\"{StaticResource {x:Type Expander}}\">\n                    <Setter Property=\"Tag\"\n                            Value=\"{StaticResource ExpandRightIcon}\" />\n                    <Setter Property=\"(Interaction.Behaviors)\">\n                        <BehaviorCollectionTemplate>\n                            <BehaviorCollection>\n                                <DataTrigger Property=\"IsExpanded\" \n                                             UseDataContext=\"False\"\n                                             Value=\"True\">\n                                    <PropertySetter Property=\"Tag\"\n                                            Value=\"{StaticResource ExpandLeftIcon}\" />\n                                </DataTrigger>\n                            </BehaviorCollection>\n                        </BehaviorCollectionTemplate>\n                    </Setter>\n                </ControlTheme>\n            </Expander.Theme>\n\n            <Grid>\n                <Grid.RowDefinitions>\n                    <RowDefinition Height=\"Auto\" />\n                    <RowDefinition Height=\"*\" />\n                </Grid.RowDefinitions>\n                <StackPanel Margin=\"0 0 5 10\">\n                    <TextBlock Margin=\"0 0 0 3\">Search:</TextBlock>\n                    <TextBox Text=\"{CompiledBinding Source={x:Static local:PlaygroundSettings.Instance}, Path=SearchText, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}\"\n                             Padding=\"4\" />\n                </StackPanel>\n                <ScrollViewer Grid.Row=\"1\">\n                    <Grid IsSharedSizeScope=\"True\"\n                          Width=\"380\">\n                        <Grid.RowDefinitions>\n                            <RowDefinition Height=\"Auto\" />\n                            <RowDefinition Height=\"Auto\" />\n                        </Grid.RowDefinitions>\n\n                        <Expander Header=\"Playground Settings\"\n                                  Padding=\"0 5 0 0\"\n                                  BorderThickness=\"0 0 0 1\"\n                                  IsExpanded=\"True\"\n                                  BorderBrush=\"{DynamicResource BackgroundBrush}\">\n                            <Expander.Theme>\n                                <ControlTheme TargetType=\"{x:Type Expander}\"\n                                       BasedOn=\"{StaticResource {x:Type Expander}}\">\n                                    <Setter Property=\"Tag\"\n                                            Value=\"{StaticResource ExpandRightIcon}\" />\n                                    <Setter Property=\"(Interaction.Behaviors)\">\n                                        <BehaviorCollectionTemplate>\n                                            <BehaviorCollection>\n                                                <DataTrigger Property=\"IsExpanded\"\n                                                             UseDataContext=\"False\"\n                                                         Value=\"True\">\n                                                    <PropertySetter Property=\"Tag\"\n                                                            Value=\"{StaticResource ExpandDownIcon}\" />\n                                                </DataTrigger>\n                                            </BehaviorCollection>\n                                        </BehaviorCollectionTemplate>\n                                    </Setter>\n                                </ControlTheme>\n                            </Expander.Theme>\n                            <Border BorderThickness=\"1\"\n                                    Padding=\"10\"\n                                    HorizontalAlignment=\"Stretch\">\n                                <local:SettingsView Items=\"{Binding Source={x:Static local:PlaygroundSettings.Instance}, Path=Settings}\" />\n                            </Border>\n                        </Expander>\n\n                        <Expander Header=\"Editor Settings\"\n                                  Padding=\"0 5 0 0\"\n                                  BorderThickness=\"0 0 0 1\"\n                                  IsExpanded=\"True\"\n                                  BorderBrush=\"{DynamicResource BackgroundBrush}\"\n                                  Grid.Row=\"1\">\n                            <Expander.Theme>\n                                <ControlTheme TargetType=\"{x:Type Expander}\"\n                                       BasedOn=\"{StaticResource {x:Type Expander}}\">\n                                    <Setter Property=\"Tag\"\n                                            Value=\"{StaticResource ExpandRightIcon}\" />\n                                    <Setter Property=\"(Interaction.Behaviors)\">\n                                        <BehaviorCollectionTemplate>\n                                            <BehaviorCollection>\n                                                <DataTrigger Property=\"IsExpanded\"\n                                                             UseDataContext=\"False\"\n                                                         Value=\"True\">\n                                                    <PropertySetter Property=\"Tag\"\n                                                            Value=\"{StaticResource ExpandDownIcon}\" />\n                                                </DataTrigger>\n                                            </BehaviorCollection>\n                                        </BehaviorCollectionTemplate>\n                                    </Setter>\n                                </ControlTheme>\n                            </Expander.Theme>\n\n                            <local:EditorSettingsView />\n                        </Expander>\n                    </Grid>\n                </ScrollViewer>\n            </Grid>\n        </Expander>\n\n        <!--INFORMATION-->\n        <Border Grid.Row=\"2\"\n                Background=\"{DynamicResource PanelBackgroundBrush}\"\n                VerticalAlignment=\"Bottom\"\n                Padding=\"10\">\n            <Grid>\n                <Grid.ColumnDefinitions>\n                    <ColumnDefinition Width=\"*\" />\n                    <ColumnDefinition Width=\"Auto\" />\n                </Grid.ColumnDefinitions>\n                <Grid.Resources>\n                    <ControlTheme TargetType=\"{x:Type TextBlock}\" x:Key=\"{x:Type TextBlock}\">\n                        <Setter Property=\"Foreground\"\n                                Value=\"{DynamicResource ForegroundBrush}\" />\n                        <Setter Property=\"Margin\"\n                                Value=\"0 0 15 0\" />\n                    </ControlTheme>\n                </Grid.Resources>\n\n                <StackPanel Orientation=\"Horizontal\">\n                    <TextBlock ToolTip.Tip=\"The number of selected items.\">\n                        <TextBlock.Inlines>\n                            <Run Text=\"Selected nodes: \" />\n                            <Run Foreground=\"YellowGreen\"\n                                 Text=\"{Binding GraphViewModel.SelectedNodes.Count, Mode=OneWay}\" />\n                            <Run Text=\"/\" />\n                            <Run Text=\"{Binding GraphViewModel.Nodes.Count, Mode=OneWay}\" />\n                        </TextBlock.Inlines>\n                    </TextBlock>\n                    <TextBlock ToolTip.Tip=\"The number of selected connections.\">\n                        <TextBlock.Inlines>\n                            <Run Text=\"Selected connections: \" />\n                            <Run Foreground=\"YellowGreen\"\n                                 Text=\"{Binding GraphViewModel.SelectedConnections.Count, Mode=OneWay}\" />\n                            <Run Text=\"/\" />\n                            <Run Text=\"{Binding GraphViewModel.Connections.Count, Mode=OneWay}\" />\n                        </TextBlock.Inlines>\n                    </TextBlock>\n                </StackPanel>\n\n                <StackPanel Orientation=\"Horizontal\"\n                            HorizontalAlignment=\"Right\">\n                    <TextBlock ToolTip.Tip=\"The viewport's location.\">\n                        <TextBlock.Inlines>\n                            <Run Text=\"Location: \" />\n                            <Run Foreground=\"Orange\"\n                                 Text=\"{Binding Location.Value, Mode=OneWay, Converter={StaticResource ToStringConverter}, Source={x:Static local:EditorSettings.Instance}}\" />\n                        </TextBlock.Inlines>\n                    </TextBlock>\n                    <TextBlock ToolTip.Tip=\"The viewport's size.\">\n                        <TextBlock.Inlines>\n                            <Run Text=\"Size: \" />\n                            <Run Foreground=\"YellowGreen\"\n                                 Text=\"{Binding GraphViewModel.ViewportSize, Mode=OneWay, Converter={StaticResource ToStringConverter}}\" />\n                        </TextBlock.Inlines>\n                    </TextBlock>\n                    <TextBlock ToolTip.Tip=\"The viewport's zoom. Not accurate when trying to zoom outside the MinViewportZoom and MaxViewportZoom because of dependency property coercion not updating the binding with the final result.\">\n                        <TextBlock.Inlines>\n                            <Run Text=\"Zoom: \" />\n                            <Run Foreground=\"DodgerBlue\"\n                                 Text=\"{Binding Zoom, Mode=OneWay, Converter={StaticResource ToStringConverter}, Source={x:Static local:EditorSettings.Instance}}\" />\n                        </TextBlock.Inlines>\n                    </TextBlock>\n                    <TextBlock ToolTip.Tip=\"The estimated frame rate. (my be buggy)\">\n                        <TextBlock.Inlines>\n                            <Run Text=\"FPS: \" />\n                            <Run Foreground=\"LawnGreen\"\n                                 Name=\"FPSText\" />\n                        </TextBlock.Inlines>\n                    </TextBlock>\n                </StackPanel>\n            </Grid>\n        </Border>\n    </Grid>\n</Window>\n"
  },
  {
    "path": "Examples/Nodify.Playground/MainWindow.xaml.cs",
    "content": "﻿using System;\nusing System.Diagnostics;\nusing System.Threading;\nusing System.Windows;\nusing System.Windows.Media;\n\nnamespace Nodify.Playground\n{\n    public static class CompositionTargetEx\n    {\n        private static Stopwatch sw = new Stopwatch();\n        private static event Action<double>? FrameUpdating;\n\n        public static event Action<double> Rendering\n        {\n            add\n            {\n                if (FrameUpdating == null)\n                {\n                    sw.Start();\n                }\n                FrameUpdating += value;\n            }\n            remove\n            {\n                FrameUpdating -= value;\n                if (FrameUpdating == null)\n                {\n                    sw.Stop();\n                }\n            }\n        }\n\n        public static void OnRendering(object? sender, EventArgs e)\n        {\n            var took = sw.Elapsed;\n            sw.Restart();\n\n            double fps = 1000 / took.TotalMilliseconds;\n            FrameUpdating?.Invoke(fps);\n        }\n    }\n\n    /// <summary>\n    /// Interaction logic for MainWindow.xaml\n    /// </summary>\n    public partial class MainWindow : Window\n    {\n        private readonly Random _rand = new Random();\n\n        public MainWindow()\n        {\n            InitializeComponent();\n\n            CompositionTargetEx.Rendering += OnRendering;\n        }\n\n        private void OnRendering(double fps)\n        {\n            Dispatcher.UIThread.Post(() =>\n            {\n                FPSText.Text = fps.ToString(\"###\");\n            });\n        }\n\n        private void BringIntoView_Click(object? sender, RoutedEventArgs e)\n        {\n            if (DataContext is PlaygroundViewModel model)\n            {\n                NodifyObservableCollection<NodeViewModel> nodes = model.GraphViewModel.Nodes;\n                int index = _rand.Next(nodes.Count);\n\n                if (nodes.Count > index)\n                {\n                    NodeViewModel node = nodes[index];\n                    EditorCommands.BringIntoView.Execute(node.Location, EditorView.Editor);\n                }\n            }\n        }\n\n        private CancellationTokenSource? _animationTokenSource;\n\n        private void AnimateConnections_Click(object sender, RoutedEventArgs e)\n        {\n            EditorSettings.Instance.IsAnimatingConnections = !EditorSettings.Instance.IsAnimatingConnections;\n        }\n\n        public override void Render(DrawingContext context)\n        {\n            base.Render(context);\n            CompositionTargetEx.OnRendering(null, default!);\n            Dispatcher.UIThread.Post(InvalidateVisual, DispatcherPriority.Send);\n        }\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.Playground/Nodify.Playground.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <OutputType>WinExe</OutputType>\n    <TargetFrameworks>net9</TargetFrameworks>\n    <Nullable>enable</Nullable>\n    <LangVersion>12</LangVersion>\n    <AssemblyOriginatorKeyFile>..\\..\\build\\Nodify.snk</AssemblyOriginatorKeyFile>\n    <SignAssembly>true</SignAssembly>\n  </PropertyGroup>\n  <ItemGroup>\n    <ProjectReference Include=\"..\\..\\Nodify\\Nodify.csproj\" />\n    <ProjectReference Include=\"..\\Nodify.Shared\\Nodify.Shared.csproj\" />\n  </ItemGroup>\n  \n  <ItemGroup>\n    <PackageReference Include=\"Avalonia\" Version=\"$(AvaloniaVersion)\"/>\n    <PackageReference Include=\"Avalonia.Desktop\" Version=\"$(AvaloniaVersion)\"/>\n    <PackageReference Include=\"Avalonia.Themes.Fluent\" Version=\"$(AvaloniaVersion)\"/>\n    <PackageReference Include=\"Avalonia.Fonts.Inter\" Version=\"$(AvaloniaVersion)\"/>\n    <PackageReference Include=\"Avalonia.Xaml.Behaviors\" Version=\"11.1.0.4\"/>\n    <!--Condition below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.-->\n    <PackageReference Condition=\"'$(Configuration)' == 'Debug'\" Include=\"Avalonia.Diagnostics\" Version=\"$(AvaloniaVersion)\"/>\n  </ItemGroup>\n\n  <ItemGroup>\n    <AdditionalFiles SourceItemGroup=\"AvaloniaXaml\" Include=\"**/*.xaml\"/>\n    <AdditionalFiles SourceItemGroup=\"AvaloniaXaml\" Include=\"Editor/*.xaml\"/>\n    <AvaloniaResource Include=\"**/*.xaml\" />\n  </ItemGroup>\n</Project>\n"
  },
  {
    "path": "Examples/Nodify.Playground/PlaygroundSettings.cs",
    "content": "﻿using System.Collections.Generic;\nusing System.Linq;\nusing System.Windows;\n\nnamespace Nodify.Playground\n{\n    public class PlaygroundSettings : ObservableObject\n    {\n        private readonly IReadOnlyCollection<ISettingViewModel> _settings;\n        public IEnumerable<ISettingViewModel> Settings => FilterAndSort(_settings);\n\n        private string? _searchText;\n        public string? SearchText\n        {\n            get => _searchText;\n            set => SetProperty(ref _searchText, value)\n                .Then(() => OnPropertyChanged(nameof(Settings)));\n        }\n\n        private PlaygroundSettings()\n        {\n            _settings = new List<ISettingViewModel>()\n            {\n                new ProxySettingViewModel<EditorGesturesMappings>(\n                    () => Instance.EditorGesturesMappings,\n                    val => Instance.EditorGesturesMappings = val,\n                    \"Editor input mappings\"),\n                new ProxySettingViewModel<EditorInputMode>(\n                    () => Instance.EditorInputMode,\n                    val => Instance.EditorInputMode = val,\n                    \"Editor input mode\"),\n                new ProxySettingViewModel<bool>(\n                    () => Instance.ShowMinimap,\n                    val => Instance.ShowMinimap = val,\n                    \"Show minimap\",\n                    \"Set Enable nodes dragging optimization to false for realtime updates\"),\n                new ProxySettingViewModel<bool>(\n                    () => Instance.DisableMinimapControls,\n                    val => Instance.DisableMinimapControls = val,\n                    \"Disable minimap controls\",\n                    \"Whether the minimap can move and zoom the viewport\"),\n                new ProxySettingViewModel<bool>(\n                    () => Instance.ResizeToViewport,\n                    val => Instance.ResizeToViewport = val,\n                    \"Minimap resize to viewport\",\n                    \"Whether the minimap should resized to also display the viewport\"),\n                new ProxySettingViewModel<PointEditor>(\n                    () => Instance.MinimapMaxViewportOffset,\n                    val => Instance.MinimapMaxViewportOffset = val,\n                    \"Minimap max viewport offset\",\n                    \"The max position from the items extent that the viewport can move to\"),\n                new ProxySettingViewModel<bool>(\n                    () => Instance.ShowGridLines,\n                    val => Instance.ShowGridLines = val,\n                    \"Show grid lines:\"),\n                new ProxySettingViewModel<bool>(\n                    () => Instance.ShouldConnectNodes,\n                    val => Instance.ShouldConnectNodes = val,\n                    \"Connect nodes:\"),\n                new ProxySettingViewModel<bool>(\n                    () => Instance.AsyncLoading,\n                    val => Instance.AsyncLoading = val,\n                    \"Async loading:\"),\n                new ProxySettingViewModel<bool>(\n                    () => Instance.UseCustomConnectors,\n                    val => Instance.UseCustomConnectors = val,\n                    \"Custom connectors:\"),\n                new ProxySettingViewModel<uint>(\n                    () => Instance.MinNodes,\n                    val => Instance.MinNodes = val,\n                    \"Min nodes:\"),\n                new ProxySettingViewModel<uint>(\n                    () => Instance.MaxNodes,\n                    val => Instance.MaxNodes = val,\n                    \"Max nodes:\"),\n                new ProxySettingViewModel<uint>(\n                    () => Instance.MinConnectors,\n                    val => Instance.MinConnectors = val,\n                    \"Min connectors:\"),\n                new ProxySettingViewModel<uint>(\n                    () => Instance.MaxConnectors,\n                    val => Instance.MaxConnectors = val,\n                    \"Max connectors:\"),\n                new ProxySettingViewModel<uint>(\n                    () => Instance.PerformanceTestNodes,\n                    val => Instance.PerformanceTestNodes = val,\n                    \"Performance test nodes:\"),\n            };\n        }\n\n        public IEnumerable<ISettingViewModel> FilterAndSort(IReadOnlyCollection<ISettingViewModel> settings)\n        {\n            if (string.IsNullOrWhiteSpace(SearchText))\n            {\n                return settings;\n            }\n\n            string searchText = SearchText!.ToLowerInvariant();\n\n            var matchingValues = settings.Where(s => s.Name.ToLowerInvariant().Contains(searchText) || (s.Description?.ToLowerInvariant()?.Contains(searchText) ?? false));\n            var sortedValues = matchingValues.OrderByDescending(s => s.Name.ToLowerInvariant().Contains(searchText));\n\n            return sortedValues;\n        }\n\n        public static PlaygroundSettings Instance { get; } = new PlaygroundSettings();\n\n        private EditorGesturesMappings _editorGesturesMappings;\n        public EditorGesturesMappings EditorGesturesMappings\n        {\n            get => _editorGesturesMappings;\n            set => SetProperty(ref _editorGesturesMappings, value)\n                .Then(() => EditorGestures.Mappings.Apply(value));\n        }\n\n        private EditorInputMode _editorInputMode;\n        public EditorInputMode EditorInputMode\n        {\n            get => _editorInputMode;\n            set => SetProperty(ref _editorInputMode, value)\n                .Then(() => EditorGestures.Mappings.Apply(value));\n        }\n\n        private bool _showMinimap = true;\n        public bool ShowMinimap\n        {\n            get => _showMinimap;\n            set => SetProperty(ref _showMinimap, value);\n        }\n\n        private bool _disableMinimapControls = false;\n        public bool DisableMinimapControls\n        {\n            get => _disableMinimapControls;\n            set => SetProperty(ref _disableMinimapControls, value);\n        }\n\n        private bool _resizeToViewport = false;\n        public bool ResizeToViewport\n        {\n            get => _resizeToViewport;\n            set => SetProperty(ref _resizeToViewport, value);\n        }\n\n        private PointEditor _minimapViewportOffset = new Size(2000, 2000);\n        public PointEditor MinimapMaxViewportOffset\n        {\n            get => _minimapViewportOffset;\n            set => SetProperty(ref _minimapViewportOffset, value);\n        }\n\n        private bool _shouldConnectNodes = true;\n        public bool ShouldConnectNodes\n        {\n            get => _shouldConnectNodes;\n            set => SetProperty(ref _shouldConnectNodes, value);\n        }\n\n        private bool _asyncLoading = true;\n        public bool AsyncLoading\n        {\n            get => _asyncLoading;\n            set => SetProperty(ref _asyncLoading, value);\n        }\n\n        private uint _minNodes = 10;\n        public uint MinNodes\n        {\n            get => _minNodes;\n            set => SetProperty(ref _minNodes, value)\n                .Then(() => MaxNodes = MaxNodes < MinNodes ? MinNodes : MaxNodes);\n        }\n\n        private uint _maxNodes = 100;\n        public uint MaxNodes\n        {\n            get => _maxNodes;\n            set => SetProperty(ref _maxNodes, value)\n                .Then(() => MaxNodes = MaxNodes < MinNodes ? MinNodes : MaxNodes);\n        }\n\n        private uint _minConnectors = 0;\n        public uint MinConnectors\n        {\n            get => _minConnectors;\n            set => SetProperty(ref _minConnectors, value)\n                .Then(() => MaxConnectors = MaxConnectors < MinConnectors ? MinConnectors : MaxConnectors);\n        }\n\n        private uint _maxConnectors = 4;\n        public uint MaxConnectors\n        {\n            get => _maxConnectors;\n            set => SetProperty(ref _maxConnectors, value)\n                .Then(() => MaxConnectors = MaxConnectors < MinConnectors ? MinConnectors : MaxConnectors);\n        }\n\n        private uint _performanceTestNodes = 1000;\n        public uint PerformanceTestNodes\n        {\n            get => _performanceTestNodes;\n            set => SetProperty(ref _performanceTestNodes, value);\n        }\n\n        private bool _showGridLines = true;\n        public bool ShowGridLines\n        {\n            get => _showGridLines;\n            set => SetProperty(ref _showGridLines, value);\n        }\n\n        private bool _customConnectors = true;\n        public bool UseCustomConnectors\n        {\n            get => _customConnectors;\n            set => SetProperty(ref _customConnectors, value);\n        }\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.Playground/PlaygroundViewModel.cs",
    "content": "﻿using System;\nusing System.Collections;\nusing System.Threading.Tasks;\nusing System.Windows.Data;\nusing System.Windows.Input;\n\nnamespace Nodify.Playground\n{\n    public class PlaygroundViewModel : ObservableObject\n    {\n        public NodifyEditorViewModel GraphViewModel { get; } = new NodifyEditorViewModel();\n\n        public PlaygroundViewModel()\n        {\n            GenerateRandomNodesCommand = new DelegateCommand(GenerateRandomNodes);\n            PerformanceTestCommand = new DelegateCommand(PerformanceTest);\n            ToggleConnectionsCommand = new DelegateCommand(ToggleConnections);\n            ResetCommand = new DelegateCommand(ResetGraph);\n\n            //BindingOperations.EnableCollectionSynchronization(GraphViewModel.Nodes, GraphViewModel.Nodes);\n            //BindingOperations.EnableCollectionSynchronization(GraphViewModel.Connections, GraphViewModel.Connections);\n\n            Settings.PropertyChanged += OnSettingsChanged;\n        }\n\n        private void OnSettingsChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)\n        {\n            if (e.PropertyName == nameof(PlaygroundSettings.ShouldConnectNodes))\n                OnPropertyChanged(nameof(ConnectNodesText));\n        }\n\n        public ICommand GenerateRandomNodesCommand { get; }\n        public ICommand PerformanceTestCommand { get; }\n        public ICommand ToggleConnectionsCommand { get; }\n        public ICommand ResetCommand { get; }\n        public PlaygroundSettings Settings => PlaygroundSettings.Instance;\n\n        public string ConnectNodesText => Settings.ShouldConnectNodes ? \"CONNECT NODES\" : \"DISCONNECT NODES\";\n\n        private void ResetGraph()\n        {\n            GraphViewModel.Nodes.Clear();\n            EditorSettings.Instance.Location = new Point(0, 0);\n            EditorSettings.Instance.Zoom = 1.0d;\n        }\n\n        private async void GenerateRandomNodes()\n        {\n            uint minNodesByType = Settings.MinNodes / 2;\n            uint maxNodesByType = Settings.MaxNodes / 2;\n\n            var nodes = RandomNodesGenerator.GenerateNodes<FlowNodeViewModel>(new NodesGeneratorSettings(minNodesByType)\n            {\n                MinNodesCount = minNodesByType,\n                MaxNodesCount = maxNodesByType,\n                MinInputCount = Settings.MinConnectors,\n                MaxInputCount = Settings.MaxConnectors,\n                MinOutputCount = Settings.MinConnectors,\n                MaxOutputCount = Settings.MaxConnectors,\n                GridSnap = EditorSettings.Instance.GridSpacing\n            });\n\n            var verticalNodes = RandomNodesGenerator.GenerateNodes<VerticalNodeViewModel>(new NodesGeneratorSettings(minNodesByType)\n            {\n                MinNodesCount = minNodesByType,\n                MaxNodesCount = maxNodesByType,\n                MinInputCount = Settings.MinConnectors,\n                MaxInputCount = Settings.MaxConnectors,\n                MinOutputCount = Settings.MinConnectors,\n                MaxOutputCount = Settings.MaxConnectors,\n                GridSnap = EditorSettings.Instance.GridSpacing\n            });\n\n            GraphViewModel.Nodes.Clear();\n            await CopyToAsync(nodes, GraphViewModel.Nodes);\n            await CopyToAsync(verticalNodes, GraphViewModel.Nodes);\n\n            if (Settings.ShouldConnectNodes)\n            {\n                await ConnectNodes();\n            }\n        }\n\n        private async void ToggleConnections()\n        {\n            if (Settings.ShouldConnectNodes)\n            {\n                await ConnectNodes();\n            }\n            else\n            {\n                GraphViewModel.Connections.Clear();\n            }\n        }\n\n        private async void PerformanceTest()\n        {\n            uint count = Settings.PerformanceTestNodes;\n            int distance = 500;\n            int size = (int)count / (int)Math.Sqrt(count);\n\n            var nodes = RandomNodesGenerator.GenerateNodes<FlowNodeViewModel>(new NodesGeneratorSettings(count)\n            {\n                NodeLocationGenerator = (s, i) => new Point(i % size * distance, i / size * distance),\n                MinInputCount = Settings.MinConnectors,\n                MaxInputCount = Settings.MaxConnectors,\n                MinOutputCount = Settings.MinConnectors,\n                MaxOutputCount = Settings.MaxConnectors,\n                GridSnap = EditorSettings.Instance.GridSpacing\n            });\n\n            GraphViewModel.Nodes.Clear();\n            await CopyToAsync(nodes, GraphViewModel.Nodes);\n\n            if (Settings.ShouldConnectNodes)\n            {\n                await ConnectNodes();\n            }\n        }\n\n        private async Task ConnectNodes()\n        {\n            var schema = new GraphSchema();\n            var connections = RandomNodesGenerator.GenerateConnections(GraphViewModel.Nodes);\n\n            if (Settings.AsyncLoading)\n            {\n                await Task.Run(() =>\n                {\n                    for (int i = 0; i < connections.Count; i++)\n                    {\n                        var con = connections[i];\n                        schema.TryAddConnection(con.Input, con.Output);\n                    }\n                });\n            }\n            else\n            {\n                for (int i = 0; i < connections.Count; i++)\n                {\n                    var con = connections[i];\n                    schema.TryAddConnection(con.Input, con.Output);\n                }\n            }\n        }\n\n        private async Task CopyToAsync(IList source, IList target)\n        {\n            if (Settings.AsyncLoading)\n            {\n                await Task.Run(() =>\n                {\n                    for (int i = 0; i < source.Count; i++)\n                    {\n                        target.Add(source[i]);\n                    }\n                });\n            }\n            else\n            {\n                for (int i = 0; i < source.Count; i++)\n                {\n                    target.Add(source[i]);\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.Playground/PointEditor.cs",
    "content": "﻿using System.Windows;\n\nnamespace Nodify.Playground\n{\n    public class PointEditor : ObservableObject\n    {\n        public double X\n        {\n            get => Value.X;\n            set\n            {\n                Value = new Point(value, Value.Y);\n                if (value >= 0)\n                {\n                    Size = new Size(value, Size.Height);\n                }\n            }\n        }\n\n        public double Y\n        {\n            get => Value.Y;\n            set\n            {\n                Value = new Point(Value.X, value);\n                if (value >= 0)\n                {\n                    Size = new Size(Size.Width, value);\n                }\n            }\n        }\n\n        private Point _value;\n        public Point Value\n        {\n            get => _value;\n            set => SetProperty(ref _value, value)\n                .Then(() =>\n                {\n                    OnPropertyChanged(nameof(X));\n                    OnPropertyChanged(nameof(Y));\n                });\n        }\n\n        private Size _size;\n        public Size Size\n        {\n            get => _size;\n            set => SetProperty(ref _size, value)\n                .Then(() =>\n                {\n                    OnPropertyChanged(nameof(X));\n                    OnPropertyChanged(nameof(Y));\n                });\n        }\n\n        public string XLabel { get; set; } = \"x\";\n        public string YLabel { get; set; } = \"y\";\n\n        public static implicit operator PointEditor(Point point)\n        {\n            return new PointEditor\n            {\n                X = point.X,\n                Y = point.Y\n            };\n        }\n\n        public static implicit operator PointEditor(Size size)\n        {\n            return new PointEditor\n            {\n                X = size.Width,\n                Y = size.Height,\n                XLabel = \"w\",\n                YLabel = \"h\"\n            };\n        }\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.Playground/PointEditorView.xaml",
    "content": "﻿<UserControl x:Class=\"Nodify.Playground.PointEditorView\"\n             xmlns=\"https://github.com/avaloniaui\"\n             xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n             xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\"\n             xmlns:d=\"http://schemas.microsoft.com/expression/blend/2008\"\n             xmlns:local=\"clr-namespace:Nodify.Playground\"\n             d:DataContext=\"{d:DesignInstance Type={x:Type local:PointEditor}, IsDesignTimeCreatable=True}\"\n             mc:Ignorable=\"d\"\n             d:DesignHeight=\"450\"\n             d:DesignWidth=\"800\">\n    <Grid>\n        <Grid.ColumnDefinitions>\n            <ColumnDefinition Width=\"Auto\"\n                              MinWidth=\"20\" />\n            <ColumnDefinition MinWidth=\"30\" />\n        </Grid.ColumnDefinitions>\n\n        <Grid.RowDefinitions>\n            <RowDefinition Height=\"Auto\" />\n            <RowDefinition Height=\"Auto\" />\n        </Grid.RowDefinitions>\n\n        <TextBlock TextAlignment=\"Center\"\n                   VerticalAlignment=\"Center\">\n                <Run Text=\"{Binding XLabel}\" />\n                <Run Text=\":\" />\n        </TextBlock>\n        <TextBox Text=\"{Binding X, Mode=TwoWay}\"\n                 Grid.Column=\"1\" />\n\n        <TextBlock TextAlignment=\"Center\"\n                   VerticalAlignment=\"Center\"\n                   Grid.Row=\"1\"\n                   Margin=\"0 5 0 0\">\n                <Run Text=\"{Binding YLabel}\" />\n                <Run Text=\":\" />\n        </TextBlock>\n        <TextBox Text=\"{Binding Y, Mode=TwoWay}\"\n                 Margin=\"0 5 0 0\"\n                 Grid.Row=\"1\"\n                 Grid.Column=\"1\" />\n    </Grid>\n</UserControl>\n"
  },
  {
    "path": "Examples/Nodify.Playground/PointEditorView.xaml.cs",
    "content": "﻿using System.Windows.Controls;\n\nnamespace Nodify.Playground\n{\n    public partial class PointEditorView : UserControl\n    {\n        public PointEditorView()\n        {\n            InitializeComponent();\n        }\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.Playground/Program.cs",
    "content": "﻿using System;\n\nnamespace Nodify.Playground;\n\nclass Program\n{\n    // Initialization code. Don't use any Avalonia, third-party APIs or any\n    // SynchronizationContext-reliant code before AppMain is called: things aren't initialized\n    // yet and stuff might break.\n    [STAThread]\n    public static void Main(string[] args) => BuildAvaloniaApp()\n        .StartWithClassicDesktopLifetime(args);\n\n    // Avalonia configuration, don't remove; also used by visual designer.\n    public static AppBuilder BuildAvaloniaApp()\n        => AppBuilder.Configure<App>()\n            .UsePlatformDetect()\n            .WithInterFont()\n            .LogToTrace();\n}"
  },
  {
    "path": "Examples/Nodify.Playground/ProxySettingViewModel.cs",
    "content": "﻿using System;\n\nnamespace Nodify.Playground\n{\n    public class ProxySettingViewModel<T> : BaseSettingViewModel<T>\n    {\n        private readonly Func<T> _getter;\n        private readonly Action<T> _setter;\n\n        public ProxySettingViewModel(Func<T> getter, Action<T> setter, string name, string? description = default)\n            : base(name, description)\n        {\n            _getter = getter;\n            _setter = setter;\n        }\n\n        public new T Value\n        {\n            get => _getter();\n            set => _setter(value);\n        }\n    }\n}"
  },
  {
    "path": "Examples/Nodify.Playground/SettingsView.xaml",
    "content": "﻿<UserControl x:Class=\"Nodify.Playground.SettingsView\"\n             xmlns=\"https://github.com/avaloniaui\"\n             xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n             xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\"\n             xmlns:d=\"http://schemas.microsoft.com/expression/blend/2008\"\n             xmlns:local=\"clr-namespace:Nodify.Playground\"\n             xmlns:nodify=\"clr-namespace:Nodify;assembly=Nodify.Shared\"\n             d:DataContext=\"{d:DesignInstance Type=local:PlaygroundViewModel, IsDesignTimeCreatable=True}\"\n             d:Foreground=\"{DynamicResource ForegroundBrush}\"\n             d:Background=\"{DynamicResource PanelBackgroundBrush}\"\n             mc:Ignorable=\"d\">\n    <ItemsControl ItemsSource=\"{Binding Items, RelativeSource={RelativeSource AncestorType=UserControl}}\">\n        <ItemsControl.Resources>\n            <DataTemplate x:Key=\"TextEditorTemplate\" DataType=\"{x:Type local:ISettingViewModel}\">\n                <TextBox Text=\"{Binding Value}\" TextWrapping=\"Wrap\" AcceptsReturn=\"True\" />\n            </DataTemplate>\n            <DataTemplate x:Key=\"NumberEditorTemplate\" DataType=\"{x:Type local:ISettingViewModel}\">\n                <TextBox Text=\"{Binding Value}\" />\n            </DataTemplate>\n            <DataTemplate x:Key=\"BooleanEditorTemplate\" DataType=\"{x:Type local:ISettingViewModel}\">\n                <CheckBox IsChecked=\"{Binding Value}\" />\n            </DataTemplate>\n            <DataTemplate x:Key=\"PointEditorTemplate\" DataType=\"{x:Type local:ISettingViewModel}\">\n                <local:PointEditorView DataContext=\"{Binding Value, Mode=TwoWay}\" />\n            </DataTemplate>\n            <DataTemplate x:Key=\"OptionEditorTemplate\" DataType=\"{x:Type local:ISettingViewModel}\">\n                <ComboBox\n                    DisplayMemberBinding=\"{Binding Name}\"\n                    SelectedValueBinding=\"{Binding Value}\"\n                    SelectedValue=\"{Binding Value, Mode=TwoWay}\"\n                    ItemsSource=\"{Binding Value, Converter={nodify:EnumValuesConverter}}\" />\n            </DataTemplate>\n        </ItemsControl.Resources>\n        <ItemsControl.ItemTemplate>\n            <DataTemplate DataType=\"{x:Type local:ISettingViewModel}\">\n                <Grid>\n                    <Grid.ColumnDefinitions>\n                        <ColumnDefinition Width=\"Auto\" SharedSizeGroup=\"PropertyName\" />\n                        <ColumnDefinition />\n                    </Grid.ColumnDefinitions>\n                    <TextBlock Text=\"{Binding Name}\" ToolTip.Tip=\"{Binding Description}\" Margin=\"0 5 5 0\" Grid.Column=\"0\" />\n                    <ContentControl Content=\"{Binding}\" Margin=\"5 5 5 0\" Grid.Column=\"1\">\n                        <ContentControl.Theme>\n                            <ControlTheme TargetType=\"{x:Type ContentControl}\">\n                                <Setter Property=\"(Interaction.Behaviors)\">\n                                    <BehaviorCollectionTemplate>\n                                        <BehaviorCollection>\n                                            <DataTrigger Property=\"Type\" Value=\"Boolean\">\n                                                <PropertySetter Property=\"ContentTemplate\"\n                                                        Value=\"{StaticResource ResourceKey=BooleanEditorTemplate}\" />\n                                            </DataTrigger>\n                                            <DataTrigger Property=\"Type\" Value=\"Number\">\n                                                <PropertySetter Property=\"ContentTemplate\"\n                                                                Value=\"{StaticResource ResourceKey=NumberEditorTemplate}\" />\n                                            </DataTrigger>\n                                            <DataTrigger Property=\"Type\" Value=\"Point\">\n                                                <PropertySetter Property=\"ContentTemplate\"\n                                                                Value=\"{StaticResource ResourceKey=PointEditorTemplate}\" />\n                                            </DataTrigger>\n                                            <DataTrigger Property=\"Type\" Value=\"Option\">\n                                                <PropertySetter Property=\"ContentTemplate\"\n                                                                Value=\"{StaticResource ResourceKey=OptionEditorTemplate}\" />\n                                            </DataTrigger>\n                                            <DataTrigger Property=\"Type\" Value=\"Text\">\n                                                <PropertySetter Property=\"ContentTemplate\"\n                                                                Value=\"{StaticResource ResourceKey=TextEditorTemplate}\" />\n                                            </DataTrigger>\n                                        </BehaviorCollection>\n                                    </BehaviorCollectionTemplate>\n                                </Setter>\n                            </ControlTheme>\n                        </ContentControl.Theme>\n                    </ContentControl>\n                </Grid>\n            </DataTemplate>\n        </ItemsControl.ItemTemplate>\n    </ItemsControl>\n</UserControl>"
  },
  {
    "path": "Examples/Nodify.Playground/SettingsView.xaml.cs",
    "content": "﻿using System.Collections.Generic;\nusing System.Windows;\nusing System.Windows.Controls;\n\nnamespace Nodify.Playground\n{\n    public partial class SettingsView : UserControl\n    {\n        public static readonly AvaloniaProperty<IEnumerable<ISettingViewModel>> ItemsProperty =\n            AvaloniaProperty.Register<SettingsView, IEnumerable<ISettingViewModel>>(nameof(Items));\n\n        public IEnumerable<ISettingViewModel> Items\n        {\n            get => (IEnumerable<ISettingViewModel>)GetValue(ItemsProperty);\n            set => SetValue(ItemsProperty, value);\n        }\n\n        public SettingsView()\n        {\n            InitializeComponent();\n        }\n    }\n}"
  },
  {
    "path": "Examples/Nodify.Playground/Themes/Brushes.xaml",
    "content": "﻿<ResourceDictionary xmlns=\"https://github.com/avaloniaui\"\n                    xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n                    xmlns:o=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation/options\">\n    \n    <SolidColorBrush x:Key=\"PanelBackgroundBrush\"\n                     Color=\"{DynamicResource PanelBackgroundColor}\"\n                     Opacity=\"0.8\" />\n    \n</ResourceDictionary>"
  },
  {
    "path": "Examples/Nodify.Playground/Themes/Dark.xaml",
    "content": "﻿<ResourceDictionary xmlns=\"https://github.com/avaloniaui\"\n                    xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\">\n    \n    <ResourceDictionary.MergedDictionaries>\n        <ResourceInclude Source=\"Brushes.xaml\" />\n    </ResourceDictionary.MergedDictionaries>\n    \n    <Color x:Key=\"PanelBackgroundColor\">#1A1A1A</Color>\n    \n</ResourceDictionary>"
  },
  {
    "path": "Examples/Nodify.Playground/Themes/Light.xaml",
    "content": "﻿<ResourceDictionary xmlns=\"https://github.com/avaloniaui\"\n                    xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\">\n\n    <ResourceDictionary.MergedDictionaries>\n        <ResourceInclude Source=\"Brushes.xaml\" />\n    </ResourceDictionary.MergedDictionaries>\n    \n    <Color x:Key=\"PanelBackgroundColor\">#E9EEFE</Color>\n\n</ResourceDictionary>"
  },
  {
    "path": "Examples/Nodify.Playground/Themes/Nodify.xaml",
    "content": "﻿<ResourceDictionary xmlns=\"https://github.com/avaloniaui\"\n                    xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\">\n    \n    <ResourceDictionary.MergedDictionaries>\n        <ResourceInclude Source=\"Brushes.xaml\" />\n    </ResourceDictionary.MergedDictionaries>\n    \n    <Color x:Key=\"PanelBackgroundColor\">#2A1B47</Color>\n    \n</ResourceDictionary>"
  },
  {
    "path": "Examples/Nodify.Shapes/App.xaml",
    "content": "﻿<Application x:Class=\"Nodify.Shapes.App\"\n             xmlns=\"https://github.com/avaloniaui\"\n             xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n             xmlns:nodify=\"https://miroiu.github.io/nodify\"\n             xmlns:controls=\"clr-namespace:Nodify.Shapes.Controls\">\n    <Application.Resources>\n        <ResourceDictionary>\n            <ControlTheme x:Key=\"IconButton\"\n                   TargetType=\"Button\"\n                   BasedOn=\"{StaticResource {x:Type Button}}\">\n                <Setter Property=\"Background\"\n                        Value=\"Transparent\" />\n                <Setter Property=\"BorderBrush\"\n                        Value=\"Transparent\" />\n                <Setter Property=\"Padding\"\n                        Value=\"2\" />\n                <Setter Property=\"Cursor\"\n                        Value=\"Hand\" />\n                <Setter Property=\"Template\">\n                    <Setter.Value>\n                        <ControlTemplate TargetType=\"{x:Type Button}\">\n                            <Border x:Name=\"PART_Border\"\n                                    BorderThickness=\"{TemplateBinding BorderThickness}\"\n                                    Background=\"{TemplateBinding Background}\"\n                                    Padding=\"{TemplateBinding Padding}\"\n                                    CornerRadius=\"3\">\n                                <ContentPresenter x:Name=\"contentPresenter\"\n                                                  Focusable=\"False\"\n                                                  Margin=\"{TemplateBinding Padding}\"\n                                                  Content=\"{TemplateBinding Content}\"\n                                                  ContentTemplate=\"{TemplateBinding ContentTemplate}\"\n                                                  HorizontalAlignment=\"{TemplateBinding HorizontalContentAlignment}\"\n                                                  VerticalAlignment=\"{TemplateBinding VerticalContentAlignment}\" />\n                            </Border>\n                        </ControlTemplate>\n                    </Setter.Value>\n                </Setter>\n                <Style Selector=\"^:disabled\">\n                    <Setter Property=\"Opacity\"\n                            Value=\"0.4\" />\n                </Style>\n            </ControlTheme>\n\n            <!--ICONS-->\n\n            <DataTemplate x:Key=\"MinusIcon\">\n                <Viewbox Stretch=\"Fill\">\n                    <Grid>\n                        <Path StrokeJoin=\"Round\"\n                              StrokeLineCap=\"Round\"\n                              Data=\"M0 0h24v24H0z\" />\n                        <Path Stroke=\"White\"\n                              StrokeJoin=\"Round\"\n                              StrokeLineCap=\"Round\"\n                              Data=\"M5 12h14\" />\n                    </Grid>\n                </Viewbox>\n            </DataTemplate>\n\n            <DataTemplate x:Key=\"PlusIcon\">\n                <Viewbox Stretch=\"Fill\">\n                    <Grid>\n                        <Path StrokeJoin=\"Round\"\n                              StrokeLineCap=\"Round\"\n                              Data=\"M0 0h24v24H0z\" />\n                        <Path Stroke=\"White\"\n                              StrokeJoin=\"Round\"\n                              StrokeLineCap=\"Round\"\n                              Data=\"M12 5v14M5 12h14\" />\n                    </Grid>\n                </Viewbox>\n            </DataTemplate>\n\n            <DataTemplate x:Key=\"MaximizeIcon\">\n                <Viewbox Stretch=\"Fill\">\n                    <Grid>\n                        <Path StrokeJoin=\"Round\"\n                              StrokeLineCap=\"Round\"\n                              Data=\"M0 0h24v24H0z\" />\n                        <Path Stroke=\"White\"\n                              StrokeJoin=\"Round\"\n                              StrokeLineCap=\"Round\"\n                              Data=\"M4 8V6a2 2 0 0 1 2-2h2M4 16v2a2 2 0 0 0 2 2h2M16 4h2a2 2 0 0 1 2 2v2M16 20h2a2 2 0 0 0 2-2v-2\" />\n                    </Grid>\n                </Viewbox>\n            </DataTemplate>\n\n            <DataTemplate x:Key=\"CursorIcon\">\n                <Viewbox Stretch=\"Fill\"\n                         Margin=\"2\">\n                    <Path Stroke=\"White\"\n                          StrokeThickness=\"1\"\n                          StrokeJoin=\"Round\"\n                          StrokeLineCap=\"Round\"\n                          Data=\"M.256.255a.874.874 0 0 0-.18.974l4.753 17.114a.875.875 0 0 0 1.603-.012L10 10l8.334-3.57a.875.875 0 0 0 .01-1.601L1.23.075a.874.874 0 0 0-.974.18Z\" />\n                </Viewbox>\n            </DataTemplate>\n\n            <DataTemplate  x:Key=\"CircleIcon\">\n                <Viewbox Stretch=\"Fill\">\n                    <Grid>\n                        <Path StrokeJoin=\"Round\"\n                              StrokeLineCap=\"Round\"\n                              Data=\"M0 0h24v24H0z\" />\n                        <Path Stroke=\"White\"\n                              StrokeJoin=\"Round\"\n                              StrokeLineCap=\"Round\"\n                              Data=\"M3 12a9 9 0 1 0 18 0 9 9 0 1 0-18 0\" />\n                    </Grid>\n                </Viewbox>\n            </DataTemplate>\n\n            <DataTemplate x:Key=\"SquareIcon\">\n                <Viewbox Stretch=\"Fill\">\n                    <Grid>\n                        <Path StrokeJoin=\"Round\"\n                              StrokeLineCap=\"Round\"\n                              Data=\"M0 0h24v24H0z\" />\n                        <Path Stroke=\"White\"\n                              StrokeJoin=\"Round\"\n                              StrokeLineCap=\"Round\"\n                              Data=\"M3 5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z\" />\n                    </Grid>\n                </Viewbox>\n            </DataTemplate>\n\n            <DataTemplate x:Key=\"TriangleIcon\">\n                <Viewbox Stretch=\"Fill\">\n                    <Grid>\n                        <Path StrokeJoin=\"Round\"\n                              StrokeLineCap=\"Round\"\n                              Data=\"M0 0h24v24H0z\" />\n                        <Path Stroke=\"White\"\n                              StrokeJoin=\"Round\"\n                              StrokeLineCap=\"Round\"\n                              Data=\"M10.363 3.591 2.257 17.125a1.914 1.914 0 0 0 1.636 2.871h16.214a1.914 1.914 0 0 0 1.636-2.87L13.637 3.59a1.914 1.914 0 0 0-3.274 0z\" />\n                    </Grid>\n                </Viewbox>\n            </DataTemplate>\n\n            <DataTemplate x:Key=\"ArrowBackIcon\">\n                <Viewbox Stretch=\"Fill\">\n                    <Grid>\n                        <Path StrokeJoin=\"Round\"\n                              StrokeLineCap=\"Round\"\n                              Data=\"M0 0h24v24H0z\" />\n                        <Path Stroke=\"White\"\n                              StrokeJoin=\"Round\"\n                              StrokeLineCap=\"Round\"\n                              Data=\"m9 14-4-4 4-4\" />\n                        <Path Stroke=\"White\"\n                              StrokeJoin=\"Round\"\n                              StrokeLineCap=\"Round\"\n                              Data=\"M5 10h11a4 4 0 1 1 0 8h-1\" />\n                    </Grid>\n                </Viewbox>\n            </DataTemplate>\n\n            <DataTemplate x:Key=\"ArrowForwardIcon\">\n                <Viewbox Stretch=\"Fill\">\n                    <Grid>\n                        <Path StrokeJoin=\"Round\"\n                              StrokeLineCap=\"Round\"\n                              Data=\"M0 0h24v24H0z\" />\n                        <Path Stroke=\"White\"\n                              StrokeJoin=\"Round\"\n                              StrokeLineCap=\"Round\"\n                              Data=\"m15 14 4-4-4-4\" />\n                        <Path Stroke=\"White\"\n                              StrokeJoin=\"Round\"\n                              StrokeLineCap=\"Round\"\n                              Data=\"M19 10H8a4 4 0 1 0 0 8h1\" />\n                    </Grid>\n                </Viewbox>\n            </DataTemplate>\n\n            <DataTemplate x:Key=\"LockIcon\">\n                <Viewbox Stretch=\"Fill\">\n                    <Grid>\n                        <Path StrokeJoin=\"Round\"\n                              StrokeLineCap=\"Round\"\n                              Data=\"M0 0h24v24H0z\" />\n                        <Path Stroke=\"White\"\n                              StrokeJoin=\"Round\"\n                              StrokeLineCap=\"Round\"\n                              Data=\"M5 13a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2v6a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2v-6z\" />\n                        <Path Stroke=\"White\"\n                              StrokeJoin=\"Round\"\n                              StrokeLineCap=\"Round\"\n                              Data=\"M11 16a1 1 0 1 0 2 0 1 1 0 0 0-2 0M8 11V7a4 4 0 1 1 8 0v4\" />\n                    </Grid>\n                </Viewbox>\n            </DataTemplate>\n\n            <DataTemplate x:Key=\"LockOpenIcon\">\n                <Viewbox Stretch=\"Fill\">\n                    <Grid>\n                        <Path StrokeJoin=\"Round\"\n                              StrokeLineCap=\"Round\"\n                              Data=\"M0 0h24v24H0z\" />\n                        <Path Stroke=\"White\"\n                              StrokeJoin=\"Round\"\n                              StrokeLineCap=\"Round\"\n                              Data=\"M5 13a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2v6a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2z\" />\n                        <Path Stroke=\"White\"\n                              StrokeJoin=\"Round\"\n                              StrokeLineCap=\"Round\"\n                              Data=\"M11 16a1 1 0 1 0 2 0 1 1 0 1 0-2 0M8 11V6a4 4 0 0 1 8 0\" />\n                    </Grid>\n                </Viewbox>\n            </DataTemplate>\n\n            <ResourceDictionary.MergedDictionaries>\n                <ResourceInclude Source=\"avares://Nodify.Shared/Themes/Icons.xaml\" />\n                <ResourceInclude Source=\"avares://Nodify.Shared/Themes/Generic.xaml\" />\n                <ResourceInclude Source=\"avares://Nodify.Shared/Themes/Dark.xaml\" />\n                <ResourceInclude Source=\"avares://Nodify/Themes/Generic.xaml\" />\n                <ResourceInclude Source=\"avares://Nodify/Themes/Dark.xaml\" />\n            </ResourceDictionary.MergedDictionaries>\n        </ResourceDictionary>\n    </Application.Resources>\n\n    <Application.Styles>\n        <FluentTheme DensityStyle=\"Compact\"/>\n        <Style Selector=\"Path\">\n            <Setter Property=\"StrokeThickness\" Value=\"1\" /> <!-- WPF default -->\n        </Style>\n        <Style Selector=\"ItemsControl\">\n            <Setter Property=\"ClipToBounds\" Value=\"False\" />\n        </Style>\n        <Style Selector=\"nodify|DecoratorContainer\">\n            <Setter Property=\"ClipToBounds\" Value=\"False\" />\n        </Style>\n        <Style Selector=\"nodify|ItemContainer\">\n            <Setter Property=\"ClipToBounds\" Value=\"False\" />\n        </Style>\n        <Style Selector=\"nodify|Connector\">\n            <Setter Property=\"ClipToBounds\" Value=\"False\" />\n        </Style>\n        <Style Selector=\"controls|ResizableContainer\">\n            <Setter Property=\"ClipToBounds\" Value=\"False\" />\n        </Style>\n    </Application.Styles>\n</Application>\n"
  },
  {
    "path": "Examples/Nodify.Shapes/App.xaml.cs",
    "content": "﻿using System.Windows;\nusing System.Windows.Input;\n\nnamespace Nodify.Shapes\n{\n    /// <summary>\n    /// Interaction logic for App.xaml\n    /// </summary>\n    public partial class App : Application\n    {\n        public App()\n        {\n            NodifyEditor.EnableDraggingContainersOptimizations = false;\n            NodifyEditor.EnableCuttingLinePreview = true;\n\n            EditorGestures.Mappings.Connection.Disconnect.Value = new AnyGesture(new MouseGesture(MouseAction.LeftClick, KeyModifiers.Alt), new MouseGesture(MouseAction.RightClick));\n        }\n\n        public override void Initialize()\n        {\n            AvaloniaXamlLoader.Load(this);\n        }\n\n        public override void OnFrameworkInitializationCompleted()\n        {\n            if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)\n            {\n                desktop.MainWindow = new MainWindow();\n            }\n            else if (ApplicationLifetime is ISingleViewApplicationLifetime singleView)\n            {\n                singleView.MainView = new MainView();\n            }\n\n            base.OnFrameworkInitializationCompleted();\n        }\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.Shapes/AppShellViewModel.cs",
    "content": "﻿using Nodify.Shapes.Canvas;\n\nnamespace Nodify.Shapes\n{\n    public class AppShellViewModel : ObservableObject\n    {\n        public CanvasViewModel Canvas { get; } = new CanvasViewModel();\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.Shapes/AssemblyInfo.cs",
    "content": "using System.Windows;\n\n[assembly: ThemeInfo(\n    ResourceDictionaryLocation.None,            //where theme specific resource dictionaries are located\n                                                //(used if a resource is not found in the page,\n                                                // or application resource dictionaries)\n    ResourceDictionaryLocation.SourceAssembly   //where the generic resource dictionary is located\n                                                //(used if a resource is not found in the page,\n                                                // app, or any theme specific resource dictionaries)\n)]\n"
  },
  {
    "path": "Examples/Nodify.Shapes/Avalonia/Cursors.cs",
    "content": "namespace Nodify.Shapes.Controls;\n\npublic static class Cursors\n{\n    public static Cursor Arrow { get; } = new Cursor(StandardCursorType.Arrow);\n}"
  },
  {
    "path": "Examples/Nodify.Shapes/Avalonia/ToolItemSelector.cs",
    "content": "using System;\nusing Avalonia.Controls.Templates;\nusing Nodify.Shapes.Canvas;\n\nnamespace Nodify.Shapes.Controls;\n\npublic class CanvasToolItemSelector : IDataTemplate\n{\n    public IDataTemplate NoneTemplate { get; set; }\n    public IDataTemplate EllipseTemplate { get; set; }\n    public IDataTemplate RectangleTemplate { get; set; }\n    public IDataTemplate TriangleTemplate { get; set; }\n\n    public Control? Build(object? param)\n    {\n        if (param is CanvasTool tool)\n        {\n            return tool switch\n            {\n                CanvasTool.None => NoneTemplate.Build(null),\n                CanvasTool.Ellipse => EllipseTemplate.Build(null),\n                CanvasTool.Rectangle => RectangleTemplate.Build(null),\n                CanvasTool.Triangle => TriangleTemplate.Build(null),\n                _ => throw new ArgumentOutOfRangeException(nameof(tool), tool, null)\n            };\n        }\n\n        return null;\n    }\n\n    public bool Match(object? data)\n    {\n        return data is CanvasTool;\n    }\n}"
  },
  {
    "path": "Examples/Nodify.Shapes/Canvas/CanvasToolbarViewModel.cs",
    "content": "﻿using System;\nusing System.Linq;\nusing System.Windows;\nusing System.Windows.Input;\n\nnamespace Nodify.Shapes.Canvas\n{\n    public enum CanvasTool\n    {\n        None,\n        Ellipse,\n        Rectangle,\n        Triangle\n    }\n\n    public class CanvasToolbarViewModel : ObservableObject\n    {\n        public static readonly CanvasTool[] AvailableTools = Enum.GetValues<CanvasTool>().ToArray();\n\n        internal static readonly EditorGestures EditorGestures = new EditorGestures();\n\n        private bool _locked;\n        public bool Locked\n        {\n            get => _locked;\n            set\n            {\n                if (SetProperty(ref _locked, value))\n                {\n                    var newMappings = _locked ? LockedGestureMappings.Instance : EditorGestures;\n                    EditorGestures.Mappings.Apply(newMappings);\n\n                    if (_locked)\n                    {\n                        _selectedTool = CanvasTool.None;\n                        OnPropertyChanged(nameof(SelectedTool));\n                    }\n                }\n            }\n        }\n\n        public ICommand ToggleLockCommand { get; set; }\n\n        private CanvasTool _selectedTool = CanvasTool.None;\n        public CanvasTool SelectedTool\n        {\n            get => _selectedTool;\n            set\n            {\n                if (SetProperty(ref _selectedTool, Locked ? CanvasTool.None : value))\n                {\n                    var newMappings = _selectedTool == CanvasTool.None ? EditorGestures : DrawingGesturesMappings.Instance;\n                    EditorGestures.Mappings.Apply(newMappings);\n                }\n            }\n        }\n\n        public CanvasViewModel Canvas { get; }\n\n        public CanvasToolbarViewModel(CanvasViewModel canvas)\n        {\n            // copy any user modifications\n            EditorGestures.Apply(EditorGestures.Mappings);\n\n            ToggleLockCommand = new DelegateCommand(() => Locked = !Locked);\n            Canvas = canvas;\n        }\n\n        public ShapeViewModel CreateShapeAtLocation(Point location)\n        {\n            using (Canvas.UndoRedo.Batch(\"Create shape\"))\n            {\n                ShapeViewModel shape = SelectedTool switch\n                {\n                    CanvasTool.Ellipse => new EllipseViewModel(),\n                    CanvasTool.Rectangle => new RectangleViewModel(),\n                    CanvasTool.Triangle => new TriangleViewModel(),\n                    CanvasTool.None => throw new InvalidOperationException(\"Cannot draw in this state\"),\n                    _ => throw new NotImplementedException(nameof(CanvasTool)),\n                };\n\n                shape.Location = location;\n                shape.Text = \"Double click to edit\";\n\n                Canvas.AddShape(shape);\n                Canvas.SelectedShapes.Clear();\n                Canvas.SelectedShapes.Add(shape);\n\n                return shape;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.Shapes/Canvas/CanvasView.xaml",
    "content": "﻿<UserControl x:Class=\"Nodify.Shapes.Canvas.CanvasView\"\n             xmlns=\"https://github.com/avaloniaui\"\n             xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n             xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\"\n             xmlns:d=\"http://schemas.microsoft.com/expression/blend/2008\"\n             xmlns:local=\"clr-namespace:Nodify.Shapes.Canvas\"\n             xmlns:nodify=\"https://miroiu.github.io/nodify\"\n             xmlns:shared=\"clr-namespace:Nodify;assembly=Nodify.Shared\"\n             xmlns:controls=\"clr-namespace:Nodify.Shapes.Controls\"\n             xmlns:o=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation/options\"\n             mc:Ignorable=\"d\"\n             Background=\"#0a172a\"\n             Foreground=\"white\"\n             x:DataType=\"local:CanvasViewModel\"\n             d:DataContext=\"{d:DesignInstance Type=local:CanvasViewModel, IsDesignTimeCreatable=True}\"\n             d:DesignHeight=\"450\"\n             d:DesignWidth=\"800\">\n    <Grid>\n        <Grid.Resources>\n            <shared:BooleanToVisibilityConverter x:Key=\"BooleanToVisibilityConverter\" />\n            <shared:InverseBooleanConverter x:Key=\"InverseBooleanConverter\" />\n            <shared:ColorToSolidColorBrushConverter x:Key=\"ColorToSolidColorBrushConverter\" />\n\n            <!-- This is a copy paste of a ControlTheme defined below in ControlTheme.Resources, because it seems to be bugged in Avalonia and this ControlTheme is ignored when it is defined inside the ControlTheme.Resources -->\n            <ControlTheme TargetType=\"{x:Type nodify:Connector}\" x:Key=\"{x:Type nodify:Connector}\"\n                   BasedOn=\"{StaticResource {x:Type nodify:Connector}}\">\n                <Setter Property=\"BorderBrush\"\n                        Value=\"{Binding BorderBrush, RelativeSource={RelativeSource AncestorType=nodify:ItemContainer}}\" />\n                <Setter Property=\"Background\"\n                        Value=\"{Binding BorderBrush, RelativeSource={RelativeSource AncestorType=nodify:ItemContainer}}\" />\n                <Setter Property=\"IsConnected\"\n                        Value=\"True\" />\n                <Setter Property=\"Template\">\n                    <Setter.Value>\n                        <ControlTemplate>\n                            <Grid Background=\"Transparent\">\n                                <Ellipse x:Name=\"PART_Connector\"\n                                         Width=\"14\"\n                                         Height=\"14\"\n                                         Stroke=\"{TemplateBinding BorderBrush}\"\n                                         Fill=\"{TemplateBinding Background}\"\n                                         HorizontalAlignment=\"{TemplateBinding HorizontalContentAlignment}\"\n                                         VerticalAlignment=\"{TemplateBinding VerticalContentAlignment}\" />\n                            </Grid>\n                        </ControlTemplate>\n                    </Setter.Value>\n                </Setter>\n            </ControlTheme>\n\n            <ControlTheme TargetType=\"{x:Type shared:Resizer}\" x:Key=\"{x:Type shared:Resizer}\">\n                <Setter Property=\"Background\"\n                        Value=\"{Binding BorderBrush, RelativeSource={RelativeSource AncestorType=nodify:ItemContainer}}\" />\n                <Setter Property=\"IsEnabled\"\n                        Value=\"{Binding ((local:CanvasViewModel)DataContext).CanvasToolbar.Locked, ElementName=Editor, Converter={StaticResource InverseBooleanConverter}}\" />\n            </ControlTheme>\n\n            <!--For some reason, the designer doesn't like having these bindings on the element itself but in a style-->\n            <ControlTheme TargetType=\"{x:Type controls:ResizableContainer}\" x:Key=\"{x:Type controls:ResizableContainer}\"\n                   BasedOn=\"{StaticResource {x:Type shared:ResizablePanel}}\">\n                <Setter Property=\"BorderBrush\"\n                        Value=\"{Binding BorderBrush, RelativeSource={RelativeSource AncestorType=nodify:ItemContainer}}\" />\n                <Setter Property=\"Padding\"\n                        Value=\"3\" />\n                <Setter Property=\"ResizeStartedCommand\"\n                        Value=\"{Binding ((local:CanvasViewModel)DataContext).ResizeShapeStartedCommand, ElementName=Editor}\" />\n                <Setter Property=\"ResizeCompletedCommand\"\n                        Value=\"{Binding ((local:CanvasViewModel)DataContext).ResizeShapeCompletedCommand, ElementName=Editor}\" />\n            </ControlTheme>\n            <!-- end of copy-paste -->\n\n            <VisualBrush x:Key=\"GridDrawingBrush\"\n                         TileMode=\"Tile\"\n                         DestinationRect=\"0 0 30 30\"\n                         SourceRect=\"0 0 30 30\"\n                         Transform=\"{Binding DpiScaledViewportTransform, ElementName=Editor}\">\n                <VisualBrush.Visual>\n                    <Rectangle Width=\"1\"\n                               Height=\"1\"\n                               Fill=\"White\" />\n                </VisualBrush.Visual>\n            </VisualBrush>\n        </Grid.Resources>\n\n        <nodify:NodifyEditor ItemsSource=\"{Binding Shapes}\"\n                             SelectedItems=\"{Binding SelectedShapes}\"\n                             Connections=\"{Binding Connections}\"\n                             Decorators=\"{Binding Decorators}\"\n                             Background=\"{StaticResource GridDrawingBrush}\"\n                             ConnectionCompletedCommand=\"{Binding CreateConnectionCommand}\"\n                             ItemsDragStartedCommand=\"{Binding MoveShapesStartedCommand}\"\n                             ItemsDragCompletedCommand=\"{Binding MoveShapesCompletedCommand}\"\n                             ItemsSelectStartedCommand=\"{Binding SelectShapesStartedCommand}\"\n                             ItemsSelectCompletedCommand=\"{Binding SelectShapesCompletedCommand}\"\n                             RemoveConnectionCommand=\"{Binding RemoveConnectionCommand}\"\n                             PointerPressed=\"Editor_MouseDown\"\n                             PointerMoved=\"Editor_MouseMove\"\n                             PointerReleased=\"Editor_MouseUp\"\n                             GridCellSize=\"5\"\n                             x:Name=\"Editor\">\n            <nodify:NodifyEditor.Theme>\n                <ControlTheme TargetType=\"{x:Type nodify:NodifyEditor}\"\n                       BasedOn=\"{StaticResource {x:Type nodify:NodifyEditor}}\">\n                    <Setter Property=\"Cursor\"\n                            Value=\"Cross\" />\n                    <Setter Property=\"(Interaction.Behaviors)\">\n                        <BehaviorCollectionTemplate>\n                            <BehaviorCollection>\n                                <DataTrigger Property=\"CanvasToolbar.SelectedTool\" Value=\"{x:Static local:CanvasTool.None}\">\n                                    <PropertySetter Property=\"Cursor\"\n                                                    Value=\"{x:Static controls:Cursors.Arrow}\" />\n                                </DataTrigger>\n                            </BehaviorCollection>\n                        </BehaviorCollectionTemplate>\n                    </Setter>\n                </ControlTheme>\n            </nodify:NodifyEditor.Theme>\n\n            <nodify:NodifyEditor.DataTemplates>\n                <DataTemplate DataType=\"{x:Type local:EllipseViewModel}\">\n                    <Ellipse Stretch=\"Fill\"\n                             Fill=\"{Binding Color, Converter={StaticResource ColorToSolidColorBrushConverter}}\"\n                             Stroke=\"{Binding BorderColor, Converter={StaticResource ColorToSolidColorBrushConverter}}\"\n                             StrokeThickness=\"2\"\n                             Opacity=\"0.8\" />\n                </DataTemplate>\n\n                <DataTemplate DataType=\"{x:Type local:RectangleViewModel}\">\n                    <Rectangle Stretch=\"Fill\"\n                               Fill=\"{Binding Color, Converter={StaticResource ColorToSolidColorBrushConverter}}\"\n                               Stroke=\"{Binding BorderColor, Converter={StaticResource ColorToSolidColorBrushConverter}}\"\n                               StrokeThickness=\"2\"\n                               Opacity=\"0.8\" />\n                </DataTemplate>\n\n                <DataTemplate DataType=\"{x:Type local:TriangleViewModel}\">\n                    <Polygon Points=\"0,100 50,0 100,100\"\n                             Stretch=\"Fill\"\n                             Fill=\"{Binding Color, Converter={StaticResource ColorToSolidColorBrushConverter}}\"\n                             Stroke=\"{Binding BorderColor, Converter={StaticResource ColorToSolidColorBrushConverter}}\"\n                             StrokeThickness=\"2\"\n                             Opacity=\"0.8\" />\n                </DataTemplate>\n\n                <DataTemplate DataType=\"{x:Type local:ShapeToolbarViewModel}\">\n                    <Canvas IsVisible=\"{Binding Shape, Converter={StaticResource BooleanToVisibilityConverter}}\">\n                        <shared:Swatches SelectedColor=\"{Binding Shape.Color}\"\n                                         Colors=\"{x:Static local:ShapeViewModel.Colors}\"\n                                         IsEnabled=\"{Binding ((local:CanvasViewModel)DataContext).CanvasToolbar.Locked, ElementName=Editor, Converter={StaticResource InverseBooleanConverter}}\"\n                                         Canvas.Top=\"-70\"\n                                         ZIndex=\"1\">\n                            <shared:Swatches.Effect>\n                                <DropShadowDirectionEffect ShadowDepth=\"1\" />\n                            </shared:Swatches.Effect>\n                        </shared:Swatches>\n                    </Canvas>\n                </DataTemplate>\n\n                <DataTemplate DataType=\"{x:Type local:UserCursorViewModel}\">\n                    <StackPanel IsHitTestVisible=\"False\">\n                        <Viewbox Width=\"24\"\n                                 Height=\"24\"\n                                 Margin=\"-10 0 0 5\"\n                                 Stretch=\"Fill\"\n                                 HorizontalAlignment=\"Left\">\n                            <Path Fill=\"{Binding Color, Converter={StaticResource ColorToSolidColorBrushConverter}}\"\n                                  Stroke=\"White\"\n                                  StrokeJoin=\"Round\"\n                                  StrokeLineCap=\"Round\"\n                                  Data=\"M.256.255a.874.874 0 0 0-.18.974l4.753 17.114a.875.875 0 0 0 1.603-.012L10 10l8.334-3.57a.875.875 0 0 0 .01-1.601L1.23.075a.874.874 0 0 0-.974.18Z\" />\n                        </Viewbox>\n                        <Border CornerRadius=\"3\"\n                                Background=\"{Binding Color, Converter={StaticResource ColorToSolidColorBrushConverter}, ConverterParameter=0.7}\"\n                                Padding=\"6 2\">\n                            <TextBlock Text=\"{Binding Name}\" />\n                        </Border>\n                    </StackPanel>\n                </DataTemplate>\n\n            </nodify:NodifyEditor.DataTemplates>\n            <nodify:NodifyEditor.Resources>\n                <ControlTheme TargetType=\"{x:Type nodify:PendingConnection}\" x:Key=\"{x:Type nodify:PendingConnection}\"\n                       BasedOn=\"{StaticResource {x:Type nodify:PendingConnection}}\">\n                    <Setter Property=\"Stroke\">\n                        <Setter.Value>\n                            <SolidColorBrush Color=\"White\"\n                                             Opacity=\"0.7\" />\n                        </Setter.Value>\n                    </Setter>\n                </ControlTheme>\n            </nodify:NodifyEditor.Resources>\n\n            <nodify:NodifyEditor.ConnectionTemplate>\n                <DataTemplate DataType=\"{x:Type local:ConnectionViewModel}\">\n                    <nodify:StepConnection Source=\"{Binding Source.Anchor}\"\n                                           Target=\"{Binding Target.Anchor}\"\n                                           SourcePosition=\"{Binding Source.Position}\"\n                                           TargetPosition=\"{Binding Target.Position}\"\n                                           Cursor=\"Hand\"\n                                           nodify:BaseConnection.IsSelectable=\"True\"\n                                           Fill=\"Transparent\"\n                                           StrokeThickness=\"3\">\n                        <nodify:StepConnection.Stroke>\n                            <SolidColorBrush Color=\"White\"\n                                             Opacity=\"0.7\" />\n                        </nodify:StepConnection.Stroke>\n                        <nodify:StepConnection.Theme>\n                            <ControlTheme TargetType=\"{x:Type nodify:StepConnection}\">\n                                <Setter Property=\"OutlineBrush\"\n                                        Value=\"Transparent\" />\n                                <Style Selector=\"^:pointerover\">\n                                    <Setter Property=\"OutlineBrush\">\n                                        <Setter.Value>\n                                            <SolidColorBrush Color=\"White\"\n                                                             Opacity=\"0.15\" />\n                                        </Setter.Value>\n                                    </Setter>\n                                </Style>\n                                <Style Selector=\"^[(nodify|BaseConnection.IsSelected)=True]\">\n                                    <Setter Property=\"OutlineBrush\">\n                                        <Setter.Value>\n                                            <SolidColorBrush Color=\"DodgerBlue\"\n                                                             Opacity=\"0.25\" />\n                                        </Setter.Value>\n                                    </Setter>\n                                </Style>\n                            </ControlTheme>\n                        </nodify:StepConnection.Theme>\n                    </nodify:StepConnection>\n                </DataTemplate>\n            </nodify:NodifyEditor.ConnectionTemplate>\n\n            <nodify:NodifyEditor.DecoratorContainerStyle>\n                <ControlTheme TargetType=\"{x:Type nodify:DecoratorContainer}\"\n                       BasedOn=\"{StaticResource {x:Type nodify:DecoratorContainer}}\">\n                    <Setter Property=\"Location\" x:DataType=\"local:ICanvasDecorator\"\n                            Value=\"{Binding Location}\" />\n                </ControlTheme>\n            </nodify:NodifyEditor.DecoratorContainerStyle>\n\n            <nodify:NodifyEditor.ItemContainerTheme>\n                <ControlTheme TargetType=\"{x:Type nodify:ItemContainer}\" x:DataType=\"local:ShapeViewModel\"\n                       BasedOn=\"{StaticResource {x:Type nodify:ItemContainer}}\">\n                    <ControlTheme.Resources>\n                        <ControlTheme TargetType=\"{x:Type nodify:Connector}\" x:Key=\"{x:Type nodify:Connector}\"\n                               BasedOn=\"{StaticResource {x:Type nodify:Connector}}\">\n                            <Setter Property=\"BorderBrush\"\n                                    Value=\"{Binding BorderBrush, RelativeSource={RelativeSource AncestorType=nodify:ItemContainer}}\" />\n                            <Setter Property=\"Background\"\n                                    Value=\"{Binding BorderBrush, RelativeSource={RelativeSource AncestorType=nodify:ItemContainer}}\" />\n                            <Setter Property=\"IsConnected\"\n                                    Value=\"True\" />\n                            <Setter Property=\"Template\">\n                                <Setter.Value>\n                                    <ControlTemplate>\n                                        <Grid Background=\"Transparent\">\n                                            <Ellipse x:Name=\"PART_Connector\"\n                                                     Width=\"14\"\n                                                     Height=\"14\"\n                                                     Stroke=\"{TemplateBinding BorderBrush}\"\n                                                     Fill=\"{TemplateBinding Background}\"\n                                                     HorizontalAlignment=\"{TemplateBinding HorizontalContentAlignment}\"\n                                                     VerticalAlignment=\"{TemplateBinding VerticalContentAlignment}\" />\n                                        </Grid>\n                                    </ControlTemplate>\n                                </Setter.Value>\n                            </Setter>\n                        </ControlTheme>\n\n                        <ControlTheme TargetType=\"{x:Type shared:Resizer}\" x:Key=\"{x:Type shared:Resizer}\"\n                               BasedOn=\"{StaticResource {x:Type shared:Resizer}}\">\n                            <Setter Property=\"Background\"\n                                    Value=\"{Binding BorderBrush, RelativeSource={RelativeSource AncestorType=nodify:ItemContainer}}\" />\n                            <Setter Property=\"IsEnabled\"\n                                    Value=\"{Binding ((local:CanvasViewModel)DataContext).CanvasToolbar.Locked, ElementName=Editor, Converter={StaticResource InverseBooleanConverter}}\" />\n                        </ControlTheme>\n\n                        <!--For some reason, the designer doesn't like having these bindings on the element itself but in a style-->\n                        <ControlTheme TargetType=\"{x:Type controls:ResizableContainer}\" x:Key=\"{x:Type controls:ResizableContainer}\"\n                               BasedOn=\"{StaticResource {x:Type shared:ResizablePanel}}\">\n                            <Setter Property=\"BorderBrush\"\n                                    Value=\"{Binding BorderBrush, RelativeSource={RelativeSource AncestorType=nodify:ItemContainer}}\" />\n                            <Setter Property=\"Padding\"\n                                    Value=\"3\" />\n                            <Setter Property=\"ResizeStartedCommand\"\n                                    Value=\"{Binding ((local:CanvasViewModel)DataContext).ResizeShapeStartedCommand, ElementName=Editor}\" />\n                            <Setter Property=\"ResizeCompletedCommand\"\n                                    Value=\"{Binding ((local:CanvasViewModel)DataContext).ResizeShapeCompletedCommand, ElementName=Editor}\" />\n                        </ControlTheme>\n                    </ControlTheme.Resources>\n                    <Setter Property=\"Location\"\n                            Value=\"{Binding Location}\" />\n                    <Setter Property=\"IsSelected\"\n                            Value=\"{Binding IsSelected}\" />\n                    <Setter Property=\"SelectedBrush\"\n                            Value=\"{Binding BorderColor, Converter={StaticResource ColorToSolidColorBrushConverter}}\" />\n                    <Setter Property=\"SelectedBorderThickness\"\n                            Value=\"1\" />\n                    <Setter Property=\"BorderBrush\"\n                            Value=\"Transparent\" />\n                    <Setter Property=\"Template\">\n                        <Setter.Value>\n                            <ControlTemplate TargetType=\"{x:Type nodify:ItemContainer}\">\n                                <controls:ResizableContainer Height=\"{Binding Height, Mode=TwoWay}\"\n                                                             Width=\"{Binding Width, Mode=TwoWay}\"\n                                                             Theme=\"{StaticResource {x:Type controls:ResizableContainer}}\"\n                                                             Directions=\"Corners\"\n                                                             GridCellSize=\"5\">\n                                    <Grid>\n                                        <ContentPresenter Cursor=\"Hand\" Content=\"{TemplateBinding Content}\" ContentTemplate=\"{TemplateBinding ContentTemplate}\" />\n\n                                        <shared:EditableTextBlock Text=\"{Binding Text}\"\n                                                                  IsEnabled=\"{Binding ((local:CanvasViewModel)DataContext).CanvasToolbar.Locked, ElementName=Editor, Converter={StaticResource InverseBooleanConverter}}\"\n                                                                  HorizontalAlignment=\"Center\"\n                                                                  VerticalAlignment=\"Center\"\n                                                                  HorizontalContentAlignment=\"Center\"\n                                                                  FontSize=\"16\"\n                                                                  ToolTip.Tip=\"Double click to edit\" />\n\n                                        <DockPanel LastChildFill=\"False\" x:DataType=\"local:ShapeViewModel\">\n                                            <nodify:Connector DataContext=\"{Binding LeftConnector}\"\n                                                              Anchor=\"{Binding Anchor, Mode=OneWayToSource}\"\n                                                              DockPanel.Dock=\"Left\"\n                                                              HorizontalContentAlignment=\"Left\"\n                                                              Margin=\"-11 0 0 0\"\n                                                              Opacity=\"0\"\n                                                              Width=\"25\"\n                                                              Height=\"25\"\n                                                              x:Name=\"LeftConnector\" />\n\n                                            <nodify:Connector DataContext=\"{Binding RightConnector}\"\n                                                              Anchor=\"{Binding Anchor, Mode=OneWayToSource}\"\n                                                              DockPanel.Dock=\"Right\"\n                                                              HorizontalContentAlignment=\"Right\"\n                                                              Margin=\"0 0 -11 0\"\n                                                              Opacity=\"0\"\n                                                              Width=\"25\"\n                                                              Height=\"25\"\n                                                              x:Name=\"RightConnector\" />\n\n                                            <nodify:Connector DataContext=\"{Binding TopConnector}\"\n                                                              Anchor=\"{Binding Anchor, Mode=OneWayToSource}\"\n                                                              DockPanel.Dock=\"Top\"\n                                                              VerticalContentAlignment=\"Top\"\n                                                              Margin=\"0 -11 0 0\"\n                                                              Opacity=\"0\"\n                                                              Width=\"25\"\n                                                              Height=\"25\"\n                                                              x:Name=\"TopConnector\" />\n\n                                            <nodify:Connector DataContext=\"{Binding BottomConnector}\"\n                                                              Anchor=\"{Binding Anchor, Mode=OneWayToSource}\"\n                                                              DockPanel.Dock=\"Bottom\"\n                                                              VerticalContentAlignment=\"Bottom\"\n                                                              Margin=\"0 0 0 -11\"\n                                                              Opacity=\"0\"\n                                                              Width=\"25\"\n                                                              Height=\"25\"\n                                                              x:Name=\"BottomConnector\" />\n                                        </DockPanel>\n                                    </Grid>\n                                </controls:ResizableContainer>\n                            </ControlTemplate>\n                        </Setter.Value>\n                    </Setter>\n                    <Style Selector=\"^:selected:null-previewing-selection\">\n                        <Style Selector=\"^ nodify|Connector\">\n                            <Setter Property=\"Opacity\" Value=\"1\" />\n                        </Style>\n                    </Style>\n                    <Style Selector=\"^:previewing-selection\">\n                        <Style Selector=\"^ nodify|Connector\">\n                            <Setter Property=\"Opacity\" Value=\"1\" />\n                        </Style>\n                    </Style>\n                    <Style Selector=\"^:selected\">\n                        <Setter Property=\"Panel.ZIndex\"\n                                Value=\"1\" />\n                    </Style>\n                </ControlTheme>\n            </nodify:NodifyEditor.ItemContainerTheme>\n        </nodify:NodifyEditor>\n\n        <!--MINIMAP-->\n        <shared:ResizablePanel Directions=\"BottomLeft\"\n                               VerticalAlignment=\"Top\"\n                               HorizontalAlignment=\"Right\"\n                               Width=\"300\"\n                               Height=\"200\"\n                               MinWidth=\"250\"\n                               MinHeight=\"150\"\n                               BorderBrush=\"{x:Null}\"\n                               Margin=\"20\">\n            <shared:ResizablePanel.Resources>\n                <ControlTheme TargetType=\"{x:Type shared:Resizer}\" BasedOn=\"{StaticResource {x:Type shared:Resizer}}\" x:Key=\"{x:Type shared:Resizer}\">\n                    <Setter Property=\"Cursor\"\n                            Value=\"SizeAll\" />\n                    <Setter Property=\"Template\">\n                        <Setter.Value>\n                            <ControlTemplate TargetType=\"{x:Type shared:Resizer}\">\n                                <Grid Margin=\"-3 -6 -6 -3\"\n                                      Background=\"Transparent\">\n                                    <Rectangle Height=\"12\"\n                                               Width=\"3\"\n                                               Fill=\"#d2d4d7\"\n                                               HorizontalAlignment=\"Left\"\n                                               VerticalAlignment=\"Bottom\" />\n                                    <Rectangle Height=\"3\"\n                                               Width=\"12\"\n                                               Fill=\"#d2d4d7\"\n                                               VerticalAlignment=\"Bottom\"\n                                               HorizontalAlignment=\"Left\" />\n                                </Grid>\n                            </ControlTemplate>\n                        </Setter.Value>\n                    </Setter>\n                </ControlTheme>\n            </shared:ResizablePanel.Resources>\n            <Border CornerRadius=\"3\"\n                    BorderBrush=\"#1e293b\"\n                    BorderThickness=\"3\">\n                <Border.Effect>\n                    <DropShadowDirectionEffect ShadowDepth=\"1\" />\n                </Border.Effect>\n\n                <nodify:Minimap ItemsSource=\"{Binding ItemsSource, ElementName=Editor}\"\n                                ViewportLocation=\"{Binding ViewportLocation, ElementName=Editor}\"\n                                ViewportSize=\"{Binding ViewportSize, ElementName=Editor}\"\n                                ResizeToViewport=\"True\"\n                                Zoom=\"Minimap_Zoom\">\n                    <nodify:Minimap.DataTemplates>\n                        <DataTemplate DataType=\"{x:Type local:EllipseViewModel}\">\n                            <Ellipse Stretch=\"Fill\"\n                                     Fill=\"{Binding Color, Converter={StaticResource ColorToSolidColorBrushConverter}}\"\n                                     Stroke=\"{Binding BorderColor, Converter={StaticResource ColorToSolidColorBrushConverter}}\"\n                                     StrokeThickness=\"2\"\n                                     Opacity=\"0.8\" />\n                        </DataTemplate>\n\n                        <DataTemplate DataType=\"{x:Type local:RectangleViewModel}\">\n                            <Rectangle Stretch=\"Fill\"\n                                       Fill=\"{Binding Color, Converter={StaticResource ColorToSolidColorBrushConverter}}\"\n                                       Stroke=\"{Binding BorderColor, Converter={StaticResource ColorToSolidColorBrushConverter}}\"\n                                       StrokeThickness=\"2\"\n                                       Opacity=\"0.8\" />\n                        </DataTemplate>\n\n                        <DataTemplate DataType=\"{x:Type local:TriangleViewModel}\">\n                            <Polygon Points=\"0,100 50,0 100,100\"\n                                     Stretch=\"Fill\"\n                                     Fill=\"{Binding Color, Converter={StaticResource ColorToSolidColorBrushConverter}}\"\n                                     Stroke=\"{Binding BorderColor, Converter={StaticResource ColorToSolidColorBrushConverter}}\"\n                                     StrokeThickness=\"2\"\n                                     Opacity=\"0.8\" />\n                        </DataTemplate>\n                    </nodify:Minimap.DataTemplates>\n                    <nodify:Minimap.Background>\n                        <SolidColorBrush Color=\"#111a2d\"\n                                         Opacity=\"0.5\" />\n                    </nodify:Minimap.Background>\n                    <nodify:Minimap.ItemContainerTheme>\n                        <ControlTheme TargetType=\"{x:Type nodify:MinimapItem}\"\n                                      x:DataType=\"local:ShapeViewModel\">\n                            <Setter Property=\"Location\"\n                                    Value=\"{Binding Location}\" />\n                            <Setter Property=\"Width\"\n                                    Value=\"{Binding Width}\" />\n                            <Setter Property=\"Height\"\n                                    Value=\"{Binding Height}\" />\n                        </ControlTheme>\n                    </nodify:Minimap.ItemContainerTheme>\n                    <nodify:Minimap.ViewportStyle>\n                        <ControlTheme TargetType=\"Rectangle\">\n                            <Setter Property=\"StrokeThickness\"\n                                    Value=\"3\" />\n                            <Setter Property=\"Fill\">\n                                <Setter.Value>\n                                    <SolidColorBrush Color=\"#445e87\"\n                                                     Opacity=\"0.3\" />\n                                </Setter.Value>\n                            </Setter>\n                        </ControlTheme>\n                    </nodify:Minimap.ViewportStyle>\n                </nodify:Minimap>\n            </Border>\n        </shared:ResizablePanel>\n\n        <!--TOOLBARS-->\n\n        <Border VerticalAlignment=\"Bottom\"\n                HorizontalAlignment=\"Center\"\n                Margin=\"0 0 0 20\"\n                CornerRadius=\"3\"\n                Background=\"#1e293b\">\n            <Border.Effect>\n                <DropShadowDirectionEffect ShadowDepth=\"1\" />\n            </Border.Effect>\n            <StackPanel Orientation=\"Horizontal\">\n                <StackPanel.Resources>\n                    <ControlTheme TargetType=\"{x:Type Button}\" x:Key=\"{x:Type Button}\"\n                           BasedOn=\"{StaticResource IconButton}\">\n                        <Setter Property=\"Margin\"\n                                Value=\"2\" />\n                        <Setter Property=\"Width\"\n                                Value=\"32\" />\n                        <Setter Property=\"Height\"\n                                Value=\"32\" />\n                    </ControlTheme>\n                </StackPanel.Resources>\n\n                <ListBox BorderThickness=\"0\"\n                         Background=\"Transparent\"\n                         ItemsSource=\"{x:Static local:CanvasToolbarViewModel.AvailableTools}\"\n                         SelectedValue=\"{Binding CanvasToolbar.SelectedTool}\">\n                    <ListBox.ItemContainerTheme>\n                        <ControlTheme TargetType=\"ListBoxItem\" BasedOn=\"{StaticResource {x:Type ListBoxItem}}\">\n                            <Setter Property=\"Margin\"\n                                    Value=\"2\" />\n                            <Setter Property=\"Padding\"\n                                    Value=\"0\" />\n                            <Setter Property=\"Width\"\n                                    Value=\"32\" />\n                            <Setter Property=\"Height\"\n                                    Value=\"32\" />\n                        </ControlTheme>\n                    </ListBox.ItemContainerTheme>\n\n                    <ListBox.Resources>\n                        <DataTemplate x:Key=\"Cursor\">\n                            <Border CornerRadius=\"3\"\n                                    Padding=\"5\">\n                                <ContentPresenter ContentTemplate=\"{StaticResource CursorIcon}\" />\n                            </Border>\n                        </DataTemplate>\n                        <DataTemplate x:Key=\"Circle\">\n                            <Border CornerRadius=\"3\"\n                                    Padding=\"5\">\n                                <ContentPresenter ContentTemplate=\"{StaticResource CircleIcon}\" />\n                            </Border>\n                        </DataTemplate>\n                        <DataTemplate x:Key=\"Square\">\n                            <Border CornerRadius=\"3\"\n                                    Padding=\"5\">\n                                <ContentPresenter ContentTemplate=\"{StaticResource SquareIcon}\" />\n                            </Border>\n                        </DataTemplate>\n                        <DataTemplate x:Key=\"Triangle\">\n                            <Border CornerRadius=\"3\"\n                                    Padding=\"5\">\n                                <ContentPresenter ContentTemplate=\"{StaticResource TriangleIcon}\" />\n                            </Border>\n                        </DataTemplate>\n                    </ListBox.Resources>\n\n                    <ListBox.ItemTemplate>\n                        <DataTemplate>\n                            <ContentControl Content=\"{Binding}\">\n                                <ContentControl.Theme>\n                                    <ControlTheme TargetType=\"ContentControl\">\n                                        <Setter Property=\"ContentTemplate\">\n                                            <controls:CanvasToolItemSelector NoneTemplate=\"{StaticResource Cursor}\"\n                                                                             EllipseTemplate=\"{StaticResource Circle}\"\n                                                                             RectangleTemplate=\"{StaticResource Square}\"\n                                                                             TriangleTemplate=\"{StaticResource Triangle}\" />\n                                        </Setter>\n                                    </ControlTheme>\n                                </ContentControl.Theme>\n                            </ContentControl>\n                        </DataTemplate>\n                    </ListBox.ItemTemplate>\n\n                    <ListBox.ItemsPanel>\n                        <ItemsPanelTemplate>\n                            <StackPanel Orientation=\"Horizontal\"\n                                        />\n                        </ItemsPanelTemplate>\n                    </ListBox.ItemsPanel>\n                </ListBox>\n\n                <WpfBtn Command=\"{Binding UndoCommand}\"\n                        CommandTarget=\"{Binding ElementName=Editor}\"\n                        ContentTemplate=\"{StaticResource ArrowBackIcon}\"\n                        ToolTip.Tip=\"CTRL+Z\"\n                        Padding=\"1\" />\n\n                <WpfBtn Command=\"{Binding RedoCommand}\"\n                        CommandTarget=\"{Binding ElementName=Editor}\"\n                        ContentTemplate=\"{StaticResource ArrowForwardIcon}\"\n                        ToolTip.Tip=\"CTRL+SHIFT+z\"\n                        Padding=\"1\" />\n\n            </StackPanel>\n        </Border>\n\n        <Border VerticalAlignment=\"Bottom\"\n                HorizontalAlignment=\"Left\"\n                Margin=\"20 0 0 20\"\n                CornerRadius=\"3\"\n                Background=\"#1e293b\">\n            <Border.Effect>\n                <DropShadowDirectionEffect ShadowDepth=\"1\" />\n            </Border.Effect>\n\n            <StackPanel>\n                <StackPanel.Resources>\n                    <ControlTheme TargetType=\"Button\" x:Key=\"{x:Type Button}\"\n                           BasedOn=\"{StaticResource IconButton}\">\n                        <Setter Property=\"Margin\"\n                                Value=\"2\" />\n                        <Setter Property=\"Width\"\n                                Value=\"32\" />\n                        <Setter Property=\"Height\"\n                                Value=\"32\" />\n                    </ControlTheme>\n                </StackPanel.Resources>\n\n                <WpfBtn Command=\"{x:Static nodify:EditorCommands.ZoomIn}\"\n                        CommandTarget=\"{Binding ElementName=Editor}\"\n                        ContentTemplate=\"{StaticResource PlusIcon}\" />\n\n                <WpfBtn Command=\"{x:Static nodify:EditorCommands.ZoomOut}\"\n                        CommandTarget=\"{Binding ElementName=Editor}\"\n                        ContentTemplate=\"{StaticResource MinusIcon}\" />\n\n                <WpfBtn Command=\"{x:Static nodify:EditorCommands.FitToScreen}\"\n                        CommandTarget=\"{Binding ElementName=Editor}\"\n                        ContentTemplate=\"{StaticResource MaximizeIcon}\" />\n\n                <Button Command=\"{Binding ToggleLockCommand}\"\n                        DataContext=\"{Binding CanvasToolbar}\">\n                    <Button.Theme>\n                        <ControlTheme TargetType=\"Button\"\n                               BasedOn=\"{StaticResource {x:Type Button}}\">\n                            <Setter Property=\"ContentTemplate\"\n                                    Value=\"{StaticResource LockOpenIcon}\" />\n                            <Setter Property=\"(Interaction.Behaviors)\">\n                                <BehaviorCollectionTemplate>\n                                    <BehaviorCollection>\n                                        <DataTrigger Property=\"Locked\" Value=\"True\">\n                                            <PropertySetter Property=\"ContentTemplate\"\n                                                            Value=\"{StaticResource LockIcon}\" />\n                                        </DataTrigger>\n                                    </BehaviorCollection>\n                                </BehaviorCollectionTemplate>\n                            </Setter>\n                        </ControlTheme>\n                    </Button.Theme>\n                </Button>\n            </StackPanel>\n        </Border>\n\n        <Border VerticalAlignment=\"Bottom\"\n                HorizontalAlignment=\"Right\"\n                Margin=\"0 0 20 20\"\n                Height=\"38\"\n                CornerRadius=\"3\">\n\n            <ItemsControl ItemsSource=\"{Binding Cursors}\">\n                <ItemsControl.ItemsPanel>\n                    <ItemsPanelTemplate>\n                        <StackPanel Orientation=\"Horizontal\" />\n                    </ItemsPanelTemplate>\n                </ItemsControl.ItemsPanel>\n\n                <ItemsControl.ItemTemplate>\n                    <DataTemplate DataType=\"{x:Type local:UserCursorViewModel}\">\n                        <Border CornerRadius=\"50\"\n                                Height=\"28\"\n                                Width=\"28\"\n                                Margin=\"-3\"\n                                Background=\"{Binding Color, Converter={StaticResource ColorToSolidColorBrushConverter}}\"\n                                Padding=\"6 2\">\n                            <Border.Effect>\n                                <DropShadowDirectionEffect ShadowDepth=\"1\" />\n                            </Border.Effect>\n                            <WpfBtn Command=\"{x:Static nodify:EditorCommands.BringIntoView}\"\n                                    CommandParameter=\"{Binding Location}\"\n                                    CommandTarget=\"{Binding ElementName=Editor}\"\n                                    Theme=\"{StaticResource IconButton}\"\n                                    ToolTip.Tip=\"{Binding Name}\"\n                                    Content=\"{Binding Name[0]}\"\n                                    HorizontalAlignment=\"Center\"\n                                    Foreground=\"White\"\n                                    Margin=\"-6 -2\" />\n                        </Border>\n                    </DataTemplate>\n                </ItemsControl.ItemTemplate>\n            </ItemsControl>\n        </Border>\n    </Grid>\n</UserControl>\n"
  },
  {
    "path": "Examples/Nodify.Shapes/Canvas/CanvasView.xaml.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Input;\nusing System.Windows.Threading;\nusing Avalonia.Controls.Primitives;\n\nnamespace Nodify.Shapes.Canvas\n{\n    public partial class CanvasView : UserControl\n    {\n        private readonly DispatcherTimer _moveToLocationTimer;\n        private readonly DispatcherTimer _generateLocationTimer;\n        private readonly Random _random = new Random();\n        private readonly Dictionary<UserCursorViewModel, Point> _moveToLocations = new Dictionary<UserCursorViewModel, Point>();\n\n        public CanvasView()\n        {\n            InitializeComponent();\n            _moveToLocationTimer = new DispatcherTimer(TimeSpan.FromSeconds(1d / 60d), DispatcherPriority.Background, OnMoveToLocationTick);\n            _generateLocationTimer = new DispatcherTimer(TimeSpan.FromSeconds(3), DispatcherPriority.Background, OnGenerateNewLocation);\n        }\n\n        protected override void OnApplyTemplate(TemplateAppliedEventArgs e)\n        {\n            _moveToLocationTimer.Start();\n            _generateLocationTimer.Start();\n\n            OnGenerateNewLocation(this, EventArgs.Empty);\n\n            base.OnApplyTemplate(e);\n        }\n\n        private void OnGenerateNewLocation(object? sender, EventArgs e)\n        {\n            var canvasVM = (CanvasViewModel)DataContext;\n\n            foreach (var cursor in canvasVM.Cursors)\n            {\n                _moveToLocations[cursor] = Editor.MouseLocation + new Vector(_random.Next(-1500, 1500), _random.Next(-1000, 1000));\n            }\n        }\n\n        private void OnMoveToLocationTick(object? sender, EventArgs e)\n        {\n            var canvasVM = (CanvasViewModel)DataContext;\n            double speed = 0.015d;\n\n            for (int i = 0; i < canvasVM.Cursors.Count; i++)\n            {\n                var user = canvasVM.Cursors[i];\n                var targetLocation = _moveToLocations[user];\n\n                Vector dir = targetLocation - user.Location;\n\n                var newLocation = user.Location + dir * speed;\n                user.Location = newLocation;\n            }\n        }\n\n        #region Drawing shapes\n\n        private ShapeViewModel? _drawingShape;\n        private Point _initialLocation;\n\n        private void Editor_MouseDown(object sender, PointerPressedEventArgs e)\n        {\n            var toolbarVm = ((CanvasViewModel)DataContext).CanvasToolbar;\n            if (toolbarVm.SelectedTool != CanvasTool.None && DrawingGesturesMappings.Instance.Draw.Matches(this, e))\n            {\n                _initialLocation = Editor.MouseLocation;\n                _drawingShape = toolbarVm.CreateShapeAtLocation(Editor.MouseLocation);\n            }\n        }\n\n        private void Editor_MouseMove(object sender, PointerEventArgs e)\n        {\n            if (_drawingShape != null)\n            {\n                _drawingShape.Width = Math.Abs(Editor.MouseLocation.X - _initialLocation.X);\n                _drawingShape.Height = Math.Abs(Editor.MouseLocation.Y - _initialLocation.Y);\n\n                if (Editor.MouseLocation.X < _initialLocation.X)\n                {\n                    _drawingShape.Location = new Point(Editor.MouseLocation.X, _drawingShape.Location.Y);\n                }\n\n                if (Editor.MouseLocation.Y < _initialLocation.Y)\n                {\n                    _drawingShape.Location = new Point(_drawingShape.Location.X, Editor.MouseLocation.Y);\n                }\n            }\n        }\n\n        private void Editor_MouseUp(object sender, PointerReleasedEventArgs e)\n        {\n            _drawingShape = null;\n        }\n\n        #endregion\n\n        private void Minimap_Zoom(object sender, ZoomEventArgs e)\n        {\n            Editor.ZoomAtPosition(e.Zoom, e.Location);\n        }\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.Shapes/Canvas/CanvasViewModel.cs",
    "content": "﻿using Nodify.Shapes.Canvas.UndoRedo;\nusing Nodify.UndoRedo;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Windows;\nusing System.Windows.Input;\n\nnamespace Nodify.Shapes.Canvas\n{\n    public class CanvasViewModel : ObservableObject\n    {\n        private readonly NodifyObservableCollection<ShapeViewModel> _shapes = new NodifyObservableCollection<ShapeViewModel>();\n        public IReadOnlyCollection<ShapeViewModel> Shapes => _shapes;\n        public NodifyObservableCollection<ShapeViewModel> SelectedShapes { get; } = new NodifyObservableCollection<ShapeViewModel>();\n\n        public NodifyObservableCollection<ICanvasDecorator> Decorators { get; } = new NodifyObservableCollection<ICanvasDecorator>();\n        public NodifyObservableCollection<ConnectionViewModel> Connections { get; } = new NodifyObservableCollection<ConnectionViewModel>();\n\n        private ShapeToolbarViewModel ShapeToolbar { get; } = new ShapeToolbarViewModel();\n        public CanvasToolbarViewModel CanvasToolbar { get; }\n\n        public ICommand MoveShapesStartedCommand { get; }\n        public ICommand MoveShapesCompletedCommand { get; }\n        public ICommand SelectShapesStartedCommand { get; }\n        public ICommand SelectShapesCompletedCommand { get; }\n        public ICommand ResizeShapeStartedCommand { get; }\n        public ICommand ResizeShapeCompletedCommand { get; }\n        public ICommand CreateConnectionCommand { get; }\n        public ICommand RemoveConnectionCommand { get; }\n        public ICommand DeleteSelectionCommand { get; }\n\n        public ICommand UndoCommand { get; }\n        public ICommand RedoCommand { get; }\n\n        public NodifyObservableCollection<UserCursorViewModel> Cursors { get; } = new NodifyObservableCollection<UserCursorViewModel>();\n        public IActionsHistory UndoRedo { get; } = ActionsHistory.Global;\n\n        private readonly Random _rand = new Random();\n\n        public CanvasViewModel()\n        {\n            CanvasToolbar = new CanvasToolbarViewModel(this);\n\n            UndoCommand = new RequeryCommand(UndoRedo.Undo, () => UndoRedo.CanUndo && !CanvasToolbar.Locked);\n            RedoCommand = new RequeryCommand(UndoRedo.Redo, () => UndoRedo.CanRedo && !CanvasToolbar.Locked);\n\n            MoveShapesStartedCommand = new DelegateCommand(MoveShapesStartedHandler);\n            MoveShapesCompletedCommand = new DelegateCommand(MoveShapesCompletedHandler);\n\n            SelectShapesStartedCommand = new DelegateCommand(SelectShapesStartedHandler);\n            SelectShapesCompletedCommand = new DelegateCommand(SelectShapesCompletedHandler);\n\n            ResizeShapeStartedCommand = new DelegateCommand(ResizeShapeStartedHandler);\n            ResizeShapeCompletedCommand = new DelegateCommand(ResizeShapeCompletedHandler);\n\n            DeleteSelectionCommand = new DelegateCommand(DeleteSelection, () => !CanvasToolbar.Locked);\n\n            CreateConnectionCommand = new DelegateCommand<(object Source, object Target)>(\n                ((object Source, object Target) pendingConnection) => AddConnection((ConnectorViewModel)pendingConnection.Source, (ConnectorViewModel)pendingConnection.Target),\n                ((object Source, object Target) pendingConnection) => CanConnect((ConnectorViewModel)pendingConnection.Source, (ConnectorViewModel)pendingConnection.Target));\n\n            RemoveConnectionCommand = new DelegateCommand<ConnectionViewModel>(RemoveConnection);\n\n            SelectedShapes.WhenAdded(shape =>\n            {\n                ShapeToolbar.Shape = SelectedShapes.Count == 1 ? shape : null;\n            });\n\n            SelectedShapes.WhenRemoved(shape =>\n            {\n                ShapeToolbar.Shape = SelectedShapes.Count == 1 ? SelectedShapes.Single() : null;\n            });\n\n            SelectedShapes.WhenCleared(shapes => ShapeToolbar.Shape = null);\n\n            FillCanvasWithShapes();\n        }\n\n        private void MoveShapesStartedHandler()\n        {\n            UndoRedo.ExecuteAction(new MoveShapesAction(this));\n            ShapeToolbar.Hide();\n        }\n\n        private void MoveShapesCompletedHandler()\n        {\n            if (UndoRedo.Current is MoveShapesAction movesShapes)\n            {\n                movesShapes.SaveLocations();\n            }\n\n            ShapeToolbar.Show();\n        }\n\n        // TODO: This does not fire for single selection (ItemContainer click), therefore I added IsSelected to ShapeViewModel.\n        private void SelectShapesStartedHandler()\n        {\n            // The workaround is to record all IsSelected changes into one history item using History.Pause and History.Resume\n            UndoRedo.Pause(\"Select shapes\");\n            //UndoRedo.ExecuteAction(new SelectShapesAction(this));\n        }\n\n        private void SelectShapesCompletedHandler()\n        {\n            UndoRedo.Resume();\n            //if (UndoRedo.Current is SelectShapesAction selectShapes)\n            //{\n            //    selectShapes.SaveSelection();\n            //}\n        }\n\n        private void ResizeShapeStartedHandler()\n        {\n            UndoRedo.ExecuteAction(new ResizeShapesAction(this));\n        }\n\n        private void ResizeShapeCompletedHandler()\n        {\n            if (UndoRedo.Current is ResizeShapesAction resizeShapes)\n            {\n                resizeShapes.SaveSizes();\n            }\n        }\n\n        private void FillCanvasWithShapes()\n        {\n            // Disable undo redo to avoid recording object construction\n            UndoRedo.IsEnabled = false;\n\n            var cursorCount = _rand.Next(3, 6);\n            for (int i = 0; i < cursorCount; i++)\n            {\n                var color = ShapeViewModel.Colors[_rand.Next(0, ShapeViewModel.Colors.Count)];\n                Cursors.Add(new UserCursorViewModel\n                {\n                    Name = $\"User {i + 1}\",\n                    Color = color,\n                    Location = new Point(_rand.Next(0, 1000), _rand.Next(0, 1000))\n                });\n            }\n\n            var ellipse = new EllipseViewModel\n            {\n                Location = new Point(100, 50),\n                Width = 150,\n                Height = 150\n            };\n            _shapes.Add(ellipse);\n\n            var rectangle = new RectangleViewModel\n            {\n                Location = new Point(400, 100),\n                Width = 150,\n                Height = 150\n            };\n            _shapes.Add(rectangle);\n\n            Connections.Add(new ConnectionViewModel(ellipse.RightConnector, rectangle.LeftConnector));\n\n            var ellipse2 = new EllipseViewModel\n            {\n                Location = new Point(100, 250),\n                Width = 150,\n                Height = 150\n            };\n            _shapes.Add(ellipse2);\n\n            var rectangle2 = new RectangleViewModel\n            {\n                Location = new Point(450, 400),\n                Width = 150,\n                Height = 150\n            };\n            _shapes.Add(rectangle2);\n\n            var triangle = new TriangleViewModel\n            {\n                Location = new Point(800, 200),\n                Width = 150,\n                Height = 150\n            };\n            _shapes.Add(triangle);\n\n            Connections.Add(new ConnectionViewModel(ellipse2.BottomConnector, rectangle2.TopConnector));\n            Connections.Add(new ConnectionViewModel(rectangle.RightConnector, rectangle2.RightConnector));\n\n            SelectedShapes.Add(triangle);\n\n            Decorators.Add(ShapeToolbar);\n            Decorators.AddRange(Cursors);\n\n            // Re-enable undo redo\n            UndoRedo.IsEnabled = true;\n        }\n\n        public void AddShape(ShapeViewModel shape)\n        {\n            var action = new DelegateAction(() => _shapes.Add(shape), () => _shapes.Remove(shape), \"Add shape\");\n            UndoRedo.ExecuteAction(action);\n        }\n\n        private void AddConnection(ConnectorViewModel source, ConnectorViewModel target)\n        {\n            var connection = new ConnectionViewModel(source, target);\n            var action = new DelegateAction(() => Connections.Add(connection), () => Connections.Remove(connection), \"Connect\");\n            UndoRedo.ExecuteAction(action);\n        }\n\n        private void RemoveConnection(ConnectionViewModel connection)\n        {\n            var action = new DelegateAction(() => Connections.Remove(connection), () => Connections.Add(connection), \"Disconnect\");\n            UndoRedo.ExecuteAction(action);\n        }\n\n        private bool CanConnect(ConnectorViewModel? source, ConnectorViewModel? target)\n        {\n            return source != null\n                && target != null\n                && source != target\n                && !Connections.Contains(new ConnectionViewModel(source, target));\n        }\n\n        public void DeleteSelection()\n        {\n            if (SelectedShapes.Count == 0)\n                return;\n\n            using (UndoRedo.Batch(\"Delete selection\"))\n            {\n                var selection = SelectedShapes.ToList();\n\n                var action = new DelegateAction(() => _shapes.RemoveRange(selection), () => _shapes.AddRange(selection), \"Delete shapes\");\n                UndoRedo.ExecuteAction(action);\n\n                foreach (var shape in selection)\n                {\n                    var connectors = new[] { shape.LeftConnector, shape.RightConnector, shape.TopConnector, shape.BottomConnector };\n                    var connectionsToRemove = Connections.Where(x => connectors.Contains(x.Source) || connectors.Contains(x.Target)).ToList();\n\n                    foreach (var connection in connectionsToRemove)\n                    {\n                        RemoveConnection(connection);\n                    }\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.Shapes/Canvas/ConnectionViewModel.cs",
    "content": "﻿using System;\n\nnamespace Nodify.Shapes.Canvas\n{\n    public class ConnectionViewModel : IEquatable<ConnectionViewModel>\n    {\n        public ConnectionViewModel(ConnectorViewModel source, ConnectorViewModel target)\n        {\n            Source = source;\n            Target = target;\n        }\n\n        public ConnectorViewModel Source { get; }\n        public ConnectorViewModel Target { get; }\n\n        public bool Equals(ConnectionViewModel? other)\n            => other?.Source == Source && other.Target == Target;\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.Shapes/Canvas/ConnectorViewModel.cs",
    "content": "﻿using System.Windows;\n\nnamespace Nodify.Shapes.Canvas\n{\n    public class ConnectorViewModel : ObservableObject\n    {\n        public ConnectorViewModel(ConnectorPosition position)\n        {\n            Position = position;\n        }\n\n        private Point _anchor;\n        public Point Anchor\n        {\n            get => _anchor;\n            set => SetProperty(ref _anchor, value);\n        }\n\n        public ConnectorPosition Position { get; }\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.Shapes/Canvas/Decorators/ICanvasDecorator.cs",
    "content": "﻿using System.Windows;\n\nnamespace Nodify.Shapes.Canvas\n{\n    public interface ICanvasDecorator\n    {\n        Point Location { get; set; }\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.Shapes/Canvas/Decorators/ShapeToolbarViewModel.cs",
    "content": "﻿using System.ComponentModel;\nusing System.Windows;\n\nnamespace Nodify.Shapes.Canvas\n{\n    public class ShapeToolbarViewModel : ObservableObject, ICanvasDecorator\n    {\n        private ShapeViewModel? _shape;\n        public ShapeViewModel? Shape\n        {\n            get => _shape;\n            set\n            {\n                var prevShape = _shape;\n                if (SetProperty(ref _shape, value))\n                {\n                    _hiddenShape = null;\n                    HookLocationEvents(prevShape, value);\n                }\n            }\n        }\n\n        private ShapeViewModel? _hiddenShape;\n\n        private Point _location;\n        public Point Location\n        {\n            get => _location;\n            set => SetProperty(ref _location, value);\n        }\n\n        private void HookLocationEvents(ShapeViewModel? prevShape, ShapeViewModel? newShape)\n        {\n            if (prevShape != null)\n                prevShape.PropertyChanged -= OnLocationChanged;\n\n            if (newShape != null)\n            {\n                newShape.PropertyChanged += OnLocationChanged;\n                Location = newShape.Location;\n            }\n        }\n\n        private void OnLocationChanged(object? sender, PropertyChangedEventArgs args)\n        {\n            if (args.PropertyName == nameof(ShapeViewModel.Location))\n                Location = ((ShapeViewModel)sender!).Location;\n        }\n\n        public void Hide()\n        {\n            _hiddenShape = _shape;\n            _shape = null;\n\n            OnPropertyChanged(nameof(Shape));\n        }\n\n        public void Show()\n        {\n            if (_hiddenShape != null)\n            {\n                _shape = _hiddenShape;\n                _hiddenShape = null;\n\n                OnPropertyChanged(nameof(Shape));\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.Shapes/Canvas/Decorators/UserCursorViewModel.cs",
    "content": "﻿using System.Windows;\nusing System.Windows.Media;\n\nnamespace Nodify.Shapes.Canvas\n{\n    public class UserCursorViewModel : ObservableObject, ICanvasDecorator\n    {\n        private Point _location;\n        public Point Location\n        {\n            get => _location;\n            set => SetProperty(ref _location, value);\n        }\n\n        public string? Name { get; set; }\n\n        public Color Color { get; set; }\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.Shapes/Canvas/Gestures/DrawingGesturesMappings.cs",
    "content": "﻿using System.Windows.Input;\n\nnamespace Nodify.Shapes.Canvas\n{\n    public class DrawingGesturesMappings : EditorGestures\n    {\n        public static readonly DrawingGesturesMappings Instance = new DrawingGesturesMappings();\n\n        public InputGestureRef Draw { get; }\n\n        public DrawingGesturesMappings()\n        {\n            Apply(UnboundGestureMappings.Instance);\n\n            Draw = new MouseGesture(MouseAction.LeftClick);\n        }\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.Shapes/Canvas/Gestures/LockedGestureMappings.cs",
    "content": "﻿using System.Windows.Input;\n\nnamespace Nodify.Shapes.Canvas\n{\n    public class LockedGestureMappings : EditorGestures\n    {\n        public static readonly LockedGestureMappings Instance = new LockedGestureMappings();\n\n        public LockedGestureMappings()\n        {\n            Apply(UnboundGestureMappings.Instance);\n\n            Editor.Pan.Value = new AnyGesture(new MouseGesture(MouseAction.LeftClick), new MouseGesture(MouseAction.RightClick), new MouseGesture(MouseAction.MiddleClick));\n        }\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.Shapes/Canvas/Gestures/UnboundGestureMappings.cs",
    "content": "﻿namespace Nodify.Shapes.Canvas\n{\n    public class UnboundGestureMappings : EditorGestures\n    {\n        public static readonly UnboundGestureMappings Instance = new UnboundGestureMappings();\n\n        public UnboundGestureMappings()\n        {\n            Editor.Selection.Apply(SelectionGestures.None);\n            ItemContainer.Selection.Apply(SelectionGestures.None);\n            Connection.Disconnect.Value = MultiGesture.None;\n            Connector.Connect.Value = MultiGesture.None;\n        }\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.Shapes/Canvas/Shapes/EllipseViewModel.cs",
    "content": "﻿using System.Windows.Media;\n\nnamespace Nodify.Shapes.Canvas\n{\n    public class EllipseViewModel : ShapeViewModel\n    {\n        public EllipseViewModel()\n        {\n            Color = Color.FromRgb(67, 141, 87);\n            Text = \"Ellipse\";\n        }\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.Shapes/Canvas/Shapes/RectangleViewModel.cs",
    "content": "﻿using System.Windows.Media;\n\nnamespace Nodify.Shapes.Canvas\n{\n    public class RectangleViewModel : ShapeViewModel\n    {\n        public RectangleViewModel()\n        {\n            Color = Color.FromRgb(63, 138, 226);\n            Text = \"Rectangle\";\n        }\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.Shapes/Canvas/Shapes/ShapeViewModel.cs",
    "content": "﻿using Nodify.UndoRedo;\nusing System.Collections.Generic;\nusing System.Windows;\nusing System.Windows.Media;\n\nnamespace Nodify.Shapes.Canvas\n{\n    public abstract class ShapeViewModel : Undoable\n    {\n        public ShapeViewModel(IActionsHistory history) : base(history)\n        {\n            RecordProperty<ShapeViewModel>(x => x.IsSelected);\n            RecordProperty<ShapeViewModel>(x => x.Color);\n            RecordProperty<ShapeViewModel>(x => x.Text);\n        }\n\n        public ShapeViewModel() : this(ActionsHistory.Global)\n        {\n        }\n\n        public static readonly IReadOnlyList<Color> Colors = new Color[]\n        {\n            Color.FromRgb(207, 76, 44),\n            Color.FromRgb(234, 156, 65),\n            Color.FromRgb(235, 195, 71),\n            Color.FromRgb(67, 141, 87),\n            Color.FromRgb(63, 138, 226),\n            Color.FromRgb(128, 61, 236),\n        };\n\n        private Point _location;\n        public Point Location\n        {\n            get => _location;\n            set => SetProperty(ref _location, value);\n        }\n\n        private double _width;\n        public double Width\n        {\n            get => _width;\n            set => SetProperty(ref _width, value);\n        }\n\n        private double _height;\n        public double Height\n        {\n            get => _height;\n            set => SetProperty(ref _height, value);\n        }\n\n        private Color _color;\n        public Color Color\n        {\n            get => _color;\n            set => SetProperty(ref _color, value).Then(x => OnPropertyChanged(nameof(BorderColor)));\n        }\n\n        public Color BorderColor => new Color((byte)Math.Clamp(Color.A * 1.5f, 0, 255), (byte)Math.Clamp(Color.R * 1.5f, 0, 255), (byte)Math.Clamp(Color.G * 1.5f, 0, 255), (byte)Math.Clamp(Color.B * 1.5f, 0, 255));\n\n        private string? _text;\n        public string? Text\n        {\n            get => _text;\n            set => SetProperty(ref _text, value);\n        }\n\n        private bool _isSelected;\n        public bool IsSelected\n        {\n            get => _isSelected;\n            set => SetProperty(ref _isSelected, value);\n        }\n\n        public ConnectorViewModel LeftConnector { get; } = new ConnectorViewModel(ConnectorPosition.Left);\n        public ConnectorViewModel RightConnector { get; } = new ConnectorViewModel(ConnectorPosition.Right);\n        public ConnectorViewModel TopConnector { get; } = new ConnectorViewModel(ConnectorPosition.Top);\n        public ConnectorViewModel BottomConnector { get; } = new ConnectorViewModel(ConnectorPosition.Bottom);\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.Shapes/Canvas/Shapes/TriangleViewModel.cs",
    "content": "﻿using System.Windows.Media;\n\nnamespace Nodify.Shapes.Canvas\n{\n    public class TriangleViewModel : ShapeViewModel\n    {\n        public TriangleViewModel()\n        {\n            Color = Color.FromRgb(235, 195, 71);\n            Text = \"Triangle\";\n        }\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.Shapes/Canvas/UndoRedo/MoveShapesAction.cs",
    "content": "﻿using Nodify.UndoRedo;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Windows;\n\nnamespace Nodify.Shapes.Canvas.UndoRedo\n{\n    public class MoveShapesAction : IAction\n    {\n        private readonly IReadOnlyCollection<ShapeViewModel> _movedShapes;\n        private readonly Dictionary<ShapeViewModel, Point> _initialLocations;\n        private Dictionary<ShapeViewModel, Point>? _finalLocations;\n\n        public MoveShapesAction(CanvasViewModel canvas)\n        {\n            _movedShapes = canvas.SelectedShapes;\n            _initialLocations = _movedShapes.ToDictionary(x => x, x => x.Location);\n        }\n\n        public string? Label => \"Move shapes\";\n\n        public void Execute()\n        {\n            _finalLocations?.ForEach(x => x.Key.Location = x.Value);\n        }\n\n        public void Undo()\n        {\n            _initialLocations.ForEach(x => x.Key.Location = x.Value);\n        }\n\n        public void SaveLocations()\n        {\n            _finalLocations = _movedShapes.ToDictionary(x => x, x => x.Location);\n        }\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.Shapes/Canvas/UndoRedo/ResizeShapesAction.cs",
    "content": "﻿using System.Collections.Generic;\nusing System.Linq;\nusing System.Windows;\nusing Nodify.UndoRedo;\n\nnamespace Nodify.Shapes.Canvas.UndoRedo\n{\n    public class ResizeShapesAction : IAction\n    {\n        private readonly IReadOnlyCollection<ShapeViewModel> _resizedShapes;\n        private readonly Dictionary<ShapeViewModel, Size> _initialSizes;\n        private Dictionary<ShapeViewModel, Size>? _finalSizes;\n\n        // Resizing could also move the shape\n        private readonly MoveShapesAction _moveShapesAction;\n\n        public ResizeShapesAction(CanvasViewModel canvas)\n        {\n            _resizedShapes = canvas.SelectedShapes;\n            _initialSizes = _resizedShapes.ToDictionary(x => x, x => new Size(x.Width, x.Height));\n\n            _moveShapesAction = new MoveShapesAction(canvas);\n        }\n\n        public string? Label => \"Resize shapes\";\n\n        public void Execute()\n        {\n            _moveShapesAction.Execute();\n\n            _finalSizes?.ForEach(x =>\n            {\n                x.Key.Width = x.Value.Width;\n                x.Key.Height = x.Value.Height;\n            });\n        }\n\n        public void Undo()\n        {\n            _initialSizes.ForEach(x =>\n            {\n                x.Key.Width = x.Value.Width;\n                x.Key.Height = x.Value.Height;\n            });\n\n            _moveShapesAction.Undo();\n        }\n\n        public void SaveSizes()\n        {\n            _moveShapesAction.SaveLocations();\n\n            _finalSizes = _resizedShapes.ToDictionary(x => x, x => new Size(x.Width, x.Height));\n        }\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.Shapes/Canvas/UndoRedo/SelectShapesAction.cs",
    "content": "﻿using Nodify.UndoRedo;\nusing System.Collections.Generic;\nusing System.Linq;\n\nnamespace Nodify.Shapes.Canvas.UndoRedo\n{\n    public class SelectShapesAction : IAction\n    {\n        private readonly CanvasViewModel _canvas;\n        private readonly List<ShapeViewModel> _initialSelection;\n        private List<ShapeViewModel>? _finalSelection;\n\n        public SelectShapesAction(CanvasViewModel canvas)\n        {\n            _canvas = canvas;\n            _initialSelection = _canvas.SelectedShapes.ToList();\n        }\n\n        public string? Label => \"Select shapes\";\n\n        public void Execute()\n        {\n            if (_finalSelection != null)\n            {\n                _canvas.SelectedShapes.Clear();\n                _canvas.SelectedShapes.AddRange(_finalSelection);\n            }\n        }\n\n        public void Undo()\n        {\n            _canvas.SelectedShapes.Clear();\n            _canvas.SelectedShapes.AddRange(_initialSelection);\n        }\n\n        public void SaveSelection()\n        {\n            _finalSelection = _canvas.SelectedShapes.ToList();\n        }\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.Shapes/Controls/ResizableContainer.cs",
    "content": "﻿using System.Windows;\n\nnamespace Nodify.Shapes.Controls\n{\n    internal class ResizableContainer : ResizablePanel\n    {\n        public static readonly AvaloniaProperty<uint> GridCellSizeProperty = NodifyEditor.GridCellSizeProperty.AddOwner<ResizableContainer>();\n\n        public uint GridCellSize\n        {\n            get => (uint)GetValue(GridCellSizeProperty);\n            set => SetValue(GridCellSizeProperty, value);\n        }\n\n        protected override void OnMove(double x, double y)\n        {\n            // we can't use the default behavior because we are not inside a Canvas\n            if (TemplatedParent is ItemContainer item)\n            {\n                item.Location = new Point(item.Location.X + x, item.Location.Y + y);\n            }\n        }\n\n        protected override void OnProcessDelta(ref double dx, ref double dy)\n        {\n            // snap to grid\n            dx = (int)dx / GridCellSize * GridCellSize;\n            dy = (int)dy / GridCellSize * GridCellSize;\n        }\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.Shapes/GlobalUsings.cs",
    "content": "global using Avalonia.Controls;\nglobal using Avalonia;\nglobal using Avalonia.Data.Converters;\nglobal using Avalonia.Markup.Xaml;\nglobal using System.Windows.Input;\nglobal using Avalonia.Interactivity;\nglobal using Avalonia.Controls.ApplicationLifetimes;\nglobal using Avalonia.Input;\nglobal using Avalonia.LogicalTree;\nglobal using Avalonia.Threading;\nglobal using Nodify.Compatibility;\nglobal using Avalonia.Media;\nglobal using System;\nglobal using MouseEventArgs = Avalonia.Input.PointerEventArgs;"
  },
  {
    "path": "Examples/Nodify.Shapes/MainView.axaml",
    "content": "<UserControl xmlns=\"https://github.com/avaloniaui\"\n             xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n             xmlns:d=\"http://schemas.microsoft.com/expression/blend/2008\"\n             xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\"\n             xmlns:local=\"clr-namespace:Nodify.Shapes\"\n             xmlns:canvas=\"clr-namespace:Nodify.Shapes.Canvas\"\n             x:DataType=\"local:AppShellViewModel\"\n             mc:Ignorable=\"d\" d:DesignWidth=\"800\" d:DesignHeight=\"450\"\n             x:Class=\"Nodify.Shapes.MainView\">\n\n    <UserControl.KeyBindings>\n        <KeyBinding Command=\"{Binding Canvas.RedoCommand}\"\n                    Gesture=\"Ctrl+Y\"/>\n        <KeyBinding Command=\"{Binding Canvas.RedoCommand}\"\n                    Gesture=\"Ctrl+Shift+Z\" />\n        <KeyBinding Command=\"{Binding Canvas.UndoCommand}\"\n                    Gesture=\"Ctrl+Z\" />\n        <KeyBinding Command=\"{Binding Canvas.DeleteSelectionCommand}\"\n                    Gesture=\"Delete\" />\n    </UserControl.KeyBindings>\n\n    <UserControl.DataContext>\n        <local:AppShellViewModel />\n    </UserControl.DataContext>\n\n    <Grid>\n        <canvas:CanvasView DataContext=\"{Binding Canvas}\" />\n    </Grid>\n</UserControl>\n"
  },
  {
    "path": "Examples/Nodify.Shapes/MainView.axaml.cs",
    "content": "using Avalonia;\nusing Avalonia.Controls;\nusing Avalonia.Markup.Xaml;\n\nnamespace Nodify.Shapes;\n\npublic partial class MainView : UserControl\n{\n    public MainView()\n    {\n        InitializeComponent();\n    }\n}"
  },
  {
    "path": "Examples/Nodify.Shapes/MainWindow.xaml",
    "content": "﻿<Window x:Class=\"Nodify.Shapes.MainWindow\"\n        xmlns=\"https://github.com/avaloniaui\"\n        xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n        xmlns:d=\"http://schemas.microsoft.com/expression/blend/2008\"\n        xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\"\n        xmlns:o=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation/options\"\n        xmlns:local=\"clr-namespace:Nodify.Shapes\"\n        xmlns:canvas=\"clr-namespace:Nodify.Shapes.Canvas\"\n        mc:Ignorable=\"d\"\n        xmlns:shared=\"clr-namespace:Nodify;assembly=Nodify.Shared\"\n        Title=\"Canvas\"\n        Height=\"650\"\n        x:DataType=\"local:AppShellViewModel\"\n        Width=\"1200\">\n    <Window.KeyBindings>\n        <KeyBinding Command=\"{Binding Canvas.RedoCommand}\"\n                    Gesture=\"Ctrl+Y\"/>\n        <KeyBinding Command=\"{Binding Canvas.RedoCommand}\"\n                    Gesture=\"Ctrl+Shift+Z\" />\n        <KeyBinding Command=\"{Binding Canvas.UndoCommand}\"\n                    Gesture=\"Ctrl+Z\" />\n        <KeyBinding Command=\"{Binding Canvas.DeleteSelectionCommand}\"\n                    Gesture=\"Delete\" />\n    </Window.KeyBindings>\n\n    <Window.DataContext>\n        <local:AppShellViewModel />\n    </Window.DataContext>\n\n    <Grid>\n        <canvas:CanvasView DataContext=\"{Binding Canvas}\" />\n    </Grid>\n</Window>\n"
  },
  {
    "path": "Examples/Nodify.Shapes/MainWindow.xaml.cs",
    "content": "﻿using System.Windows;\n\nnamespace Nodify.Shapes\n{\n    /// <summary>\n    /// Interaction logic for MainWindow.xaml\n    /// </summary>\n    public partial class MainWindow : Window\n    {\n        public MainWindow()\n        {\n            InitializeComponent();\n        }\n    }\n}"
  },
  {
    "path": "Examples/Nodify.Shapes/Nodify.Shapes.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <TargetFrameworks>net9</TargetFrameworks>\n    <Nullable>enable</Nullable>\n    <AssemblyOriginatorKeyFile>..\\..\\build\\Nodify.snk</AssemblyOriginatorKeyFile>\n    <SignAssembly>true</SignAssembly>\n    <AvaloniaUseCompiledBindingsByDefault>true</AvaloniaUseCompiledBindingsByDefault>\n    <PublishAot>true</PublishAot>\n  </PropertyGroup>\n\n  <ItemGroup>\n    <ProjectReference Include=\"..\\..\\Nodify\\Nodify.csproj\" />\n    <ProjectReference Include=\"..\\Nodify.Shared\\Nodify.Shared.csproj\" />\n  </ItemGroup>\n\n  <ItemGroup>\n    <PackageReference Include=\"Avalonia\" Version=\"$(AvaloniaVersion)\"/>\n    <PackageReference Include=\"Avalonia.Themes.Fluent\" Version=\"$(AvaloniaVersion)\"/>\n    <PackageReference Include=\"Avalonia.Fonts.Inter\" Version=\"$(AvaloniaVersion)\"/>\n    <PackageReference Include=\"Avalonia.Xaml.Behaviors\" Version=\"11.1.0.4\"/>\n    <!--Condition below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.-->\n    <PackageReference Condition=\"'$(Configuration)' == 'Debug'\" Include=\"Avalonia.Diagnostics\" Version=\"$(AvaloniaVersion)\"/>\n  </ItemGroup>\n\n  <ItemGroup>\n    <AdditionalFiles SourceItemGroup=\"AvaloniaXaml\" Include=\"**/*.xaml\"/>\n    <AvaloniaResource Include=\"**/*.xaml\" />\n  </ItemGroup>\n\n</Project>\n"
  },
  {
    "path": "Examples/Nodify.Shapes.Desktop/Nodify.Shapes.Desktop.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <OutputType>WinExe</OutputType>\n    <TargetFrameworks>net9</TargetFrameworks>\n    <Nullable>enable</Nullable>\n    <AssemblyOriginatorKeyFile>..\\..\\build\\Nodify.snk</AssemblyOriginatorKeyFile>\n    <SignAssembly>true</SignAssembly>\n    <AvaloniaUseCompiledBindingsByDefault>true</AvaloniaUseCompiledBindingsByDefault>\n    <PublishAot>true</PublishAot>\n  </PropertyGroup>\n  <ItemGroup>\n    <PackageReference Include=\"StringMath\" Version=\"4.1.2\" />\n  </ItemGroup>\n\n  <ItemGroup>\n    <ProjectReference Include=\"..\\..\\Nodify\\Nodify.csproj\" />\n    <ProjectReference Include=\"..\\Nodify.Shapes\\Nodify.Shapes.csproj\" />\n    <ProjectReference Include=\"..\\Nodify.Shared\\Nodify.Shared.csproj\" />\n  </ItemGroup>\n\n  <ItemGroup>\n    <PackageReference Include=\"Avalonia\" Version=\"$(AvaloniaVersion)\"/>\n    <PackageReference Include=\"Avalonia.Desktop\" Version=\"$(AvaloniaVersion)\"/>\n    <PackageReference Include=\"Avalonia.Themes.Fluent\" Version=\"$(AvaloniaVersion)\"/>\n    <PackageReference Include=\"Avalonia.Fonts.Inter\" Version=\"$(AvaloniaVersion)\"/>\n    <PackageReference Include=\"Avalonia.Xaml.Behaviors\" Version=\"11.1.0.4\"/>\n    <!--Condition below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.-->\n    <PackageReference Condition=\"'$(Configuration)' == 'Debug'\" Include=\"Avalonia.Diagnostics\" Version=\"$(AvaloniaVersion)\"/>\n  </ItemGroup>\n\n  <ItemGroup>\n    <AdditionalFiles SourceItemGroup=\"AvaloniaXaml\" Include=\"**/*.xaml\"/>\n    <AvaloniaResource Include=\"**/*.xaml\" />\n  </ItemGroup>\n\n</Project>\n"
  },
  {
    "path": "Examples/Nodify.Shapes.Desktop/Program.cs",
    "content": "﻿using System;\nusing Avalonia;\n\nnamespace Nodify.Shapes.Desktop;\n\nclass Program\n{\n    // Initialization code. Don't use any Avalonia, third-party APIs or any\n    // SynchronizationContext-reliant code before AppMain is called: things aren't initialized\n    // yet and stuff might break.\n    [STAThread]\n    public static void Main(string[] args) => BuildAvaloniaApp()\n        .StartWithClassicDesktopLifetime(args);\n\n    // Avalonia configuration, don't remove; also used by visual designer.\n    public static AppBuilder BuildAvaloniaApp()\n        => AppBuilder.Configure<App>()\n            .UsePlatformDetect()\n            .WithInterFont()\n            .LogToTrace();\n}"
  },
  {
    "path": "Examples/Nodify.Shapes.Web/AppBundle/app.css",
    "content": "﻿:root {\n    --sat: env(safe-area-inset-top);\n    --sar: env(safe-area-inset-right);\n    --sab: env(safe-area-inset-bottom);\n    --sal: env(safe-area-inset-left);\n}\n\n/* HTML styles for the splash screen */\n\n.highlight {\n    color: white;\n    font-size: 2.5rem;\n    display: block;\n}\n\n.purple {\n    color: #8b44ac;\n}\n\n.icon {\n    opacity: 0.05;\n    height: 35%;\n    width: 35%;\n    position: absolute;\n    background-repeat: no-repeat;\n    right: 0px;\n    bottom: 0px;\n    margin-right: 3%;\n    margin-bottom: 5%;\n    z-index: 5000;\n    background-position: right bottom;\n    pointer-events: none;\n}\n\n#avalonia-splash a {\n    color: whitesmoke;\n    text-decoration: none;\n}\n\n.center {\n    display: flex;\n    justify-content: center;\n    align-items: center;\n    height: 100vh;\n}\n\n#avalonia-splash {\n    position: relative;\n    height: 100%;\n    width: 100%;\n    color: whitesmoke;\n    background: #1b2a4e;\n    font-family: 'Nunito', sans-serif;\n    background-position: center;\n    background-size: cover;\n    background-repeat: no-repeat;\n    justify-content: center;\n    align-items: center;\n}\n\n.splash-close {\n    animation: fadeout 0.25s linear forwards;\n}\n\n@keyframes fadeout {\n    0% {\n        opacity: 100%;\n    }\n\n    100% {\n        opacity: 0;\n        visibility: collapse;\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.Shapes.Web/AppBundle/index.html",
    "content": "<!DOCTYPE html>\n<html>\n\n<head>\n    <title>Nodify.Shapes</title>\n    <meta charset=\"UTF-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n    <base href=\"/\" />\n    <link rel=\"modulepreload\" href=\"./main.js\" />\n    <link rel=\"modulepreload\" href=\"./_framework/dotnet.js\" />\n    <link rel=\"modulepreload\" href=\"./_framework/avalonia.js\" />\n    <link rel=\"stylesheet\" href=\"./app.css\" />\n</head>\n\n<body style=\"margin: 0px; overflow: hidden\">\n    <div id=\"out\">\n        <div id=\"avalonia-splash\">\n            <div class=\"center\">\n                <h2 class=\"purple\">\n                    Powered by\n                    <a class=\"highlight\" href=\"https://www.avaloniaui.net/\" target=\"_blank\">Avalonia UI</a>\n                </h2>\n            </div>\n            <img class=\"icon\" src=\"Logo.svg\" alt=\"Avalonia Logo\" />\n        </div>\n    </div>\n    <script type='module' src=\"./main.js\"></script>\n</body>\n\n</html>"
  },
  {
    "path": "Examples/Nodify.Shapes.Web/AppBundle/main.js",
    "content": "﻿import { dotnet } from './_framework/dotnet.js'\n\nconst is_browser = typeof window != \"undefined\";\nif (!is_browser) throw new Error(`Expected to be running in a browser`);\n\nconst dotnetRuntime = await dotnet\n    .withDiagnosticTracing(false)\n    .withApplicationArgumentsFromQuery()\n    .create();\n\ndotnetRuntime.setModuleImports(\"main.js\", {});\nconsole.log(dotnetRuntime);\n\nconst config = dotnetRuntime.getConfig();\n\nawait dotnetRuntime.runMain(config.mainAssemblyName, []);"
  },
  {
    "path": "Examples/Nodify.Shapes.Web/Nodify.Shapes.Web.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\n  <PropertyGroup>\n    <TargetFramework>net9.0-browser</TargetFramework>\n    <RuntimeIdentifier>browser-wasm</RuntimeIdentifier>\n    <WasmMainJSPath>AppBundle\\main.js</WasmMainJSPath>\n    <OutputType>Exe</OutputType>\n    <Nullable>enable</Nullable>\n    <WarningsAsErrors>nullable</WarningsAsErrors>\n    <WasmEnableThreads>false</WasmEnableThreads>\n    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>\n  </PropertyGroup>\n\n  <ItemGroup>\n    <WasmExtraFilesToDeploy Include=\"AppBundle\\**\"/>\n  </ItemGroup>\n\n  <ItemGroup>\n    <PackageReference Include=\"Avalonia.Browser\" Version=\"$(AvaloniaVersion)\"/>\n  </ItemGroup>\n\n  <ItemGroup>\n    <ProjectReference Include=\"..\\Nodify.Shapes\\Nodify.Shapes.csproj\" />\n  </ItemGroup>\n\n</Project>\n"
  },
  {
    "path": "Examples/Nodify.Shapes.Web/Program.cs",
    "content": "using System.Threading.Tasks;\nusing Avalonia;\nusing Avalonia.Browser;\n\nnamespace Nodify.Shapes.Web;\n\ninternal partial class Program\n{\n    private static async Task Main(string[] args)\n    {\n        await BuildAvaloniaApp()\n            .WithInterFont()\n            .StartBrowserAppAsync(\"out\");\n    }\n\n    public static AppBuilder BuildAvaloniaApp()\n        => AppBuilder.Configure<App>();\n}"
  },
  {
    "path": "Examples/Nodify.Shapes.Web/Properties/launchSettings.json",
    "content": "{\n  \"profiles\": {\n    \"Nodify.Shapes\": {\n      \"commandName\": \"Project\",\n      \"launchBrowser\": false,\n      \"environmentVariables\": {\n        \"ASPNETCORE_ENVIRONMENT\": \"Development\"\n      },\n      \"applicationUrl\": \"https://localhost:5001;http://localhost:5000\",\n      \"inspectUri\": \"{wsProtocol}://{url.hostname}:{url.port}/debug?browser={browserInspectUri}\"\n    }\n  }\n}"
  },
  {
    "path": "Examples/Nodify.Shapes.Web/runtimeconfig.template.json",
    "content": "{\n  \"wasmHostProperties\": {\n    \"perHostConfig\": [\n      {\n        \"name\": \"browser\",\n        \"html-path\": \"index.html\",\n        \"Host\": \"browser\"\n      }\n    ]\n  }\n}"
  },
  {
    "path": "Examples/Nodify.Shared/Behaviours/DataTrigger.cs",
    "content": "using System;\nusing System.Collections.Generic;\nusing System.Diagnostics.CodeAnalysis;\nusing System.Globalization;\nusing Avalonia;\nusing Avalonia.Data;\nusing Avalonia.Metadata;\nusing Avalonia.Reactive;\nusing Avalonia.Xaml.Interactivity;\n\n[assembly: XmlnsDefinition(\"https://github.com/avaloniaui\", \"Nodify.Shared.Behaviours\")]\n\nnamespace Nodify.Shared.Behaviours;\n\npublic class DataTrigger : Trigger\n{\n    private List<IDisposable> activeBindings = new();\n    \n    /// <summary>\n    /// Identifies the <seealso cref=\"Property\"/> avalonia property.\n    /// </summary>\n    public static readonly StyledProperty<string> PropertyProperty =\n        AvaloniaProperty.Register<DataTrigger, string>(nameof(Property));\n    \n    public static readonly StyledProperty<object?> BoundProperty =\n        AvaloniaProperty.Register<DataTrigger, object?>(nameof(Bound));\n\n    /// <summary>\n    /// Identifies the <seealso cref=\"ComparisonCondition\"/> avalonia property.\n    /// </summary>\n    public static readonly StyledProperty<ComparisonConditionType> ComparisonConditionProperty =\n        AvaloniaProperty.Register<DataTrigger, ComparisonConditionType>(nameof(ComparisonCondition));\n\n    /// <summary>\n    /// Identifies the <seealso cref=\"Value\"/> avalonia property.\n    /// </summary>\n    public static readonly StyledProperty<object?> ValueProperty =\n        AvaloniaProperty.Register<DataTrigger, object?>(nameof(Value));\n    \n    public string Property\n    {\n        get => GetValue(PropertyProperty);\n        set => SetValue(PropertyProperty, value);\n    }\n    \n    public object? Bound\n    {\n        get => GetValue(BoundProperty);\n        set => SetValue(BoundProperty, value);\n    }\n\n    /// <summary>\n    /// Gets or sets the type of comparison to be performed between <see cref=\"Property\"/> and <see cref=\"DataTrigger.Value\"/>. This is a avalonia property.\n    /// </summary>\n    public ComparisonConditionType ComparisonCondition\n    {\n        get => GetValue(ComparisonConditionProperty);\n        set => SetValue(ComparisonConditionProperty, value);\n    }\n\n    /// <summary>\n    /// Gets or sets the value to be compared with the value of <see cref=\"Property\"/>. This is a avalonia property.\n    /// </summary>\n    public object? Value\n    {\n        get => GetValue(ValueProperty);\n        set => SetValue(ValueProperty, value);\n    }\n\n    public bool UseDataContext { get; set; } = true;\n    \n    public object? Source { get; set; }\n\n    protected override void OnAttached()\n    {\n        base.OnAttached();\n        this.Bind(BoundProperty, new Binding(Source == null && UseDataContext ? (Property == \".\" ? \"DataContext\" : $\"DataContext.{Property}\") : Property) { Source = Source ?? AssociatedObject });\n    }\n\n    static DataTrigger()\n    {\n        BoundProperty.Changed.Subscribe(\n            (IObserver<AvaloniaPropertyChangedEventArgs<object>>)new AnonymousObserver<AvaloniaPropertyChangedEventArgs<object?>>(OnValueChanged));\n\n        ComparisonConditionProperty.Changed.Subscribe(\n            (IObserver<AvaloniaPropertyChangedEventArgs<ComparisonConditionType>>)new AnonymousObserver<AvaloniaPropertyChangedEventArgs<ComparisonConditionType>>(OnValueChanged));\n\n        ValueProperty.Changed.Subscribe(\n            (IObserver<AvaloniaPropertyChangedEventArgs<object>>)new AnonymousObserver<AvaloniaPropertyChangedEventArgs<object?>>(OnValueChanged));\n    }\n\n    [RequiresUnreferencedCode(\"This functionality is not compatible with trimming.\")]\n    private static bool Compare(object? leftOperand, ComparisonConditionType operatorType, object? rightOperand)\n    {\n        if (leftOperand is not null && rightOperand is not null &&\n            leftOperand.GetType() != rightOperand.GetType())\n        {\n            var value = rightOperand.ToString();\n            var destinationType = leftOperand.GetType();\n            if (value is not null)\n            {\n                rightOperand = TypeConverterHelper.Convert(value, destinationType);\n            }\n        }\n\n        var leftComparableOperand = leftOperand as IComparable;\n        var rightComparableOperand = rightOperand as IComparable;\n        if (leftComparableOperand is not null && rightComparableOperand is not null)\n        {\n            return EvaluateComparable(leftComparableOperand, operatorType, rightComparableOperand);\n        }\n\n        switch (operatorType)\n        {\n            case ComparisonConditionType.Equal:\n                return Equals(leftOperand, rightOperand);\n\n            case ComparisonConditionType.NotEqual:\n                return !Equals(leftOperand, rightOperand);\n\n            case ComparisonConditionType.LessThan:\n            case ComparisonConditionType.LessThanOrEqual:\n            case ComparisonConditionType.GreaterThan:\n            case ComparisonConditionType.GreaterThanOrEqual:\n            {\n                throw leftComparableOperand switch\n                {\n                    null when rightComparableOperand is null => new ArgumentException(string.Format(\n                        CultureInfo.CurrentCulture,\n                        \"Binding property of type {0} and Value property of type {1} cannot be used with operator {2}.\",\n                        leftOperand?.GetType().Name ?? \"null\", rightOperand?.GetType().Name ?? \"null\",\n                        operatorType.ToString())),\n                    null => new ArgumentException(string.Format(CultureInfo.CurrentCulture,\n                        \"Binding property of type {0} cannot be used with operator {1}.\",\n                        leftOperand?.GetType().Name ?? \"null\", operatorType.ToString())),\n                    _ => new ArgumentException(string.Format(CultureInfo.CurrentCulture,\n                        \"Value property of type {0} cannot be used with operator {1}.\",\n                        rightOperand?.GetType().Name ?? \"null\", operatorType.ToString()))\n                };\n            }\n        }\n\n        return false;\n    }\n\n    /// <summary>\n    /// Evaluates both operands that implement the IComparable interface.\n    /// </summary>\n    [RequiresUnreferencedCode(\"This functionality is not compatible with trimming.\")]\n    private static bool EvaluateComparable(IComparable leftOperand, ComparisonConditionType operatorType, IComparable rightOperand)\n    {\n        object? convertedOperand = null;\n        try\n        {\n            convertedOperand = Convert.ChangeType(rightOperand, leftOperand.GetType(), CultureInfo.CurrentCulture);\n        }\n        catch (FormatException)\n        {\n            // FormatException: Convert.ChangeType(\"hello\", typeof(double), ...);\n        }\n        catch (InvalidCastException)\n        {\n            // InvalidCastException: Convert.ChangeType(4.0d, typeof(Rectangle), ...);\n        }\n\n        if (convertedOperand is null)\n        {\n            return operatorType == ComparisonConditionType.NotEqual;\n        }\n\n        var comparison = leftOperand.CompareTo((IComparable)convertedOperand);\n        return operatorType switch\n        {\n            ComparisonConditionType.Equal => comparison == 0,\n            ComparisonConditionType.NotEqual => comparison != 0,\n            ComparisonConditionType.LessThan => comparison < 0,\n            ComparisonConditionType.LessThanOrEqual => comparison <= 0,\n            ComparisonConditionType.GreaterThan => comparison > 0,\n            ComparisonConditionType.GreaterThanOrEqual => comparison >= 0,\n            _ => false\n        };\n    }\n\n    private static void OnValueChanged(AvaloniaPropertyChangedEventArgs args)\n    {\n        if (args.Sender is not DataTrigger behavior || behavior.AssociatedObject is null)\n        {\n            return;\n        }\n\n        // NOTE: In UWP version binding null check is not present but Avalonia throws exception as Bindings are null when first initialized.\n        var binding = behavior.Bound;\n        if (binding is not null)\n        {\n            foreach (var b in behavior.activeBindings)\n                b.Dispose();\n            behavior.activeBindings.Clear();\n            // Some value has changed--either the binding value, reference value, or the comparison condition. Re-evaluate the equation.\n            if (Compare(behavior.Bound, behavior.ComparisonCondition, behavior.Value))\n            {\n                foreach (var result in Interaction.ExecuteActions(behavior.AssociatedObject, behavior.Actions, args))\n                {\n                    if (result is IDisposable disposable)\n                        behavior.activeBindings.Add(disposable);\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.Shared/Behaviours/PropertySetter.cs",
    "content": "using System;\nusing System.ComponentModel;\nusing System.Diagnostics.CodeAnalysis;\nusing System.Globalization;\nusing System.Linq;\nusing System.Reflection;\nusing Avalonia;\nusing Avalonia.Controls;\nusing Avalonia.Data;\nusing Avalonia.Xaml.Interactivity;\n\nnamespace Nodify.Shared.Behaviours;\n\n\n/// <summary>\n/// An action that will change a specified property to a specified value when invoked.\n/// </summary>\npublic class PropertySetter : AvaloniaObject, IAction\n{\n    private static readonly char[] s_trimChars = { '(', ')' };\n    private static readonly char[] s_separator = { '.' };\n\n    [RequiresUnreferencedCode(\"This functionality is not compatible with trimming.\")]\n    private static Type? GetTypeByName(string name)\n    {\n        return\n            AppDomain.CurrentDomain.GetAssemblies()\n                .Reverse()\n                .Select(assembly => assembly.GetType(name))\n                .FirstOrDefault(t => t is not null)\n            ??\n            AppDomain.CurrentDomain.GetAssemblies()\n                .Reverse()\n                .SelectMany(assembly => assembly.GetTypes())\n                .FirstOrDefault(t => t.Name == name);\n    }\n\n    [RequiresUnreferencedCode(\"This functionality is not compatible with trimming.\")]\n    private static AvaloniaProperty? FindAttachedProperty(object? targetObject, string propertyName)\n    {\n        if (targetObject is null)\n        {\n            return null;\n        }\n        \n        var propertyNames = propertyName.Trim().Trim(s_trimChars).Split(s_separator);\n        if (propertyNames.Length != 2)\n        {\n            return null;\n        }\n        var targetPropertyTypeName = propertyNames[0];\n        var targetPropertyName = propertyNames[1];\n        var targetType = GetTypeByName(targetPropertyTypeName) ?? targetObject.GetType();\n\n        var registeredAttached = AvaloniaPropertyRegistry.Instance.GetRegisteredAttached(targetType);\n\n        foreach (var avaloniaProperty in registeredAttached)\n        {\n            if (avaloniaProperty.OwnerType.Name == targetPropertyTypeName && avaloniaProperty.Name == targetPropertyName)\n            {\n                return avaloniaProperty;\n            }\n        }\n\n        var registeredInherited = AvaloniaPropertyRegistry.Instance.GetRegisteredInherited(targetType);\n\n        foreach (var avaloniaProperty in registeredInherited)\n        {\n            if (avaloniaProperty.Name == targetPropertyName)\n            {\n                return avaloniaProperty;\n            }\n        }\n\n        return null;\n    }\n\n    /// <summary>\n    /// Identifies the <seealso cref=\"Property\"/> avalonia property.\n    /// </summary>\n    public static readonly StyledProperty<string> PropertyNameProperty =\n        AvaloniaProperty.Register<PropertySetter, string>(nameof(Property));\n\n    /// <summary>\n    /// Identifies the <seealso cref=\"TargetObject\"/> avalonia property.\n    /// </summary>\n    public static readonly StyledProperty<object?> TargetObjectProperty =\n        AvaloniaProperty.Register<PropertySetter, object?>(nameof(TargetObject));\n\n    /// <summary>\n    /// Identifies the <seealso cref=\"Value\"/> avalonia property.\n    /// </summary>\n    public static readonly StyledProperty<object?> ValueProperty =\n        AvaloniaProperty.Register<PropertySetter, object?>(nameof(Value));\n\n    /// <summary>\n    /// Gets or sets the name of the property to change. This is a avalonia property.\n    /// </summary>\n    public string Property\n    {\n        get => GetValue(PropertyNameProperty);\n        set => SetValue(PropertyNameProperty, value);\n    }\n\n    /// <summary>\n    /// Gets or sets the value to set. This is a avalonia property.\n    /// </summary>\n    public object? Value\n    {\n        get => GetValue(ValueProperty);\n        set => SetValue(ValueProperty, value);\n    }\n\n    /// <summary>\n    /// Gets or sets the object whose property will be changed.\n    /// If <seealso cref=\"TargetObject\"/> is not set or cannot be resolved, the sender of <seealso cref=\"Execute\"/> will be used. This is a avalonia property.\n    /// </summary>\n    [ResolveByName]\n    public object? TargetObject\n    {\n        get => GetValue(TargetObjectProperty);\n        set => SetValue(TargetObjectProperty, value);\n    }\n\n    /// <summary>\n    /// Executes the action.\n    /// </summary>\n    /// <param name=\"sender\">The <see cref=\"object\"/> that is passed to the action by the behavior. Generally this is <seealso cref=\"IBehavior.AssociatedObject\"/> or a target object.</param>\n    /// <param name=\"parameter\">The value of this parameter is determined by the caller.</param>\n    /// <returns>True if updating the property value succeeds; else false.</returns>\n    public virtual object Execute(object? sender, object? parameter)\n    {\n        object? targetObject;\n        if (GetValue(TargetObjectProperty) is not null)\n        {\n            targetObject = TargetObject;\n        }\n        else\n        {\n            targetObject = sender;\n        }\n\n        if (targetObject is null)\n        {\n            return null!;\n        }\n\n        if (targetObject is AvaloniaObject avaloniaObject)\n        {\n            if (Property.Contains('.'))\n            {\n                var avaloniaProperty = FindAttachedProperty(targetObject, Property);\n                if (avaloniaProperty is not null)\n                {\n                    return UpdateAvaloniaPropertyValue(avaloniaObject, avaloniaProperty)!;\n                }\n\n                return null!;\n            }\n            else\n            {\n                var avaloniaProperty = AvaloniaPropertyRegistry.Instance.FindRegistered(avaloniaObject, Property);\n                if (avaloniaProperty is not null)\n                {\n                    return UpdateAvaloniaPropertyValue(avaloniaObject, avaloniaProperty)!;\n                }\n            }\n        }\n\n        UpdatePropertyValue(targetObject);\n        return null!;\n    }\n\n    [RequiresUnreferencedCode(\"This functionality is not compatible with trimming.\")]\n    private void UpdatePropertyValue(object targetObject)\n    {\n        var targetType = targetObject.GetType();\n        var targetTypeName = targetType.Name;\n        var propertyInfo = targetType.GetRuntimeProperty(Property);\n\n        if (propertyInfo is null)\n        {\n            throw new ArgumentException(string.Format(\n                CultureInfo.CurrentCulture,\n                \"Cannot find a property named {0} on type {1}.\",\n                Property,\n                targetTypeName));\n        }\n        else if (!propertyInfo.CanWrite)\n        {\n            throw new ArgumentException(string.Format(\n                CultureInfo.CurrentCulture,\n                \"Cannot find a property named {0} on type {1}.\",\n                Property,\n                targetTypeName));\n        }\n\n        Exception? innerException = null;\n        try\n        {\n            object? result = null;\n            var propertyType = propertyInfo.PropertyType;\n            var propertyTypeInfo = propertyType.GetTypeInfo();\n            if (Value is null)\n            {\n                // The result can be null if the type is generic (nullable), or the default value of the type in question\n                result = propertyTypeInfo.IsValueType ? Activator.CreateInstance(propertyType) : null;\n            }\n            else if (propertyTypeInfo.IsAssignableFrom(Value.GetType().GetTypeInfo()))\n            {\n                result = Value;\n            }\n            else\n            {\n                var valueAsString = Value.ToString();\n                if (valueAsString is not null)\n                {\n                    result = propertyTypeInfo.IsEnum ? Enum.Parse(propertyType, valueAsString, false) :\n                        TypeConverterHelper.Convert(valueAsString, propertyType);\n                }\n            }\n\n            propertyInfo.SetValue(targetObject, result, Array.Empty<object>());\n        }\n        catch (FormatException e)\n        {\n            innerException = e;\n        }\n        catch (ArgumentException e)\n        {\n            innerException = e;\n        }\n\n        if (innerException is not null)\n        {\n            throw new ArgumentException(string.Format(\n                    CultureInfo.CurrentCulture,\n                    \"Cannot assign value of type {0} to property {1} of type {2}. The {1} property can be assigned only values of type {2}.\",\n                    Value?.GetType().Name ?? \"null\",\n                    Property,\n                    propertyInfo.PropertyType.Name),\n                innerException);\n        }\n    }\n\n    [RequiresUnreferencedCode(\"This functionality is not compatible with trimming.\")]\n    private IDisposable? UpdateAvaloniaPropertyValue(AvaloniaObject avaloniaObject, AvaloniaProperty property)\n    {\n        ValidateAvaloniaProperty(property);\n\n        Exception? innerException = null;\n        try\n        {\n            object? result = null;\n            var propertyType = property.PropertyType;\n            var propertyTypeInfo = propertyType.GetTypeInfo();\n            if (Value is null)\n            {\n                // The result can be null if the type is generic (nullable), or the default value of the type in question\n                result = propertyTypeInfo.IsValueType ? Activator.CreateInstance(propertyType) : null;\n            }\n            else if (propertyTypeInfo.IsAssignableFrom(Value.GetType().GetTypeInfo()))\n            {\n                result = Value;\n            }\n            else\n            {\n                var valueAsString = Value.ToString();\n                if (valueAsString is not null)\n                {\n                    result = propertyTypeInfo.IsEnum ? Enum.Parse(propertyType, valueAsString, false) :\n                        TypeConverterHelper.Convert(valueAsString, propertyType);\n                }\n            }\n\n            return avaloniaObject.SetValue(property, result, BindingPriority.StyleTrigger);\n        }\n        catch (FormatException e)\n        {\n            innerException = e;\n        }\n        catch (ArgumentException e)\n        {\n            innerException = e;\n        }\n\n        if (innerException is not null)\n        {\n            throw new ArgumentException(string.Format(\n                    CultureInfo.CurrentCulture,\n                    \"Cannot assign value of type {0} to property {1} of type {2}. The {1} property can be assigned only values of type {2}.\",\n                    Value?.GetType().Name ?? \"null\",\n                    Property,\n                    avaloniaObject.GetType().Name),\n                innerException);\n        }\n\n        return null;\n    }\n\n    /// <summary>\n    /// Ensures the property is not null and can be written to.\n    /// </summary>\n    private void ValidateAvaloniaProperty(AvaloniaProperty? property)\n    {\n        if (property is null)\n        {\n            throw new ArgumentException(string.Format(\n                CultureInfo.CurrentCulture,\n                \"Cannot find a property named {0}.\",\n                Property));\n        }\n        else if (property.IsReadOnly)\n        {\n            throw new ArgumentException(string.Format(\n                CultureInfo.CurrentCulture,\n                \"Cannot find a property named {0}.\",\n                Property));\n        }\n    }\n}\n\ninternal static class TypeConverterHelper\n{\n    /// <summary>\n    /// Converts string representation of a value to its object representation.\n    /// </summary>\n    /// <param name=\"value\">The value to convert.</param>\n    /// <param name=\"destinationType\">The destination type.</param>\n    /// <returns>Object representation of the string value.</returns>\n    /// <exception cref=\"ArgumentNullException\">destinationType cannot be null.</exception>\n    [RequiresUnreferencedCode(\"This functionality is not compatible with trimming.\")]\n    public static object? Convert(string value, Type destinationType)\n    {\n        if (destinationType is null)\n        {\n            throw new ArgumentNullException(nameof(destinationType));\n        }\n\n        var destinationTypeFullName = destinationType.FullName;\n        if (destinationTypeFullName is null)\n        {\n            return null;\n        }\n\n        var scope = GetScope(destinationTypeFullName);\n\n        // Value types in the \"System\" namespace must be special cased due to a bug in the xaml compiler\n        if (string.Equals(scope, \"System\", StringComparison.Ordinal))\n        {\n            if (string.Equals(destinationTypeFullName, typeof(string).FullName, StringComparison.Ordinal))\n            {\n                return value;\n            }\n\n            if (string.Equals(destinationTypeFullName, typeof(bool).FullName, StringComparison.Ordinal))\n            {\n                return bool.Parse(value);\n            }\n\n            if (string.Equals(destinationTypeFullName, typeof(int).FullName, StringComparison.Ordinal))\n            {\n                return int.Parse(value, CultureInfo.InvariantCulture);\n            }\n\n            if (string.Equals(destinationTypeFullName, typeof(double).FullName, StringComparison.Ordinal))\n            {\n                return double.Parse(value, CultureInfo.InvariantCulture);\n            }\n        }\n\n        try\n        {\n            if (destinationType.BaseType == typeof(Enum))\n                return Enum.Parse(destinationType, value);\n\n            if (destinationType.GetInterfaces().Any(t => t == typeof(IConvertible)))\n            {\n                return (value as IConvertible).ToType(destinationType, CultureInfo.InvariantCulture);\n            }\n\n            var converter = TypeDescriptor.GetConverter(destinationType);\n            return converter.ConvertFromInvariantString(value);\n        }\n        catch (ArgumentException)\n        {\n            // not an enum\n        }\n        catch (InvalidCastException)\n        {\n            // not able to convert to anything\n        }\n\n        return null;\n    }\n\n    private static string GetScope(string name)\n    {\n        var indexOfLastPeriod = name.LastIndexOf('.');\n        return indexOfLastPeriod != name.Length - 1 ? name[..indexOfLastPeriod] : name;\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.Shared/Behaviours/WpfBtn.cs",
    "content": "using System;\nusing Avalonia.LogicalTree;\n\nnamespace Nodify.Shared.Behaviours;\n\n/// <summary>\n/// Button with CommandTarget for WPF compatibility.\n/// </summary>\npublic class WpfBtn : Button\n{\n    public static readonly StyledProperty<IInputElement?> CommandTargetProperty = AvaloniaProperty.Register<WpfBtn, IInputElement?>(nameof(CommandTarget));\n\n    protected override Type StyleKeyOverride => typeof(Button);\n\n    public IInputElement? CommandTarget\n    {\n        get => (IInputElement?)GetValue(CommandTargetProperty);\n        set => SetValue(CommandTargetProperty, value);\n    }\n    \n    protected override void OnClick()\n    {\n        if (Command is RoutedCommand routedCommand)\n        {\n            var target = CommandTarget ?? this;\n            \n            if (routedCommand.CanExecute(CommandParameter, target))\n                routedCommand.Execute(CommandParameter, target);\n        }\n        else\n            base.OnClick();\n    }\n\n    protected override bool IsEnabledCore => IsEnabled;\n\n    private void CanExecuteChanged(object? sender, EventArgs e)\n    {\n        if (Command is RoutedCommand routedCommand)\n        {\n            IsEnabled = routedCommand.CanExecute(CommandParameter, CommandTarget ?? this);\n        }\n        else\n        {\n            IsEnabled = Command?.CanExecute(CommandParameter) ?? false;\n        }\n        UpdateIsEffectivelyEnabled();\n    }\n    \n    protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)\n    {\n        if (change.Property == CommandProperty)\n        {\n            if (((ILogical)this).IsAttachedToLogicalTree)\n            {\n                var (oldValue, newValue) = change.GetOldAndNewValue<ICommand?>();\n                if (oldValue is ICommand oldCommand)\n                {\n                    oldCommand.CanExecuteChanged -= CanExecuteChanged;\n                }\n\n                if (newValue is ICommand newCommand)\n                {\n                    newCommand.CanExecuteChanged += CanExecuteChanged;\n                }\n            }\n\n            CanExecuteChanged(this, EventArgs.Empty);\n        }\n        else if (change.Property == CommandParameterProperty)\n        {\n            CanExecuteChanged(this, EventArgs.Empty);\n        }\n        else\n            base.OnPropertyChanged(change);\n    }\n    \n    protected override void OnAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e)\n    {\n        base.OnAttachedToLogicalTree(e);\n\n        if (Command != null)\n        {\n            Command.CanExecuteChanged += CanExecuteChanged;\n            CanExecuteChanged(this, EventArgs.Empty);\n        }\n    }\n\n    protected override void OnDetachedFromLogicalTree(LogicalTreeAttachmentEventArgs e)\n    {\n        base.OnDetachedFromLogicalTree(e);\n\n        if (Command != null)\n        {\n            Command.CanExecuteChanged -= CanExecuteChanged;\n        }\n    }\n}"
  },
  {
    "path": "Examples/Nodify.Shared/BindingProxy.cs",
    "content": "using System.Windows;\nusing Avalonia;\n\nnamespace Nodify\n{\n    public class BindingProxy : AvaloniaObject\n    {\n        public static readonly StyledProperty<object?> DataContextProperty =\n            AvaloniaProperty.Register<BindingProxy, object?>(nameof(DataContext));\n\n        public object? DataContext\n        {\n            get => GetValue(DataContextProperty);\n            set => SetValue(DataContextProperty, value);\n        }\n    }\n}"
  },
  {
    "path": "Examples/Nodify.Shared/BoxValue.cs",
    "content": "﻿using System.Windows;\n\nnamespace Nodify.Shared\n{\n    public static class BoxValue\n    {\n        public static readonly object Point = default(Point);\n        public static readonly object Size = default(Size);\n        public static readonly object Rect = default(Rect);\n        public static readonly object False = false;\n        public static readonly object True = true;\n        public static readonly object DoubleHalf = 0.5d;\n        public static readonly object Double0 = 0d;\n        public static readonly object Double1 = 1d;\n        public static readonly object Double2 = 2d;\n        public static readonly object Int0 = 0;\n        public static readonly object Int1 = 1;\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.Shared/Controls/EditableTextBlock.cs",
    "content": "﻿using System.Windows;\nusing System.Windows.Input;\nusing Nodify.Compatibility;\nusing Nodify.Shared;\n\nnamespace Nodify\n{\n    [TemplatePart(Name = ElementTextBox, Type = typeof(TextBox))]\n    internal class EditableTextBlock : WpfControl\n    {\n        private const string ElementTextBox = \"PART_TextBox\";\n\n        public static readonly StyledProperty<bool> IsEditingProperty = AvaloniaProperty.Register<EditableTextBlock, bool>(nameof(IsEditing), false, defaultBindingMode: BindingMode.TwoWay, coerce: CoerceIsEditing);\n        public static readonly StyledProperty<bool> IsEditableProperty = AvaloniaProperty.Register<EditableTextBlock, bool>(nameof(IsEditable), true);\n        public static readonly StyledProperty<string?> TextProperty = TextBlock.TextProperty.AddOwner<EditableTextBlock>();\n        public static readonly StyledProperty<bool> AcceptsReturnProperty = TextBox.AcceptsReturnProperty.AddOwner<EditableTextBlock>();\n        public static readonly StyledProperty<TextWrapping> TextWrappingProperty = TextBlock.TextWrappingProperty.AddOwner<EditableTextBlock>();\n        public static readonly StyledProperty<TextTrimming> TextTrimmingProperty = TextBlock.TextTrimmingProperty.AddOwner<EditableTextBlock>();\n        public static readonly StyledProperty<int> MinLinesProperty = TextBox.MinLinesProperty.AddOwner<EditableTextBlock>();\n        public static readonly StyledProperty<int> MaxLinesProperty = TextBox.MaxLinesProperty.AddOwner<EditableTextBlock>();\n        public static readonly StyledProperty<int> MaxLengthProperty = TextBox.MaxLengthProperty.AddOwner<EditableTextBlock>();\n        public static readonly StyledProperty<VerticalAlignment> VerticalContentAlignmentProperty = ContentControl.VerticalContentAlignmentProperty.AddOwner<EditableTextBlock>();\n        public static readonly StyledProperty<HorizontalAlignment> HorizontalContentAlignmentProperty = ContentControl.HorizontalContentAlignmentProperty.AddOwner<EditableTextBlock>();\n\n        private static void OnIsEditingChanged(AvaloniaObject d, AvaloniaPropertyChangedEventArgs e) { }\n\n        private static bool CoerceIsEditing(AvaloniaObject d, bool value)\n        {\n            if (!((EditableTextBlock)d).IsEditable)\n            {\n                return false;\n            }\n\n            return value;\n        }\n\n        public string Text\n        {\n            get => (string)GetValue(TextProperty);\n            set => SetValue(TextProperty, value);\n        }\n\n        public bool IsEditing\n        {\n            get => (bool)GetValue(IsEditingProperty);\n            set => SetValue(IsEditingProperty, value);\n        }\n\n        public bool IsEditable\n        {\n            get => (bool)GetValue(IsEditableProperty);\n            set => SetValue(IsEditableProperty, value);\n        }\n\n        public bool AcceptsReturn\n        {\n            get => (bool)GetValue(AcceptsReturnProperty);\n            set => SetValue(AcceptsReturnProperty, value);\n        }\n\n        public int MaxLength\n        {\n            get => (int)GetValue(MaxLengthProperty);\n            set => SetValue(MaxLengthProperty, value);\n        }\n\n        public int MinLines\n        {\n            get => (int)GetValue(MinLinesProperty);\n            set => SetValue(MaxLinesProperty, value);\n        }\n\n        public int MaxLines\n        {\n            get => (int)GetValue(MaxLinesProperty);\n            set => SetValue(MaxLinesProperty, value);\n        }\n\n        public TextWrapping TextWrapping\n        {\n            get => (TextWrapping)GetValue(TextWrappingProperty);\n            set => SetValue(TextWrappingProperty, value);\n        }\n\n        public TextTrimming TextTrimming\n        {\n            get => (TextTrimming)GetValue(TextTrimmingProperty);\n            set => SetValue(TextTrimmingProperty, value);\n        }\n        \n        public VerticalAlignment VerticalContentAlignment\n        {\n            get => (VerticalAlignment)GetValue(VerticalContentAlignmentProperty);\n            set => SetValue(VerticalContentAlignmentProperty, value);\n        }\n        \n        public HorizontalAlignment HorizontalContentAlignment\n        {\n            get => (HorizontalAlignment)GetValue(HorizontalContentAlignmentProperty);\n            set => SetValue(HorizontalContentAlignmentProperty, value);\n        }\n\n        protected TextBox? TextBox { get; private set; }\n\n        static EditableTextBlock()\n        {\n            TextWrappingProperty.OverrideDefaultValue<EditableTextBlock>(TextWrapping.Wrap);\n            TextTrimmingProperty.OverrideDefaultValue<EditableTextBlock>(TextTrimming.CharacterEllipsis);\n            IsEditingProperty.Changed.AddClassHandler<EditableTextBlock>(OnIsEditingChanged);\n            TextProperty.OverrideMetadata<EditableTextBlock>(new StyledPropertyMetadata<string?>(defaultBindingMode: BindingMode.TwoWay));\n            FocusableProperty.OverrideDefaultValue<EditableTextBlock>(true);\n        }\n\n        protected override void OnApplyTemplate(TemplateAppliedEventArgs e)\n        {\n            base.OnApplyTemplate(e);\n\n            TextBox = e.NameScope.Find<TextBox>(ElementTextBox);\n\n            if (TextBox != null)\n            {\n                TextBox.LostFocus += OnLostFocus;\n                TextBox.GetObservable(IsVisibleProperty)\n                    .Subscribe(new AnonuymousObserver<bool>(OnTextBoxVisiblityChanged));\n\n                if (IsEditing)\n                {\n                    TextBox.Focus();\n                    TextBox.SelectAll();\n                }\n            }\n        }\n\n        private void OnTextBoxVisiblityChanged(bool e)\n        {\n            if (IsEditing && TextBox != null)\n            {\n                if (TextBox.Focus())\n                {\n                    TextBox.SelectAll();\n                }\n                else\n                {\n                    IsEditing = false;\n                }\n            }\n        }\n\n        protected override void OnMouseDown(MouseButtonEventArgs e)\n        {\n            if (IsEditing)\n            {\n                e.Handled = true;\n            }\n            else if (IsEditable && e.ChangedButton == MouseButton.Left && e.ClickCount == 2)\n            {\n                IsEditing = true;\n                e.Handled = true;\n            }\n        }\n\n        protected override void OnMouseUp(MouseButtonEventArgs e)\n        {\n            if (IsEditing)\n            {\n                e.Handled = true;\n            }\n        }\n\n        private void OnLostFocus(object? sender, RoutedEventArgs e)\n        {\n            IsEditing = false;\n        }\n\n        protected override void OnKeyDown(KeyEventArgs e)\n        {\n            if (IsEditing && e.Key == Key.Escape || !AcceptsReturn && e.Key == Key.Enter)\n            {\n                IsEditing = false;\n            }\n\n            if(e.Key == Key.Enter && IsFocused && !IsEditing)\n            {\n                IsEditing = true;\n            }\n        }\n        private class AnonuymousObserver<T> : System.IObserver<T>\n        {\n            private readonly System.Action<T> _onNext;\n            private readonly System.Action<System.Exception>? _onError;\n            private readonly System.Action? _onCompleted;\n\n            public AnonuymousObserver(System.Action<T> onNext, System.Action<System.Exception>? onError = null, System.Action? onCompleted = null)\n            {\n                _onNext = onNext;\n                _onError = onError;\n                _onCompleted = onCompleted;\n            }\n\n            public void OnCompleted() => _onCompleted?.Invoke();\n            public void OnError(System.Exception error) => _onError?.Invoke(error);\n            public void OnNext(T value) => _onNext?.Invoke(value);\n        }\n\n        protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)\n        {\n            base.OnPropertyChanged(change);\n            if (change.Property == IsEditingProperty)\n                PseudoClasses.Set(\":editing\", (bool)change.NewValue);\n        }\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.Shared/Controls/ResizablePanel.cs",
    "content": "﻿using System;\nusing System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Controls.Primitives;\nusing System.Windows.Input;\n\nnamespace Nodify\n{\n    public class ResizablePanel : ContentControl\n    {\n        internal static readonly ResizeDirections BoxedResizeDirection = ResizeDirections.All;\n\n        public static readonly AvaloniaProperty<ResizeDirections> DirectionsProperty\n            = AvaloniaProperty.Register<ResizablePanel, ResizeDirections>(nameof(Directions), BoxedResizeDirection);\n\n        public static readonly AvaloniaProperty<ICommand> ResizeStartedCommandProperty = AvaloniaProperty.Register<ResizablePanel, ICommand>(nameof(ResizeStartedCommand));\n\n        public static readonly AvaloniaProperty<ICommand> ResizeCompletedCommandProperty = AvaloniaProperty.Register<ResizablePanel, ICommand>(nameof(ResizeCompletedCommand));\n\n        public ResizeDirections Directions\n        {\n            get => (ResizeDirections)GetValue(DirectionsProperty);\n            set => SetValue(DirectionsProperty, value);\n        }\n\n        public ICommand? ResizeStartedCommand\n        {\n            get => (ICommand)GetValue(ResizeStartedCommandProperty);\n            set => SetValue(ResizeStartedCommandProperty, value);\n        }\n\n        public ICommand? ResizeCompletedCommand\n        {\n            get => (ICommand)GetValue(ResizeCompletedCommandProperty);\n            set => SetValue(ResizeCompletedCommandProperty, value);\n        }\n\n        static ResizablePanel()\n        {\n            DefaultStyleKeyProperty.OverrideMetadata(typeof(ResizablePanel), new FrameworkPropertyMetadata(typeof(ResizablePanel)));\n            ClipToBoundsProperty.OverrideDefaultValue<ResizablePanel>(false); // to match WPF behavior\n        }\n\n        public ResizablePanel()\n        {\n            AddHandler(Thumb.DragDeltaEvent, OnResize);\n            AddHandler(Thumb.DragStartedEvent, OnDragStarted);\n            AddHandler(Thumb.DragCompletedEvent, OnDragCompleted);\n        }\n\n        private void OnDragStarted(object sender, VectorEventArgs e)\n        {\n            if (ResizeStartedCommand?.CanExecute(null) ?? false)\n            {\n                ResizeStartedCommand.Execute(null);\n            }\n        }\n\n        private void OnDragCompleted(object sender, VectorEventArgs e)\n        {\n            if (ResizeCompletedCommand?.CanExecute(null) ?? false)\n            {\n                ResizeCompletedCommand.Execute(null);\n            }\n        }\n\n        private void OnResize(object sender, VectorEventArgs e)\n        {\n            if (e.Source is Resizer resizer)\n            {\n                double resizeX = 0;\n                double resizeY = 0;\n\n                double moveX = 0;\n                double moveY = 0;\n\n                if (resizer.Direction.HasFlag(ResizeDirections.Top))\n                {\n                    moveY = resizeY = ResizeTop(e);\n                }\n\n                if (resizer.Direction.HasFlag(ResizeDirections.Bottom))\n                {\n                    resizeY = ResizeBottom(e);\n                }\n\n                if (resizer.Direction.HasFlag(ResizeDirections.Left))\n                {\n                    moveX = resizeX = ResizeLeft(e);\n                }\n\n                if (resizer.Direction.HasFlag(ResizeDirections.Right))\n                {\n                    resizeX = ResizeRight(e);\n                }\n\n                if (resizer.Direction.HasFlag(ResizeDirections.TopLeft))\n                {\n                    moveY = resizeY = ResizeTop(e);\n                    moveX = resizeX = ResizeLeft(e);\n                }\n\n                if (resizer.Direction.HasFlag(ResizeDirections.TopRight))\n                {\n                    moveY = resizeY = ResizeTop(e);\n                    resizeX = ResizeRight(e);\n                }\n\n                if (resizer.Direction.HasFlag(ResizeDirections.BottomLeft))\n                {\n                    resizeY = ResizeBottom(e);\n                    moveX = resizeX = ResizeLeft(e);\n                }\n\n                if (resizer.Direction.HasFlag(ResizeDirections.BottomRight))\n                {\n                    resizeY = ResizeBottom(e);\n                    resizeX = ResizeRight(e);\n                }\n\n                OnProcessDelta(ref resizeX, ref resizeY);\n                OnProcessDelta(ref moveX, ref moveY);\n\n                OnMove(moveX, moveY);\n\n                Width -= resizeX;\n                Height -= resizeY;\n\n                e.Handled = true;\n            }\n        }\n\n        private double ResizeBottom(VectorEventArgs e)\n        {\n            return Math.Min(-e.Vector.Y, Bounds.Height - MinHeight);\n        }\n\n        private double ResizeTop(VectorEventArgs e)\n        {\n            return Math.Min(e.Vector.Y, Bounds.Height - MinHeight);\n        }\n\n        private double ResizeRight(VectorEventArgs e)\n        {\n            return Math.Min(-e.Vector.X, Bounds.Width - MinWidth);\n        }\n\n        private double ResizeLeft(VectorEventArgs e)\n        {\n            return Math.Min(e.Vector.X, Bounds.Width - MinWidth);\n        }\n\n        protected virtual void OnMove(double x, double y)\n        {\n            Canvas.SetTop(this, Canvas.GetTop(this) + y);\n            Canvas.SetLeft(this, Canvas.GetLeft(this) + x);\n        }\n\n        protected virtual void OnProcessDelta(ref double dx, ref double dy)\n        {\n        }\n    }\n\n    public class Resizer : Thumb\n    {\n        public static readonly AvaloniaProperty<ResizeDirections> DirectionProperty\n            = AvaloniaProperty.Register<Resizer, ResizeDirections>(nameof(Direction), ResizablePanel.BoxedResizeDirection);\n\n        public ResizeDirections Direction\n        {\n            get => (ResizeDirections)GetValue(DirectionProperty);\n            set => SetValue(DirectionProperty, value);\n        }\n\n        static Resizer()\n        {\n            DefaultStyleKeyProperty.OverrideMetadata(typeof(Resizer), new FrameworkPropertyMetadata(typeof(Resizer)));\n            ClipToBoundsProperty.OverrideDefaultValue<Resizer>(false); // to match WPF behavior\n        }\n    }\n\n    [Flags]\n    public enum ResizeDirections\n    {\n        Top = 1,\n        Left = 2,\n        Bottom = 4,\n        Right = 8,\n        TopLeft = 16,\n        TopRight = 32,\n        BottomLeft = 64,\n        BottomRight = 128,\n\n        Edges = Top | Left | Bottom | Right,\n        Corners = TopLeft | TopRight | BottomLeft | BottomRight,\n        All = Edges | Corners\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.Shared/Controls/Swatches.xaml.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Input;\nusing System.Windows.Media;\nusing Nodify.Shared;\n\nnamespace Nodify\n{\n    public partial class Swatches : TemplatedControl\n    {\n        public static readonly AvaloniaProperty<Color> SelectedColorProperty\n            = AvaloniaProperty.Register<Swatches, Color>(nameof(SelectedColor), defaultBindingMode: BindingMode.TwoWay);\n\n        public static readonly AvaloniaProperty<IEnumerable<Color>> ColorsProperty\n            = AvaloniaProperty.Register<Swatches, IEnumerable<Color>>(nameof(Colors),  defaultValue: Array.Empty<Color>());\n\n        public Color SelectedColor\n        {\n            get => (Color)GetValue(SelectedColorProperty);\n            set => SetValue(SelectedColorProperty, value);\n        }\n\n        public IEnumerable<Color> Colors\n        {\n            get => (IEnumerable<Color>)GetValue(ColorsProperty);\n            set => SetValue(ColorsProperty, value);\n        }\n\n        static Swatches()\n        {\n            DefaultStyleKeyProperty.OverrideMetadata(typeof(Swatches), new FrameworkPropertyMetadata(typeof(Swatches)));\n            FocusableProperty.OverrideDefaultValue<Swatches>(true);\n        }\n\n        protected override void OnPointerPressed(PointerPressedEventArgs e)\n        {\n            var color = e.Source is Control fe && fe.DataContext is Color c ? c : (Color?)null;\n            if (color.HasValue)\n            {\n                SelectedColor = color.Value;\n            }\n\n            e.Handled = true;\n        }\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.Shared/Controls/TabControlEx.cs",
    "content": "﻿using System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Input;\n\nnamespace Nodify\n{\n    [TemplatePart(Name = ElementScrollViewer, Type = typeof(ScrollViewer))]\n    public class TabControlEx : TabControl\n    {\n        private const string ElementScrollViewer = \"PART_ScrollViewer\";\n\n        public static readonly StyledProperty<ICommand?> AddTabCommandProperty = AvaloniaProperty.Register<TabControlEx, ICommand?>(nameof(AddTabCommand));\n        public static readonly StyledProperty<bool> AutoScrollToEndProperty = AvaloniaProperty.Register<TabControlEx, bool>(nameof(AutoScrollToEnd));\n\n        public ICommand? AddTabCommand\n        {\n            get { return (ICommand?)GetValue(AddTabCommandProperty); }\n            set { SetValue(AddTabCommandProperty, value); }\n        }\n        public bool AutoScrollToEnd\n        {\n            get { return (bool)GetValue(AutoScrollToEndProperty); }\n            set { SetValue(AutoScrollToEndProperty, value); }\n        }\n\n        protected ScrollViewer? ScrollViewer { get; private set; }\n\n        static TabControlEx()\n        {\n            DefaultStyleKeyProperty.OverrideMetadata(typeof(TabControlEx), new FrameworkPropertyMetadata(typeof(TabControlEx)));\n        }\n\n        protected override void OnApplyTemplate(TemplateAppliedEventArgs e)\n        {\n            base.OnApplyTemplate(e);\n\n            ScrollViewer = e.NameScope.Find<ScrollViewer>(ElementScrollViewer);\n            if(ScrollViewer != null)\n            {\n                ScrollViewer.ScrollChanged += OnScrollChanged;\n            }\n        }\n\n        private void OnScrollChanged(object? sender, ScrollChangedEventArgs e)\n        {\n            if(e.ExtentDelta.X > 0 && ScrollViewer.Viewport.Width < ScrollViewer.Extent.Width && AutoScrollToEnd)\n            {\n                ScrollViewer?.ScrollToEnd();\n            }\n        }\n\n        protected override Control CreateContainerForItemOverride(object? item, int index, object? recycleKey)\n        {\n            return new TabItemEx();\n        }\n\n        protected override bool NeedsContainerOverride(object? item, int index, out object? recycleKey)\n        {\n            return NeedsContainer<TabItemEx>(item, out recycleKey);\n        }\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.Shared/Controls/TabItemEx.cs",
    "content": "﻿using System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Input;\n\nnamespace Nodify\n{\n    public class TabItemEx : TabItem\n    {\n        public static readonly StyledProperty<ICommand?>? CloseTabCommandProperty = AvaloniaProperty.Register<TabItemEx, ICommand?>(nameof(CloseTabCommand));\n        public static readonly StyledProperty<object?> CloseTabCommandParameterProperty = AvaloniaProperty.Register<TabItemEx, object?>(nameof(CloseTabCommandParameter));\n        \n        public ICommand? CloseTabCommand\n        {\n            get { return (ICommand?)GetValue(CloseTabCommandProperty); }\n            set { SetValue(CloseTabCommandProperty, value); }\n        }\n\n        public object? CloseTabCommandParameter\n        {\n            get { return (object?)GetValue(CloseTabCommandParameterProperty); }\n            set { SetValue(CloseTabCommandParameterProperty, value); }\n        }\n\n        static TabItemEx()\n        {\n            DefaultStyleKeyProperty.OverrideMetadata(typeof(TabItemEx), new FrameworkPropertyMetadata(typeof(TabItemEx)));\n        }\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.Shared/Converters/BooleanToVisibilityConverter.cs",
    "content": "﻿using System;\nusing System.Globalization;\nusing System.Windows;\nusing System.Windows.Data;\nusing System.Windows.Markup;\n\nnamespace Nodify\n{\n    public class BooleanToVisibilityConverter : MarkupExtension, IValueConverter\n    {\n        public bool FalseVisibility { get; set; } = false;\n        public bool Negate { get; set; }\n\n        public object? Convert(object value, Type targetType, object parameter, CultureInfo culture)\n        {\n            string? stringValue = value?.ToString();\n            if (bool.TryParse(stringValue, out var b))\n            {\n                return (Negate ? !b : b) ? true : FalseVisibility;\n            }\n            else if (double.TryParse(stringValue, out var d))\n            {\n                return (Negate ? !(d > 0) : (d > 0)) ? true : FalseVisibility;\n            }\n\n            bool result = value != null;\n            return (Negate ? !result : result) ? true : FalseVisibility;\n        }\n\n        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)\n            => value is bool v && v;\n\n        public override object ProvideValue(IServiceProvider serviceProvider) => this;\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.Shared/Converters/ColorToSolidColorBrushConverter.cs",
    "content": "﻿using System;\nusing System.Globalization;\nusing System.Windows.Data;\nusing System.Windows.Media;\n\nnamespace Nodify\n{\n    public class ColorToSolidColorBrushConverter : IValueConverter\n    {\n        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)\n        {\n            if (value is Color color)\n            {\n                var brush = new SolidColorBrush(color);\n\n                if(double.TryParse(parameter?.ToString(), out double opacity))\n                    brush.Opacity = opacity;\n\n                return brush;\n            }\n\n            return value;\n        }\n\n        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)\n        {\n            if (value is SolidColorBrush brush)\n                return brush.Color;\n\n            return value;\n        }\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.Shared/Converters/DebugConverter.cs",
    "content": "﻿using System;\nusing System.Globalization;\nusing System.Windows.Data;\nusing System.Windows.Markup;\n\nnamespace Nodify\n{\n    public class DebugConverter : MarkupExtension, IValueConverter\n    {\n        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)\n        {\n            Console.WriteLine($\"Value: {value} :: Parameter: {parameter}\");\n            return value;\n        }\n\n        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)\n        {\n            Console.WriteLine($\"Value: {value} :: Parameter: {parameter}\");\n            return value;\n        }\n\n        public override object ProvideValue(IServiceProvider serviceProvider) => this;\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.Shared/Converters/EnumValuesConverter.cs",
    "content": "﻿using System;\nusing System.Globalization;\nusing System.Windows.Data;\nusing System.Windows.Markup;\n\nnamespace Nodify\n{\n    public readonly struct EnumValue\n    {\n        public EnumValue(string name, object? value)\n        {\n            Name = name;\n            Value = value;\n        }\n\n        public string Name { get; }\n        public object? Value { get; }\n    }\n\n    public class EnumValuesConverter : MarkupExtension, IValueConverter\n    {\n        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)\n        {\n            if (value is Enum enumValue)\n            {\n                var type = enumValue.GetType();\n                var values = Enum.GetValues(type);\n                var names = Enum.GetNames(type);\n\n                EnumValue[] result = new EnumValue[values.Length];\n                for (int i = 0; i < values.Length; i++)\n                {\n                    result[i] = new EnumValue(names[i], values.GetValue(i));\n                }\n\n                return result;\n            }\n\n            return value;\n        }\n\n        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)\n        {\n            throw new NotImplementedException();\n        }\n\n        public override object ProvideValue(IServiceProvider serviceProvider) => this;\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.Shared/Converters/InverseBooleanConverter.cs",
    "content": "﻿using System;\nusing System.Globalization;\nusing System.Windows.Data;\n\nnamespace Nodify\n{\n    public class InverseBooleanConverter : IValueConverter\n    {\n        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)\n        {\n            if (targetType != typeof(bool))\n                throw new InvalidOperationException(\"The value must be of type bool.\");\n\n            return !(bool)value;\n        }\n\n        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)\n        {\n            if (targetType != typeof(bool))\n                throw new InvalidOperationException(\"The value must be of type bool.\");\n\n            return !(bool)value;\n        }\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.Shared/Converters/MultiValueEqualityConverter.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Globalization;\nusing System.Linq;\nusing System.Windows.Data;\n\nnamespace Nodify\n{\n    public class MultiValueEqualityConverter : IMultiValueConverter\n    {\n        public object? Convert(IList<object?> values, Type targetType, object? parameter, CultureInfo culture)\n        {\n            if (values == null)\n            {\n                return false;\n            }\n\n            return AllElementsEqual(values) || AllElementsNull(values);\n        }\n\n        public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)\n        {\n            throw new NotImplementedException();\n        }\n\n        private static bool AllElementsEqual(IEnumerable<object> values)\n        {\n            object? firstElement = values.FirstOrDefault();\n            return values.All(o => o?.Equals(firstElement) == true);\n        }\n\n        private static bool AllElementsNull(IEnumerable<object> values)\n        {\n            return values.All(o => o == null);\n        }\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.Shared/Converters/RandomBrushConverter.cs",
    "content": "﻿using System;\nusing System.Globalization;\nusing System.Windows.Data;\nusing System.Windows.Media;\n\nnamespace Nodify\n{\n    public class RandomBrushConverter : IValueConverter\n    {\n        private readonly Random _rand = new Random();\n\n        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)\n        {\n            if (double.TryParse(parameter?.ToString(), out double alpha))\n            {\n                return new SolidColorBrush(Color.FromRgb((byte)_rand.Next(256), (byte)_rand.Next(256), (byte)_rand.Next(256)))\n                {\n                    Opacity = alpha\n                };\n            }\n\n            return new SolidColorBrush(Color.FromRgb((byte)_rand.Next(256), (byte)_rand.Next(256), (byte)_rand.Next(256)));\n        }\n\n        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)\n        {\n            return value;\n        }\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.Shared/Converters/ResizeDirectionToVisiblityConverter.cs",
    "content": "﻿using System;\nusing System.Globalization;\nusing System.Windows;\nusing System.Windows.Data;\n\nnamespace Nodify\n{\n    internal class ResizeDirectionToVisiblityConverter : IValueConverter\n    {\n        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)\n        {\n            if (!(value is ResizeDirections resizeDirections))\n            {\n                return false;\n            }\n\n            if (Enum.TryParse(parameter.ToString(), out ResizeDirections direction))\n            {\n                return resizeDirections.HasFlag(direction) ? true : false;\n            }\n\n            return false;\n        }\n\n        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)\n        {\n            throw new NotImplementedException();\n        }\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.Shared/Converters/StringToVisibilityConverter.cs",
    "content": "﻿using System;\nusing System.Globalization;\nusing System.Windows;\nusing System.Windows.Data;\nusing System.Windows.Markup;\n\nnamespace Nodify\n{\n    public class StringToVisibilityConverter : MarkupExtension, IValueConverter\n    {\n        public bool NullVisibility { get; set; } = false;\n\n        public object? Convert(object value, Type targetType, object parameter, CultureInfo culture) \n            => string.IsNullOrEmpty(value as string) ? NullVisibility : true;\n\n        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)\n            => throw new NotImplementedException();\n\n        public override object ProvideValue(IServiceProvider serviceProvider) => this;\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.Shared/Converters/ToStringConverter.cs",
    "content": "﻿using System;\nusing System.Globalization;\nusing System.Windows;\nusing System.Windows.Data;\n\nnamespace Nodify\n{\n    public class ToStringConverter : IValueConverter\n    {\n        public object? Convert(object? value, Type targetType, object parameter, CultureInfo culture)\n        {\n            if (value is Point p)\n            {\n                return $\"{p.X:0.0}, {p.Y:0.0}\";\n            }\n\n            if (value is Size s)\n            {\n                return $\"{s.Width:0.0}, {s.Height:0.0}\";\n            }\n\n            if(value is double d)\n            {\n                return d.ToString(\"0.00\");\n            }\n\n            return value?.ToString();\n        }\n\n        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)\n        {\n            throw new NotImplementedException();\n        }\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.Shared/DelegateCommand.cs",
    "content": "﻿using System;\nusing System.Windows.Input;\n\nnamespace Nodify\n{\n    public interface INodifyCommand : ICommand\n    {\n        void RaiseCanExecuteChanged();\n    }\n\n    public class DelegateCommand : INodifyCommand\n    {\n        private readonly Action _action;\n        private readonly Func<bool>? _condition;\n\n        public event EventHandler? CanExecuteChanged;\n\n        public DelegateCommand(Action action, Func<bool>? executeCondition = default)\n        {\n            _action = action ?? throw new ArgumentNullException(nameof(action));\n            _condition = executeCondition;\n        }\n\n        public bool CanExecute(object? parameter)\n            => _condition?.Invoke() ?? true;\n\n        public void Execute(object? parameter)\n            => _action();\n\n        public void RaiseCanExecuteChanged()\n            => CanExecuteChanged?.Invoke(this, new EventArgs());\n    }\n\n    public class DelegateCommand<T> : INodifyCommand\n    {\n        private readonly Action<T> _action;\n        private readonly Func<T, bool>? _condition;\n\n        public event EventHandler? CanExecuteChanged;\n\n        public DelegateCommand(Action<T> action, Func<T, bool>? executeCondition = default)\n        {\n            _action = action ?? throw new ArgumentNullException(nameof(action));\n            _condition = executeCondition;\n        }\n\n        public bool CanExecute(object? parameter)\n        {\n            if (parameter is T value)\n            {\n                return _condition?.Invoke(value) ?? true;\n            }\n\n            return _condition?.Invoke(default!) ?? true;\n        }\n\n        public void Execute(object? parameter)\n        {\n            if (parameter is T value)\n            {\n                _action(value);\n            }\n            else\n            {\n                _action(default!);\n            }\n        }\n\n        public void RaiseCanExecuteChanged()\n            => CanExecuteChanged?.Invoke(this, new EventArgs());\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.Shared/FluentSyntax.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\n\nnamespace Nodify\n{\n    public static class FluentSyntax\n    {\n        public static void Then<T>(this T caller, Action<T> action)\n            => action?.Invoke(caller);\n\n        public static bool Then(this bool condition, Action action)\n        {\n            if (condition)\n            {\n                action();\n            }\n\n            return condition;\n        }\n\n        public static bool Else(this bool condition, Action action)\n        {\n            if (!condition)\n            {\n                action();\n            }\n\n            return condition;\n        }\n\n        public static IEnumerable<T> ForEach<T>(this IEnumerable<T> collection, Action<T> action)\n        {\n            if (collection is IList<T> list)\n            {\n                for (int i = 0; i < list.Count; i++)\n                {\n                    action(list[i]);\n                }\n            }\n            else\n            {\n                foreach (var item in collection)\n                {\n                    action(item);\n                }\n            }\n\n            return collection;\n        }\n\n        public static ICollection<T> AddRange<T>(this ICollection<T> collection, IEnumerable<T> values)\n        {\n            values.ForEach(collection.Add);\n            return collection;\n        }\n\n        public static ICollection<T> RemoveRange<T>(this ICollection<T> collection, IEnumerable<T> values)\n        {\n            values.ForEach(v => collection.Remove(v));\n            return collection;\n        }\n\n        public static ICollection<T> RemoveOne<T>(this ICollection<T> collection, Func<T, bool> search)\n        {\n            if (collection.FirstOrDefault(search) is { } x)\n            {\n                collection.Remove(x);\n            }\n\n            return collection;\n        }\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.Shared/GlobalUsings.cs",
    "content": "global using Avalonia.Controls;\nglobal using Avalonia;\nglobal using Avalonia.Data.Converters;\nglobal using Avalonia.Markup.Xaml;\nglobal using System.Windows.Input;\nglobal using Avalonia.Interactivity;\nglobal using Avalonia.Input;\nglobal using Avalonia.Threading;\nglobal using Avalonia.Controls.Metadata;\nglobal using Avalonia.Controls.Primitives;\nglobal using Avalonia.Data;\nglobal using Avalonia.Layout;\nglobal using Avalonia.Media;\nglobal using Nodify.Compatibility;\nglobal using System.Linq;\nglobal using Avalonia.Markup.Xaml.Styling;\nglobal using Avalonia.Styling;"
  },
  {
    "path": "Examples/Nodify.Shared/Nodify.Shared.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <TargetFrameworks>net9</TargetFrameworks>\n    <Nullable>enable</Nullable>\n    <AssemblyOriginatorKeyFile>..\\..\\build\\Nodify.snk</AssemblyOriginatorKeyFile>\n    <SignAssembly>true</SignAssembly>\n  </PropertyGroup>\n  \n  <ItemGroup>\n    <PackageReference Include=\"Avalonia\" Version=\"$(AvaloniaVersion)\"/>\n    <PackageReference Include=\"Avalonia.Xaml.Behaviors\" Version=\"11.1.0.4\"/>\n  </ItemGroup>\n\n  <ItemGroup>\n    <None Remove=\"Themes\\*.xaml\" />\n    <AvaloniaResource Include=\"Themes\\*.xaml\" />\n  </ItemGroup>\n\n  <ItemGroup>\n    <ProjectReference Include=\"..\\..\\Nodify\\Nodify.csproj\" />\n  </ItemGroup>\n</Project>\n"
  },
  {
    "path": "Examples/Nodify.Shared/NodifyObservableCollection.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Collections.ObjectModel;\nusing System.Collections.Specialized;\nusing System.ComponentModel;\n\nnamespace Nodify\n{\n    public interface INodifyObservableCollection<T>\n    {\n        /// <summary>\n        /// Called when a new item is added\n        /// </summary>\n        /// <param name=\"added\">The callback to execute when an item is added</param>\n        /// <returns>Returns self</returns>\n        INodifyObservableCollection<T> WhenAdded(Action<T> added);\n\n        /// <summary>\n        /// Called when an existing item is removed\n        /// Note: It is not called when items are cleared if <see cref=\"WhenCleared(Action{IList{T}})\"/> is used\n        /// </summary>\n        /// <param name=\"added\">The callback to execute when an item is removed</param>\n        /// <returns>Returns self</returns>\n        INodifyObservableCollection<T> WhenRemoved(Action<T> removed);\n\n        /// <summary>\n        /// Called when the collection is cleared\n        /// NOTE: It does not call <see cref=\"WhenRemoved(Action{T})\"/> on each item\n        /// </summary>\n        /// <param name=\"added\">The callback to execute when the collection is cleared</param>\n        /// <returns>Returns self</returns>\n        INodifyObservableCollection<T> WhenCleared(Action<IList<T>> cleared);\n    }\n\n    public class NodifyObservableCollection<T> : Collection<T>, INodifyObservableCollection<T>, INotifyPropertyChanged, INotifyCollectionChanged\n    {\n        protected static readonly PropertyChangedEventArgs IndexerPropertyChanged = new PropertyChangedEventArgs(\"Item[]\");\n        protected static readonly PropertyChangedEventArgs CountPropertyChanged = new PropertyChangedEventArgs(\"Count\");\n        protected static readonly NotifyCollectionChangedEventArgs ResetCollectionChanged = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset);\n\n        private readonly List<Action<T>> _added = new List<Action<T>>();\n        private readonly List<Action<T>> _removed = new List<Action<T>>();\n        private readonly List<Action<IList<T>>> _cleared = new List<Action<IList<T>>>();\n\n        public event NotifyCollectionChangedEventHandler? CollectionChanged;\n        public event PropertyChangedEventHandler? PropertyChanged;\n\n        public NodifyObservableCollection()\n        {\n        }\n\n        public NodifyObservableCollection(IEnumerable<T> collection)\n            : base(new List<T>(collection))\n        {\n        }\n\n        #region Collection Events\n\n        public INodifyObservableCollection<T> WhenAdded(Action<T> added)\n        {\n            if (added != null)\n            {\n                _added.Add(added);\n            }\n            return this;\n        }\n\n        public INodifyObservableCollection<T> WhenRemoved(Action<T> removed)\n        {\n            if (removed != null)\n            {\n                _removed.Add(removed);\n            }\n            return this;\n        }\n\n        public INodifyObservableCollection<T> WhenCleared(Action<IList<T>> cleared)\n        {\n            if (cleared != null)\n            {\n                _cleared.Add(cleared);\n            }\n            return this;\n        }\n\n        protected virtual void NotifyOnItemAdded(T item)\n        {\n            for (int i = 0; i < _added.Count; i++)\n            {\n                _added[i](item);\n            }\n        }\n\n        protected virtual void NotifyOnItemRemoved(T item)\n        {\n            for (int i = 0; i < _removed.Count; i++)\n            {\n                _removed[i](item);\n            }\n        }\n\n        protected virtual void NotifyOnItemsCleared(IList<T> items)\n        {\n            for (int i = 0; i < _cleared.Count; i++)\n            {\n                _cleared[i](items);\n            }\n        }\n\n        #endregion\n\n        #region Collection Handlers\n\n        protected override void ClearItems()\n        {\n            var items = _cleared.Count > 0 || _removed.Count > 0 ? new List<T>(this) : new List<T>();\n            base.ClearItems();\n\n            if (_cleared.Count > 0)\n            {\n                NotifyOnItemsCleared(items);\n            }\n            else if (_removed.Count > 0)\n            {\n                for (int i = 0; i < items.Count; i++)\n                {\n                    NotifyOnItemRemoved(items[i]);\n                }\n            }\n\n            OnPropertyChanged(CountPropertyChanged);\n            OnPropertyChanged(IndexerPropertyChanged);\n            OnCollectionChanged(ResetCollectionChanged);\n        }\n\n        protected override void InsertItem(int index, T item)\n        {\n            base.InsertItem(index, item);\n\n            OnPropertyChanged(CountPropertyChanged);\n            OnPropertyChanged(IndexerPropertyChanged);\n            OnCollectionChanged(NotifyCollectionChangedAction.Add, item, index);\n            NotifyOnItemAdded(item);\n        }\n\n        protected override void RemoveItem(int index)\n        {\n            var item = base[index];\n            base.RemoveItem(index);\n\n            OnPropertyChanged(CountPropertyChanged);\n            OnPropertyChanged(IndexerPropertyChanged);\n            OnCollectionChanged(NotifyCollectionChangedAction.Remove, item, index);\n            NotifyOnItemRemoved(item);\n        }\n\n        protected override void SetItem(int index, T item)\n        {\n            T prev = base[index];\n            base.SetItem(index, item);\n            OnPropertyChanged(IndexerPropertyChanged);\n            OnCollectionChanged(NotifyCollectionChangedAction.Replace, prev, item, index);\n            NotifyOnItemRemoved(prev);\n            NotifyOnItemAdded(item);\n        }\n\n        public void Move(int oldIndex, int newIndex)\n        {\n            T prev = base[oldIndex];\n            base.RemoveItem(oldIndex);\n            base.InsertItem(newIndex, prev);\n            OnPropertyChanged(IndexerPropertyChanged);\n            OnCollectionChanged(NotifyCollectionChangedAction.Move, prev, newIndex, oldIndex);\n        }\n\n        protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs e)\n            => CollectionChanged?.Invoke(this, e);\n\n        protected virtual void OnPropertyChanged(PropertyChangedEventArgs args)\n            => PropertyChanged?.Invoke(this, args);\n\n        private void OnCollectionChanged(NotifyCollectionChangedAction action, object? item, int index)\n            => OnCollectionChanged(new NotifyCollectionChangedEventArgs(action, item, index));\n\n        private void OnCollectionChanged(NotifyCollectionChangedAction action, object? item, int index, int oldIndex)\n            => OnCollectionChanged(new NotifyCollectionChangedEventArgs(action, item, index, oldIndex));\n\n        private void OnCollectionChanged(NotifyCollectionChangedAction action, object? oldItem, object? newItem, int index)\n            => OnCollectionChanged(new NotifyCollectionChangedEventArgs(action, newItem, oldItem, index));\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.Shared/ObservableObject.cs",
    "content": "﻿using System.Collections.Generic;\nusing System;\nusing System.ComponentModel;\nusing System.Runtime.CompilerServices;\nusing System.Windows;\n\nnamespace Nodify\n{\n    public class ObservableObject : INotifyPropertyChanged\n    {\n        /// <summary>\n        /// Gets or sets the dispatcher to use to dispatch PropertyChanged events. Defaults to UI thread.\n        /// </summary>\n        public virtual Action<Action> PropertyChangedDispatcher { get; set; } = Dispatcher.UIThread.Invoke;\n\n        /// <summary>\n        /// Occurs when a property value changes\n        /// </summary>\n        public event PropertyChangedEventHandler? PropertyChanged;\n\n        /// <summary>\n        /// Fires the PropertyChanged notification.\n        /// </summary>\n        /// <remarks>Specially named so that Fody.PropertyChanged calls it</remarks>\n        /// <param name=\"propertyName\">Name of the property to raise the notification for</param>\n        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = \"\")\n        {\n            PropertyChangedDispatcher(() => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)));\n        }\n\n        /// <summary>\n        /// Takes, by reference, a field, and its new value. If field != value, will set field = value and raise a PropertyChanged notification\n        /// </summary>\n        /// <typeparam name=\"T\">Type of field being set and notified</typeparam>\n        /// <param name=\"field\">Field to assign</param>\n        /// <param name=\"value\">Value to assign to the field, if it differs</param>\n        /// <param name=\"propertyName\">Name of the property to notify for. Defaults to the calling property</param>\n        /// <returns>True if field != value and a notification was raised; false otherwise</returns>\n        protected virtual bool SetProperty<T>(ref T field, T value, [CallerMemberName] string propertyName = \"\")\n        {\n            if (!EqualityComparer<T>.Default.Equals(field, value))\n            {\n                field = value;\n                OnPropertyChanged(propertyName);\n                return true;\n            }\n\n            return false;\n        }\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.Shared/Properties/AssemblyInfo.cs",
    "content": "using System.Windows;\n\n[assembly: ThemeInfo(\n    ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located\n                                     //(used if a resource is not found in the page,\n                                     // or application resource dictionaries)\n    ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located\n                                              //(used if a resource is not found in the page,\n                                              // app, or any theme specific resource dictionaries)\n)]\n\n[assembly: System.Runtime.CompilerServices.InternalsVisibleTo(\"Nodify.StateMachine, PublicKey=0024000004800000940000000602000000240000525341310004000001000100dd4a9985a64bac9b6ff775d13d7b4a0ed7cd2826239ac3ed2a5117bc2ca38128a0b204567e3bb9ef1ff99d9e8b676a5edfd713c23285c12757dd52272233fbb9bd48bfd4d4f4554dabd09243931195249cd7f006e92c5edbab4c4c1205d2edfd77cdc0d2b8bef6d26b1803989399d22b5d25ce4141a9d6a4f708fcc011c6e1cf\")]\n[assembly: System.Runtime.CompilerServices.InternalsVisibleTo(\"Nodify.Calculator, PublicKey=0024000004800000940000000602000000240000525341310004000001000100dd4a9985a64bac9b6ff775d13d7b4a0ed7cd2826239ac3ed2a5117bc2ca38128a0b204567e3bb9ef1ff99d9e8b676a5edfd713c23285c12757dd52272233fbb9bd48bfd4d4f4554dabd09243931195249cd7f006e92c5edbab4c4c1205d2edfd77cdc0d2b8bef6d26b1803989399d22b5d25ce4141a9d6a4f708fcc011c6e1cf\")]\n[assembly: System.Runtime.CompilerServices.InternalsVisibleTo(\"Nodify.Playground, PublicKey=0024000004800000940000000602000000240000525341310004000001000100dd4a9985a64bac9b6ff775d13d7b4a0ed7cd2826239ac3ed2a5117bc2ca38128a0b204567e3bb9ef1ff99d9e8b676a5edfd713c23285c12757dd52272233fbb9bd48bfd4d4f4554dabd09243931195249cd7f006e92c5edbab4c4c1205d2edfd77cdc0d2b8bef6d26b1803989399d22b5d25ce4141a9d6a4f708fcc011c6e1cf\")]\n[assembly: System.Runtime.CompilerServices.InternalsVisibleTo(\"Nodify.Shapes, PublicKey=0024000004800000940000000602000000240000525341310004000001000100dd4a9985a64bac9b6ff775d13d7b4a0ed7cd2826239ac3ed2a5117bc2ca38128a0b204567e3bb9ef1ff99d9e8b676a5edfd713c23285c12757dd52272233fbb9bd48bfd4d4f4554dabd09243931195249cd7f006e92c5edbab4c4c1205d2edfd77cdc0d2b8bef6d26b1803989399d22b5d25ce4141a9d6a4f708fcc011c6e1cf\")]"
  },
  {
    "path": "Examples/Nodify.Shared/RequeryCommand.cs",
    "content": "﻿using System;\nusing System.Windows.Input;\n\nnamespace Nodify\n{\n    public class RequeryCommand : INodifyCommand\n    {\n        private readonly Action _action;\n        private readonly Func<bool>? _condition;\n\n        public event EventHandler? CanExecuteChanged\n        {\n            add => CommandManager.RequerySuggested += value;\n            remove => CommandManager.RequerySuggested -= value;\n        }\n\n        public RequeryCommand(Action action, Func<bool>? executeCondition = default)\n        {\n            _action = action ?? throw new ArgumentNullException(nameof(action));\n            _condition = executeCondition;\n        }\n\n        public bool CanExecute(object? parameter)\n            => _condition?.Invoke() ?? true;\n\n        public void Execute(object? parameter)\n            => _action();\n\n        public void RaiseCanExecuteChanged() { }\n    }\n\n    public class RequeryCommand<T> : INodifyCommand\n    {\n        private readonly Action<T> _action;\n        private readonly Func<T, bool>? _condition;\n\n        public event EventHandler? CanExecuteChanged\n        {\n            add => CommandManager.RequerySuggested += value;\n            remove => CommandManager.RequerySuggested -= value;\n        }\n\n        public RequeryCommand(Action<T> action, Func<T, bool>? executeCondition = default)\n        {\n            _action = action ?? throw new ArgumentNullException(nameof(action));\n            _condition = executeCondition;\n        }\n\n        public bool CanExecute(object? parameter)\n        {\n            if (parameter is T value)\n            {\n                return _condition?.Invoke(value) ?? true;\n            }\n\n            return _condition?.Invoke(default!) ?? true;\n        }\n\n        public void Execute(object? parameter)\n        {\n            if (parameter is T value)\n            {\n                _action(value);\n            }\n            else\n            {\n                _action(default!);\n            }\n        }\n\n        public void RaiseCanExecuteChanged() { }\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.Shared/StringExtensions.cs",
    "content": "﻿using System.Collections.Generic;\n\nnamespace Nodify\n{\n    public static class StringExtensions\n    {\n        public static string GetUnique(this ICollection<string> values, in string baseValue)\n        {\n            int counter = 1;\n            string result = baseValue;\n\n            while (values.Contains(result))\n            {\n                result = $\"{baseValue}{counter}\";\n                counter++;\n            }\n\n            return result;\n        }\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.Shared/ThemeManager.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Reflection;\nusing System.Windows;\nusing System.Windows.Input;\n\nnamespace Nodify\n{\n    public static class ThemeManager\n    {\n        private static readonly string? _assemblyName = Assembly.GetEntryAssembly()?.GetName().Name;\n\n        private static readonly Dictionary<string, List<(Uri baseUri, Uri source)>> _themesUris = new Dictionary<string, List<(Uri, Uri)>>();\n        private static readonly Dictionary<string, List<ResourceInclude>> _themesResources = new Dictionary<string, List<ResourceInclude>>();\n\n        public static string? ActiveTheme { get; private set; }\n\n        private static readonly List<string> _availableThemes = new List<string>();\n        public static IReadOnlyCollection<string> AvailableThemes => _availableThemes;\n\n        public static ICommand SetNextThemeCommand { get; }\n\n        static ThemeManager()\n        {\n            PreloadTheme(\"Dark\");\n            PreloadTheme(\"Light\");\n            PreloadTheme(\"Nodify\");\n\n            SetNextThemeCommand = new DelegateCommand(SetNextTheme);\n        }\n\n        private static List<ResourceInclude> FindExistingResources(List<(Uri baseUri, Uri source)> uris)\n        {\n            var result = new List<ResourceInclude>();\n            foreach (var d in Application.Current.Resources.MergedDictionaries)\n            {\n                if (d is ResourceInclude resourceInclude && uris.Any(x => x.source == resourceInclude.Source))\n                {\n                    result.Add(resourceInclude);\n                }\n            }\n\n            return result;\n        }\n\n        private static void PreloadTheme(string themeName)\n        {\n            if (!_themesUris.TryGetValue(themeName, out var preload))\n            {\n                preload = new List<(Uri, Uri)>(2)\n                {\n                    (new Uri(\"resm:Resources?assembly=Nodify.Shared\"), new Uri($\"avares://Nodify.Shared/Themes/{themeName}.xaml\")),\n                    (new Uri(\"resm:Resources?assembly=Nodify\"), new Uri($\"avares://Nodify/Themes/{themeName}.xaml\")),\n                };\n                if (_assemblyName != null)\n                {\n                    preload.Add((new Uri($\"resm:Resources?assembly={_assemblyName}\"), new Uri($\"avares://{_assemblyName}/Themes/{themeName}.xaml\")));\n                }\n\n                _themesUris.Add(themeName, preload);\n            }\n\n            var resources = FindExistingResources(preload);\n            if (resources.Count == 0)\n            {\n                for (int i = 0; i < preload.Count; i++)\n                {\n                    try\n                    {\n                        resources.Add(new ResourceInclude(preload[i].baseUri)\n                        {\n                            Source = preload[i].source\n                        });\n                    }\n                    catch\n                    {\n\n                    }\n                }\n            }\n            else if (ActiveTheme == null)\n            {\n                ActiveTheme = themeName;\n            }\n\n            _themesResources.Add(themeName, resources);\n            _availableThemes.Add(themeName);\n        }\n\n        public static void SetNextTheme()\n        {\n            if (ActiveTheme != null)\n            {\n                var i = _availableThemes.IndexOf(ActiveTheme);\n                var next = i + 1 == _availableThemes.Count ? 0 : i + 1;\n\n                SetTheme(_availableThemes[next]);\n            }\n            else if (_availableThemes.Count > 0)\n            {\n                SetTheme(_availableThemes[0]);\n            }\n        }\n\n        public static void SetTheme(string themeName)\n        {\n            if (!_themesResources.ContainsKey(themeName))\n            {\n                PreloadTheme(themeName);\n            }\n\n            // Load new theme if it is valid\n            if (_themesResources.TryGetValue(themeName, out var resources))\n            {\n                foreach (var res in resources)\n                {\n                    Application.Current.Resources.MergedDictionaries.Add(res);\n                }\n\n                // Unload current theme\n                if (ActiveTheme != null)\n                {\n                    foreach (var res in _themesResources[ActiveTheme])\n                    {\n                        Application.Current.Resources.MergedDictionaries.Remove(res);\n                    }\n                }\n\n                ActiveTheme = themeName;\n            }\n            \n            Application.Current.RequestedThemeVariant = themeName == \"Light\" ? ThemeVariant.Light : ThemeVariant.Dark;\n        }\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.Shared/Themes/Brushes.xaml",
    "content": "﻿<ResourceDictionary xmlns=\"https://github.com/avaloniaui\"\n                    xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n                    xmlns:o=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation/options\">\n\n    <SolidColorBrush x:Key=\"ForegroundBrush\"\n                     Color=\"{DynamicResource ForegroundColor}\" />\n\n    <SolidColorBrush x:Key=\"DisabledForegroundBrush\"\n                     Color=\"{DynamicResource DisabledForegroundColor}\" />\n\n    <SolidColorBrush x:Key=\"BackgroundBrush\"\n                     Color=\"{DynamicResource BackgroundColor}\" />\n\n    <SolidColorBrush x:Key=\"ContrastBackgroundBrush\"\n                     Color=\"{DynamicResource ContrastBackgroundColor}\" />\n    \n    <SolidColorBrush x:Key=\"HighlightedBackgroundBrush\"\n                     Color=\"{DynamicResource HighlightedBackgroundColor}\" />\n\n    <SolidColorBrush x:Key=\"BorderBrush\"\n                     Color=\"{DynamicResource BorderColor}\" />\n\n    <SolidColorBrush x:Key=\"DisabledBorderBrush\"\n                     Color=\"{DynamicResource DisabledBorderColor}\" />\n\n    <SolidColorBrush x:Key=\"HighlightedBorderBrush\"\n                     Color=\"{DynamicResource HighlightedBorderColor}\" />\n    \n    <SolidColorBrush x:Key=\"FocusedBorderBrush\"\n                     Color=\"{DynamicResource FocusedBorderColor}\" />\n    \n    <SolidColorBrush x:Key=\"GridLinesBrush\"\n                     Color=\"{DynamicResource GridLinesColor}\" />\n\n</ResourceDictionary>"
  },
  {
    "path": "Examples/Nodify.Shared/Themes/Controls.xaml",
    "content": "﻿<ResourceDictionary xmlns=\"https://github.com/avaloniaui\"\n                    xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\">\n    \n    <ControlTheme TargetType=\"{x:Type CheckBox}\" x:Key=\"{x:Type CheckBox}\"\n           BasedOn=\"{StaticResource {x:Type CheckBox}}\">\n        <Setter Property=\"MinHeight\"\n                Value=\"24\" />\n    <!--     <Setter Property=\"SnapsToDevicePixels\" -->\n    <!--             Value=\"False\" /> -->\n    <!--     <Setter Property=\"Foreground\" -->\n    <!--             Value=\"{DynamicResource ForegroundBrush}\" /> -->\n    <!--     <Setter Property=\"Background\" -->\n    <!--             Value=\"{DynamicResource BackgroundBrush}\" /> -->\n    <!--     <Setter Property=\"BorderBrush\" -->\n    <!--             Value=\"{DynamicResource BorderBrush}\" /> -->\n    <!--     <Setter Property=\"BorderThickness\" -->\n    <!--             Value=\"1\" /> -->\n    <!--     <Setter Property=\"IsThreeState\" -->\n    <!--             Value=\"False\" /> -->\n    <!--     <Setter Property=\"Template\"> -->\n    <!--         <Setter.Value> -->\n    <!--             <ControlTemplate TargetType=\"CheckBox\"> -->\n    <!--                 <DockPanel Background=\"Transparent\"> -->\n    <!--                     <Border x:Name=\"Border\" -->\n    <!--                             Width=\"16\" -->\n    <!--                             Height=\"16\" -->\n    <!--                             Background=\"{TemplateBinding Background}\" -->\n    <!--                             BorderBrush=\"{TemplateBinding BorderBrush}\" -->\n    <!--                             BorderThickness=\"{TemplateBinding BorderThickness}\" -->\n    <!--                             ClipToBounds=\"True\"> -->\n    <!--                         <Path x:Name=\"PART_CheckMark\" -->\n    <!--                               Stroke=\"{TemplateBinding Foreground}\" -->\n    <!--                               Width=\"7\" -->\n    <!--                               Height=\"7\" -->\n    <!--                               Stretch=\"Fill\" -->\n    <!--                               VerticalAlignment=\"Center\" -->\n    <!--                               HorizontalAlignment=\"Center\" -->\n    <!--                               StrokeThickness=\"2\" -->\n    <!--                               StrokeLineCap=\"Round\" -->\n    <!--                               Data=\"M 30,100 L 80,140 L 160,60\" /> -->\n    <!--                     </Border> -->\n    <!--                     <ContentPresenter Margin=\"4,0,0,0\" -->\n    <!--                                       HorizontalAlignment=\"Left\" -->\n    <!--                                       VerticalAlignment=\"Center\" -->\n    <!--                                       RecognizesAccessKey=\"True\" /> -->\n    <!--                 </DockPanel> -->\n    <!--                 <ControlTemplate.Triggers> -->\n    <!--                     <Trigger Property=\"IsMouseOver\" -->\n    <!--                              Value=\"True\"> -->\n    <!--                         <Setter Property=\"BorderBrush\" -->\n    <!--                                 Value=\"{DynamicResource HighlightedBorderBrush}\" /> -->\n    <!--                     </Trigger> -->\n    <!--                     <Trigger Property=\"IsPressed\" -->\n    <!--                              Value=\"True\"> -->\n    <!--                         <Setter Property=\"BorderBrush\" -->\n    <!--                                 Value=\"{DynamicResource FocusedBorderBrush}\" /> -->\n    <!--                     </Trigger> -->\n    <!--                     <Trigger Property=\"IsChecked\" -->\n    <!--                              Value=\"False\"> -->\n    <!--                         <Setter TargetName=\"PART_CheckMark\" -->\n    <!--                                 Property=\"Visibility\" -->\n    <!--                                 Value=\"Collapsed\" /> -->\n    <!--                     </Trigger> -->\n    <!--                     <Trigger Property=\"IsEnabled\" -->\n    <!--                              Value=\"False\"> -->\n    <!--                         <Setter Property=\"BorderBrush\" -->\n    <!--                                 Value=\"{DynamicResource DisabledBorderBrush}\" /> -->\n    <!--                         <Setter TargetName=\"PART_CheckMark\" -->\n    <!--                                 Property=\"Stroke\" -->\n    <!--                                 Value=\"{DynamicResource DisabledBorderBrush}\" /> -->\n    <!--                     </Trigger> -->\n    <!--                 </ControlTemplate.Triggers> -->\n    <!--             </ControlTemplate> -->\n    <!--         </Setter.Value> -->\n    <!--     </Setter> -->\n    </ControlTheme>\n\n    <ControlTheme TargetType=\"{x:Type TextBox}\" x:Key=\"{x:Type TextBox}\"\n           BasedOn=\"{StaticResource {x:Type TextBox}}\">\n        <Setter Property=\"Background\"\n                Value=\"{DynamicResource BackgroundBrush}\" />\n        <Setter Property=\"BorderBrush\"\n                Value=\"{DynamicResource BorderBrush}\" />\n        <Setter Property=\"Foreground\"\n                Value=\"{DynamicResource ForegroundBrush}\" />\n        <Setter Property=\"BorderThickness\"\n                Value=\"1\" />\n        <Setter Property=\"Padding\"\n                Value=\"4 2\" />\n        <Setter Property=\"VerticalContentAlignment\"\n                Value=\"Center\" />\n    </ControlTheme>\n    <!-- -->\n    <!-- <ControlTheme TargetType=\"{x:Type ToggleButton}\" -->\n    <!--        BasedOn=\"{StaticResource {x:Type ToggleButton}}\"> -->\n    <!--     <Setter Property=\"Foreground\" -->\n    <!--             Value=\"{DynamicResource ForegroundBrush}\" /> -->\n    <!--     <Setter Property=\"Background\" -->\n    <!--             Value=\"{DynamicResource BackgroundBrush}\" /> -->\n    <!--     <Setter Property=\"BorderBrush\" -->\n    <!--             Value=\"{DynamicResource BorderBrush}\" /> -->\n    <!--     <Setter Property=\"BorderThickness\" -->\n    <!--             Value=\"1\" /> -->\n    <!--     <Setter Property=\"Template\"> -->\n    <!--         <Setter.Value> -->\n    <!--             <ControlTemplate TargetType=\"{x:Type ToggleButton}\"> -->\n    <!--                 <Grid> -->\n    <!--                     <Grid.ColumnDefinitions> -->\n    <!--                         <ColumnDefinition /> -->\n    <!--                         <ColumnDefinition Width=\"20\" /> -->\n    <!--                     </Grid.ColumnDefinitions> -->\n    <!-- -->\n    <!--                     <Border x:Name=\"PART_Border\" -->\n    <!--                             Grid.ColumnSpan=\"2\" -->\n    <!--                             Background=\"{TemplateBinding Background}\" -->\n    <!--                             BorderBrush=\"{TemplateBinding BorderBrush}\" -->\n    <!--                             BorderThickness=\"{TemplateBinding BorderThickness}\" /> -->\n    <!-- -->\n    <!--                     <Path Grid.Column=\"1\" -->\n    <!--                           Fill=\"{TemplateBinding Foreground}\" -->\n    <!--                           HorizontalAlignment=\"Center\" -->\n    <!--                           VerticalAlignment=\"Center\" -->\n    <!--                           Data=\"M0,0 L0,2 L4,6 L8,2 L8,0 L4,4 z\" /> -->\n    <!--                 </Grid> -->\n    <!--                 <ControlTemplate.Triggers> -->\n    <!--                     <Trigger Property=\"IsMouseOver\" -->\n    <!--                              Value=\"True\"> -->\n    <!--                         <Setter TargetName=\"PART_Border\" -->\n    <!--                                 Property=\"Background\" -->\n    <!--                                 Value=\"{DynamicResource HighlightedBackgroundBrush}\" /> -->\n    <!--                         <Setter Property=\"BorderBrush\" -->\n    <!--                                 TargetName=\"PART_Border\" -->\n    <!--                                 Value=\"{DynamicResource HighlightedBorderBrush}\" /> -->\n    <!--                     </Trigger> -->\n    <!--                     <Trigger Property=\"IsChecked\" -->\n    <!--                              Value=\"True\"> -->\n    <!--                         <Setter Property=\"BorderBrush\" -->\n    <!--                                 TargetName=\"PART_Border\" -->\n    <!--                                 Value=\"{DynamicResource FocusedBorderBrush}\" /> -->\n    <!--                     </Trigger> -->\n    <!--                 </ControlTemplate.Triggers> -->\n    <!--             </ControlTemplate> -->\n    <!--         </Setter.Value> -->\n    <!--     </Setter> -->\n    <!-- </ControlTheme> -->\n    <!-- -->\n    <ControlTheme TargetType=\"{x:Type ComboBox}\" x:Key=\"{x:Type ComboBox}\"\n           BasedOn=\"{StaticResource {x:Type ComboBox}}\">\n        <Setter Property=\"HorizontalAlignment\"\n                Value=\"Stretch\" />\n        <Setter Property=\"Background\"\n                Value=\"{DynamicResource BackgroundBrush}\" />\n        <Setter Property=\"Foreground\"\n                Value=\"{DynamicResource ForegroundBrush}\" />\n        <Setter Property=\"BorderBrush\"\n                Value=\"{DynamicResource BorderBrush}\" />\n        <Setter Property=\"BorderThickness\"\n                Value=\"1\" />\n        <Setter Property=\"Padding\"\n                Value=\"6,3,5,3\" />\n    <!--     <Setter Property=\"Template\"> -->\n    <!--         <Setter.Value> -->\n    <!--             <ControlTemplate TargetType=\"{x:Type ComboBox}\"> -->\n    <!--                 <Grid> -->\n    <!--                     <ToggleButton Focusable=\"False\" -->\n    <!--                                   IsThreeState=\"False\" -->\n    <!--                                   IsChecked=\"{Binding Path=IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}\" -->\n    <!--                                   IsEnabled=\"{TemplateBinding IsEnabled}\" -->\n    <!--                                   ClickMode=\"Press\" /> -->\n    <!-- -->\n    <!--                     <ContentPresenter x:Name=\"PART_Content\" -->\n    <!--                                       IsHitTestVisible=\"False\" -->\n    <!--                                       Content=\"{TemplateBinding SelectionBoxItem}\" -->\n    <!--                                       ContentTemplate=\"{TemplateBinding SelectionBoxItemTemplate}\" -->\n    <!--                                       ContentTemplateSelector=\"{TemplateBinding ItemTemplateSelector}\" -->\n    <!--                                       Margin=\"4 2 24 2\" -->\n    <!--                                       VerticalAlignment=\"Center\" -->\n    <!--                                       HorizontalAlignment=\"Left\" /> -->\n    <!-- -->\n    <!--                     <TextBox x:Name=\"PART_EditableTextBox\" -->\n    <!--                              HorizontalAlignment=\"Left\" -->\n    <!--                              VerticalAlignment=\"Center\" -->\n    <!--                              Margin=\"4 2 24 2\" -->\n    <!--                              Focusable=\"True\" -->\n    <!--                              BorderBrush=\"{TemplateBinding BorderBrush}\" -->\n    <!--                              BorderThickness=\"{TemplateBinding BorderThickness}\" -->\n    <!--                              Background=\"{TemplateBinding Background}\" -->\n    <!--                              Foreground=\"{TemplateBinding Foreground}\" -->\n    <!--                              IsReadOnly=\"{TemplateBinding IsReadOnly}\" -->\n    <!--                              Visibility=\"Hidden\" /> -->\n    <!-- -->\n    <!--                     <Popup Placement=\"Bottom\" -->\n    <!--                            IsOpen=\"{TemplateBinding IsDropDownOpen}\" -->\n    <!--                            Focusable=\"False\" -->\n    <!--                            AllowsTransparency=\"True\" -->\n    <!--                            PopupAnimation=\"Slide\"> -->\n    <!--                         <Grid SnapsToDevicePixels=\"True\" -->\n    <!--                               MinWidth=\"{TemplateBinding ActualWidth}\" -->\n    <!--                               MaxHeight=\"{TemplateBinding MaxDropDownHeight}\"> -->\n    <!--                             <Border x:Name=\"PART_DropDown\" -->\n    <!--                                     Background=\"{TemplateBinding Background}\" -->\n    <!--                                     BorderBrush=\"{TemplateBinding BorderBrush}\" -->\n    <!--                                     BorderThickness=\"{TemplateBinding BorderThickness}\" -->\n    <!--                                     MinHeight=\"15\" -->\n    <!--                                     Margin=\"0 2 0 0\" /> -->\n    <!--                             <ScrollViewer Margin=\"2 3\" -->\n    <!--                                           SnapsToDevicePixels=\"True\"> -->\n    <!--                                 <StackPanel IsItemsHost=\"True\" -->\n    <!--                                             KeyboardNavigation.DirectionalNavigation=\"Contained\" /> -->\n    <!--                             </ScrollViewer> -->\n    <!--                         </Grid> -->\n    <!--                     </Popup> -->\n    <!--                 </Grid> -->\n    <!--                 <ControlTemplate.Triggers> -->\n    <!--                     <Trigger Property=\"IsEnabled\" -->\n    <!--                              Value=\"False\"> -->\n    <!--                         <Setter Property=\"Foreground\" -->\n    <!--                                 Value=\"{DynamicResource DisabledForegroundBrush}\" /> -->\n    <!--                     </Trigger> -->\n    <!--                     <Trigger Property=\"IsEditable\" -->\n    <!--                              Value=\"True\"> -->\n    <!--                         <Setter Property=\"IsTabStop\" -->\n    <!--                                 Value=\"False\" /> -->\n    <!--                         <Setter TargetName=\"PART_EditableTextBox\" -->\n    <!--                                 Property=\"Visibility\" -->\n    <!--                                 Value=\"Visible\" /> -->\n    <!--                         <Setter TargetName=\"PART_Content\" -->\n    <!--                                 Property=\"Visibility\" -->\n    <!--                                 Value=\"Hidden\" /> -->\n    <!--                     </Trigger> -->\n    <!--                 </ControlTemplate.Triggers> -->\n    <!--             </ControlTemplate> -->\n    <!--         </Setter.Value> -->\n    <!--     </Setter> -->\n    </ControlTheme>\n    <!-- -->\n    <!-- <ControlTheme TargetType=\"{x:Type ComboBoxItem}\" -->\n    <!--        BasedOn=\"{StaticResource {x:Type ComboBoxItem}}\"> -->\n    <!--     <Setter Property=\"VerticalContentAlignment\" -->\n    <!--             Value=\"Center\" /> -->\n    <!--     <Setter Property=\"HorizontalContentAlignment\" -->\n    <!--             Value=\"Left\" /> -->\n    <!--     <Setter Property=\"Background\" -->\n    <!--             Value=\"{DynamicResource BackgroundBrush}\" /> -->\n    <!--     <Setter Property=\"BorderBrush\" -->\n    <!--             Value=\"{DynamicResource HighlightedBackgroundBrush}\" /> -->\n    <!--     <Setter Property=\"Template\"> -->\n    <!--         <Setter.Value> -->\n    <!--             <ControlTemplate TargetType=\"{x:Type ComboBoxItem}\"> -->\n    <!--                 <Border Name=\"Border\" -->\n    <!--                         Padding=\"4 2\" -->\n    <!--                         SnapsToDevicePixels=\"True\"> -->\n    <!--                     <ContentPresenter /> -->\n    <!--                 </Border> -->\n    <!--                 <ControlTemplate.Triggers> -->\n    <!--                     <Trigger Property=\"IsHighlighted\" -->\n    <!--                              Value=\"True\"> -->\n    <!--                         <Setter TargetName=\"Border\" -->\n    <!--                                 Property=\"Background\" -->\n    <!--                                 Value=\"{Binding BorderBrush, RelativeSource={RelativeSource TemplatedParent}}\" /> -->\n    <!--                     </Trigger> -->\n    <!--                     <Trigger Property=\"IsEnabled\" -->\n    <!--                              Value=\"False\"> -->\n    <!--                         <Setter Property=\"Foreground\" -->\n    <!--                                 Value=\"{DynamicResource DisabledForegroundBrush}\" /> -->\n    <!--                     </Trigger> -->\n    <!--                 </ControlTemplate.Triggers> -->\n    <!--             </ControlTemplate> -->\n    <!--         </Setter.Value> -->\n    <!--     </Setter> -->\n    <!-- </ControlTheme> -->\n    <!-- -->\n    <!-- <ControlTheme TargetType=\"{x:Type ScrollViewer}\" -->\n    <!--        BasedOn=\"{StaticResource {x:Type ScrollViewer}}\"> -->\n    <!--     <Setter Property=\"VerticalScrollBarVisibility\" -->\n    <!--             Value=\"Auto\" /> -->\n    <!--     <Setter Property=\"HorizontalScrollBarVisibility\" -->\n    <!--             Value=\"Auto\" /> -->\n    <!--     <Setter Property=\"Template\"> -->\n    <!--         <Setter.Value> -->\n    <!--             <ControlTemplate TargetType=\"{x:Type ScrollViewer}\"> -->\n    <!--                 <Grid> -->\n    <!--                     <Grid.ColumnDefinitions> -->\n    <!--                         <ColumnDefinition /> -->\n    <!--                         <ColumnDefinition Width=\"Auto\" /> -->\n    <!--                     </Grid.ColumnDefinitions> -->\n    <!--                     <Grid.RowDefinitions> -->\n    <!--                         <RowDefinition /> -->\n    <!--                         <RowDefinition Height=\"Auto\" /> -->\n    <!--                     </Grid.RowDefinitions> -->\n    <!--                     <ScrollContentPresenter CanContentScroll=\"{TemplateBinding CanContentScroll}\" -->\n    <!--                                             ContentSource=\"Content\" /> -->\n    <!--                     <ScrollBar x:Name=\"PART_VerticalScrollBar\" -->\n    <!--                                Value=\"{TemplateBinding VerticalOffset}\" -->\n    <!--                                Maximum=\"{TemplateBinding ScrollableHeight}\" -->\n    <!--                                ViewportSize=\"{TemplateBinding ViewportHeight}\" -->\n    <!--                                Visibility=\"{TemplateBinding ComputedVerticalScrollBarVisibility}\" -->\n    <!--                                Orientation=\"Vertical\" -->\n    <!--                                Width=\"7\" -->\n    <!--                                Grid.Column=\"1\" -->\n    <!--                                Grid.Row=\"0\" /> -->\n    <!--                     <ScrollBar x:Name=\"PART_HorizontalScrollBar\" -->\n    <!--                                Value=\"{TemplateBinding HorizontalOffset}\" -->\n    <!--                                Maximum=\"{TemplateBinding ScrollableWidth}\" -->\n    <!--                                ViewportSize=\"{TemplateBinding ViewportWidth}\" -->\n    <!--                                Visibility=\"{TemplateBinding ComputedHorizontalScrollBarVisibility}\" -->\n    <!--                                Orientation=\"Horizontal\" -->\n    <!--                                Height=\"7\" -->\n    <!--                                Grid.Column=\"0\" -->\n    <!--                                Grid.Row=\"1\" /> -->\n    <!--                 </Grid> -->\n    <!--             </ControlTemplate> -->\n    <!--         </Setter.Value> -->\n    <!--     </Setter> -->\n    <!-- </ControlTheme> -->\n    <!-- -->\n    <!-- <ControlTheme TargetType=\"{x:Type ScrollBar}\" -->\n    <!--        BasedOn=\"{StaticResource {x:Type ScrollBar}}\"> -->\n    <!--     <ControlTheme.Resources> -->\n    <!--         <ControlTheme TargetType=\"{x:Type Thumb}\" -->\n    <!--                BasedOn=\"{StaticResource {x:Type Thumb}}\"> -->\n    <!--             <Setter Property=\"Template\"> -->\n    <!--                 <Setter.Value> -->\n    <!--                     <ControlTemplate TargetType=\"{x:Type Thumb}\"> -->\n    <!--                         <Rectangle  -->\n    <!--                                    Height=\"{TemplateBinding Height}\" -->\n    <!--                                    Width=\"{TemplateBinding Width}\" -->\n    <!--                                    Fill=\"{DynamicResource HighlightedBackgroundBrush}\" /> -->\n    <!--                     </ControlTemplate> -->\n    <!--                 </Setter.Value> -->\n    <!--             </Setter> -->\n    <!--         </ControlTheme> -->\n    <!--     </ControlTheme.Resources> -->\n    <!--     <Setter Property=\"Background\" -->\n    <!--             Value=\"{DynamicResource BackgroundBrush}\" /> -->\n    <!--     <Setter Property=\"BorderBrush\" -->\n    <!--             Value=\"{DynamicResource BorderBrush}\" /> -->\n    <!--     <Setter Property=\"Foreground\" -->\n    <!--             Value=\"{DynamicResource ForegroundBrush}\" /> -->\n    <!--     <Setter Property=\"BorderThickness\" -->\n    <!--             Value=\"1\" /> -->\n    <!--     <Setter Property=\"Template\"> -->\n    <!--         <Setter.Value> -->\n    <!--             <ControlTemplate TargetType=\"{x:Type ScrollBar}\"> -->\n    <!--                 <Grid x:Name=\"PART_Content\" -->\n    <!--                       Height=\"{TemplateBinding Height}\" -->\n    <!--                       Width=\"{TemplateBinding Width}\" -->\n    <!--                       Margin=\"0 5 0 5\"> -->\n    <!--                     <Border Background=\"{TemplateBinding Background}\" -->\n    <!--                             BorderThickness=\"{TemplateBinding BorderThickness}\" -->\n    <!--                             BorderBrush=\"{TemplateBinding BorderBrush}\" /> -->\n    <!--                     <Track x:Name=\"PART_Track\" -->\n    <!--                            IsEnabled=\"{TemplateBinding IsMouseOver}\" -->\n    <!--                            IsDirectionReversed=\"True\"> -->\n    <!--                         <Track.Thumb> -->\n    <!--                             <Thumb /> -->\n    <!--                         </Track.Thumb> -->\n    <!--                     </Track> -->\n    <!--                 </Grid> -->\n    <!--             </ControlTemplate> -->\n    <!--         </Setter.Value> -->\n    <!--     </Setter> -->\n    <!--     <Style.Triggers> -->\n    <!--         <Trigger Property=\"Orientation\" -->\n    <!--                  Value=\"Horizontal\"> -->\n    <!--             <Setter Property=\"Width\" -->\n    <!--                     Value=\"Auto\" /> -->\n    <!--             <Setter Property=\"Template\"> -->\n    <!--                 <Setter.Value> -->\n    <!--                     <ControlTemplate TargetType=\"{x:Type ScrollBar}\"> -->\n    <!--                         <Grid x:Name=\"PART_Content\" -->\n    <!--                               Height=\"{TemplateBinding Height}\" -->\n    <!--                               Width=\"{TemplateBinding Width}\" -->\n    <!--                               Margin=\"5 0 5 0\"> -->\n    <!--                             <Border Background=\"{TemplateBinding Background}\" -->\n    <!--                                     BorderThickness=\"{TemplateBinding BorderThickness}\" -->\n    <!--                                     BorderBrush=\"{TemplateBinding BorderBrush}\" /> -->\n    <!--                             <Track x:Name=\"PART_Track\" -->\n    <!--                                    IsEnabled=\"{TemplateBinding IsMouseOver}\"> -->\n    <!--                                 <Track.Thumb> -->\n    <!--                                     <Thumb /> -->\n    <!--                                 </Track.Thumb> -->\n    <!--                             </Track> -->\n    <!--                         </Grid> -->\n    <!--                     </ControlTemplate> -->\n    <!--                 </Setter.Value> -->\n    <!--             </Setter> -->\n    <!--         </Trigger> -->\n    <!--     </Style.Triggers> -->\n    <!-- </ControlTheme> -->\n    <!-- -->\n    <!-- <ControlTheme TargetType=\"{x:Type Separator}\" -->\n    <!--        BasedOn=\"{StaticResource {x:Type Separator}}\"> -->\n    <!--     <Setter Property=\"Focusable\" -->\n    <!--             Value=\"False\" /> -->\n    <!--     <Setter Property=\"VerticalAlignment\" -->\n    <!--             Value=\"Stretch\" /> -->\n    <!--     <Setter Property=\"HorizontalAlignment\" -->\n    <!--             Value=\"Stretch\" /> -->\n    <!--     <Setter Property=\"Height\" -->\n    <!--             Value=\"1\" /> -->\n    <!--     <Setter Property=\"Width\" -->\n    <!--             Value=\"2\" /> -->\n    <!--     <Setter Property=\"Background\" -->\n    <!--             Value=\"{DynamicResource BackgroundBrush}\" /> -->\n    <!--     <Setter Property=\"BorderBrush\" -->\n    <!--             Value=\"{DynamicResource BorderBrush}\" /> -->\n    <!--     <Setter Property=\"Template\"> -->\n    <!--         <Setter.Value> -->\n    <!--             <ControlTemplate TargetType=\"{x:Type Separator}\"> -->\n    <!--                 <Border BorderThickness=\"{TemplateBinding BorderThickness}\" -->\n    <!--                         Background=\"{TemplateBinding Background}\" -->\n    <!--                         BorderBrush=\"{TemplateBinding BorderBrush}\" -->\n    <!--                         Height=\"{TemplateBinding Height}\" -->\n    <!--                         Width=\"{TemplateBinding Width}\" /> -->\n    <!--             </ControlTemplate> -->\n    <!--         </Setter.Value> -->\n    <!--     </Setter> -->\n    <!-- </ControlTheme> -->\n\n    <ControlTheme x:Key=\"IconCheckBox\"\n           TargetType=\"{x:Type CheckBox}\"\n           BasedOn=\"{StaticResource {x:Type CheckBox}}\">\n        <Setter Property=\"Padding\"\n                Value=\"1 2\" />\n        <Setter Property=\"HorizontalAlignment\"\n                Value=\"Left\" />\n        <Setter Property=\"VerticalAlignment\"\n                Value=\"Center\" />\n        <Setter Property=\"Background\"\n                Value=\"Transparent\" />\n        <Setter Property=\"BorderBrush\"\n                Value=\"{DynamicResource HighlightedBackgroundBrush}\" />\n        <Setter Property=\"Foreground\"\n                Value=\"{DynamicResource ForegroundBrush}\" />\n        <Setter Property=\"Template\">\n            <Setter.Value>\n                <ControlTemplate TargetType=\"{x:Type CheckBox}\">\n                    <Border x:Name=\"PART_Border\"\n                            BorderThickness=\"{TemplateBinding BorderThickness}\"\n                            Background=\"{TemplateBinding Background}\"\n                            Padding=\"{TemplateBinding Padding}\">\n                        <ContentPresenter HorizontalAlignment=\"Left\"\n                                          VerticalAlignment=\"Center\"\n                                          Content=\"{TemplateBinding Content}\"\n                                          ContentTemplate=\"{TemplateBinding ContentTemplate}\"\n                                          />\n                    </Border>\n                </ControlTemplate>\n            </Setter.Value>\n        </Setter>\n        <Style Selector=\"^:pointerover /template/ Border#PART_Border\">\n            <Setter Property=\"Background\"\n                    Value=\"{Binding BorderBrush, RelativeSource={RelativeSource TemplatedParent}}\" />\n        </Style>\n        <Style Selector=\"^:pressed /template/ Border#PART_Border\">\n            <Setter Property=\"Background\"\n                    Value=\"{Binding BorderBrush, RelativeSource={RelativeSource TemplatedParent}}\" />\n        </Style>\n        <Style Selector=\"^:disabled\">\n            <Setter Property=\"Foreground\"\n                    Value=\"{DynamicResource DisabledForegroundBrush}\" />\n        </Style>\n    </ControlTheme>\n\n    <ControlTheme x:Key=\"IconButton\"\n           TargetType=\"{x:Type Button}\"\n           BasedOn=\"{StaticResource {x:Type Button}}\">\n        <Setter Property=\"Padding\"\n                Value=\"1 2\" />\n        <Setter Property=\"HorizontalAlignment\"\n                Value=\"Left\" />\n        <Setter Property=\"VerticalAlignment\"\n                Value=\"Center\" />\n        <Setter Property=\"Background\"\n                Value=\"Transparent\" />\n        <Setter Property=\"BorderBrush\"\n                Value=\"{DynamicResource HighlightedBackgroundBrush}\" />\n        <Setter Property=\"Foreground\"\n                Value=\"{DynamicResource ForegroundBrush}\" />\n        <Setter Property=\"Template\">\n            <Setter.Value>\n                <ControlTemplate TargetType=\"{x:Type Button}\">\n                    <Border x:Name=\"PART_Border\"\n                            BorderThickness=\"{TemplateBinding BorderThickness}\"\n                            Background=\"{TemplateBinding Background}\"\n                            Padding=\"{TemplateBinding Padding}\">\n                        <ContentPresenter x:Name=\"contentPresenter\"\n                                          Focusable=\"False\"\n                                          Content=\"{TemplateBinding Content}\"\n                                          ContentTemplate=\"{TemplateBinding ContentTemplate}\"\n                                          Margin=\"{TemplateBinding Padding}\"\n                                          HorizontalAlignment=\"{TemplateBinding HorizontalContentAlignment}\"\n                                          VerticalAlignment=\"{TemplateBinding VerticalContentAlignment}\">\n                            <ContentPresenter.DataTemplates>\n                                <DataTemplate x:DataType=\"DrawingBrush\">\n                                    <Rectangle Fill=\"{Binding $parent[Button].Content}\" Width=\"16\" Height=\"16\" />\n                                </DataTemplate>\n                            </ContentPresenter.DataTemplates>\n                        </ContentPresenter>\n                    </Border>\n                </ControlTemplate>\n            </Setter.Value>\n        </Setter>\n        <Style Selector=\"^:pointerover /template/ Border#PART_Border\">\n            <Setter Property=\"Background\"\n                    Value=\"{Binding BorderBrush, RelativeSource={RelativeSource TemplatedParent}}\" />\n        </Style>\n        <Style Selector=\"^:pressed /template/ Border#PART_Border\">\n            <Setter Property=\"Background\"\n                    Value=\"{Binding BorderBrush, RelativeSource={RelativeSource TemplatedParent}}\" />\n        </Style>\n        <Style Selector=\"^:disabled\">\n            <Setter Property=\"Foreground\"\n                    Value=\"{DynamicResource DisabledForegroundBrush}\" />\n        </Style>\n    </ControlTheme>\n\n    <ControlTheme x:Key=\"HollowButton\"\n           TargetType=\"{x:Type Button}\"\n           BasedOn=\"{StaticResource {x:Type Button}}\">\n        <Setter Property=\"Margin\"\n                Value=\"0,0,5,0\" />\n        <Setter Property=\"Background\"\n                Value=\"Transparent\" />\n        <Setter Property=\"Padding\"\n                Value=\"7\" />\n        <Setter Property=\"BorderBrush\"\n                Value=\"OrangeRed\" />\n        <Setter Property=\"Cursor\"\n                Value=\"Hand\" />\n        <Setter Property=\"Foreground\"\n                Value=\"{DynamicResource ForegroundBrush}\" />\n        <Setter Property=\"Template\">\n            <Setter.Value>\n                <ControlTemplate TargetType=\"{x:Type Button}\">\n                    <Border Background=\"{TemplateBinding Background}\"\n                            BorderBrush=\"{TemplateBinding BorderBrush}\"\n                            BorderThickness=\"{TemplateBinding BorderThickness}\"\n                            CornerRadius=\"3\"\n                            Margin=\"{TemplateBinding Margin}\">\n                        <ContentPresenter Margin=\"{TemplateBinding Padding}\" Content=\"{TemplateBinding Content}\" ContentTemplate=\"{TemplateBinding ContentTemplate}\" />\n                    </Border>\n                </ControlTemplate>\n            </Setter.Value>\n        </Setter>\n        <Style Selector=\"^:pointerover\">\n            <Setter Property=\"Background\"\n                    Value=\"{DynamicResource HighlightedBackgroundBrush}\" />\n        </Style>\n    </ControlTheme>\n\n    <!-- <ControlTheme TargetType=\"{x:Type MenuItem}\"> -->\n    <!--     <Setter Property=\"OverridesDefaultStyle\" -->\n    <!--             Value=\"True\" /> -->\n    <!--     <Setter Property=\"Foreground\" -->\n    <!--             Value=\"{Binding Path=Foreground, RelativeSource={RelativeSource AncestorType={x:Type ContextMenu}}}\" /> -->\n    <!--     <Setter Property=\"Background\" -->\n    <!--             Value=\"{Binding Path=Background, RelativeSource={RelativeSource AncestorType={x:Type ContextMenu}}}\" /> -->\n    <!--     <Setter Property=\"BorderThickness\" -->\n    <!--             Value=\"1\" /> -->\n    <!--     <Setter Property=\"Padding\" -->\n    <!--             Value=\"3 0 0 0\" /> -->\n    <!--     <Setter Property=\"Template\"> -->\n    <!--         <Setter.Value> -->\n    <!--             <ControlTemplate TargetType=\"{x:Type MenuItem}\"> -->\n    <!--                 <Border x:Name=\"PART_Border\" -->\n    <!--                         Margin=\"{TemplateBinding Margin}\" -->\n    <!--                         Background=\"{TemplateBinding Background}\" -->\n    <!--                         BorderBrush=\"{TemplateBinding Background}\" -->\n    <!--                         BorderThickness=\"{TemplateBinding BorderThickness}\" -->\n    <!--                         Padding=\"1\"> -->\n    <!--                     <Grid> -->\n    <!--                         <Grid.ColumnDefinitions> -->\n    <!--                             <ColumnDefinition MinWidth=\"17\" -->\n    <!--                                               Width=\"Auto\" -->\n    <!--                                               SharedSizeGroup=\"MenuItemIcon\" /> -->\n    <!--                             <ColumnDefinition Width=\"Auto\" -->\n    <!--                                               SharedSizeGroup=\"MenuItemText\" /> -->\n    <!--                             <ColumnDefinition Width=\"Auto\" -->\n    <!--                                               SharedSizeGroup=\"MenuItemGesture\" /> -->\n    <!--                             <ColumnDefinition Width=\"14\" /> -->\n    <!--                         </Grid.ColumnDefinitions> -->\n    <!-- -->\n    <!--                         <ContentPresenter x:Name=\"Icon\" -->\n    <!--                                           Margin=\"4 0 6 0\" -->\n    <!--                                           VerticalAlignment=\"Center\" -->\n    <!--                                           ContentSource=\"Icon\" -->\n    <!--                                           Visibility=\"Visible\" /> -->\n    <!-- -->\n    <!--                         <ContentPresenter Grid.Column=\"1\" -->\n    <!--                                           x:Name=\"Header\" -->\n    <!--                                           Margin=\"{TemplateBinding Padding}\" -->\n    <!--                                           RecognizesAccessKey=\"True\" -->\n    <!--                                           ContentSource=\"Header\" -->\n    <!--                                           Visibility=\"Visible\" /> -->\n    <!-- -->\n    <!--                         <ContentPresenter Grid.Column=\"2\" -->\n    <!--                                           x:Name=\"InputGestureText\" -->\n    <!--                                           Margin=\"32 1 8 1\" -->\n    <!--                                           ContentSource=\"InputGestureText\" -->\n    <!--                                           VerticalAlignment=\"Center\" -->\n    <!--                                           Visibility=\"Visible\" /> -->\n    <!-- -->\n    <!--                         <Grid Grid.Column=\"3\" -->\n    <!--                               Margin=\"4 0 6 0\" -->\n    <!--                               x:Name=\"ArrowPanel\" -->\n    <!--                               VerticalAlignment=\"Center\"> -->\n    <!--                             <Path x:Name=\"ArrowPanelPath\" -->\n    <!--                                   HorizontalAlignment=\"Right\" -->\n    <!--                                   VerticalAlignment=\"Center\" -->\n    <!--                                   Fill=\"{TemplateBinding Foreground}\" -->\n    <!--                                   Data=\"M0,0 L0,8 L4,4 z\" /> -->\n    <!--                         </Grid> -->\n    <!-- -->\n    <!--                         <Popup AllowsTransparency=\"True\" -->\n    <!--                                IsOpen=\"{Binding Path=IsSubmenuOpen, RelativeSource={RelativeSource TemplatedParent}}\" -->\n    <!--                                Placement=\"Right\" -->\n    <!--                                x:Name=\"PART_Popup\" -->\n    <!--                                Focusable=\"False\" -->\n    <!--                                PopupAnimation=\"{DynamicResource {x:Static SystemParameters.MenuPopupAnimationKey}}\"> -->\n    <!--                             <Border Background=\"{Binding Path=Background, RelativeSource={RelativeSource AncestorType={x:Type ContextMenu}}}\" -->\n    <!--                                     BorderBrush=\"{Binding Path=BorderBrush, RelativeSource={RelativeSource AncestorType={x:Type ContextMenu}}}\" -->\n    <!--                                     BorderThickness=\"{Binding Path=BorderThickness, RelativeSource={RelativeSource AncestorType={x:Type ContextMenu}}}\" -->\n    <!--                                     Margin=\"0 0 5 5\"> -->\n    <!--                                 <Border.Effect> -->\n    <!--                                     <DropShadowEffect BlurRadius=\"5\" -->\n    <!--                                                       Opacity=\"0.5\" /> -->\n    <!--                                 </Border.Effect> -->\n    <!--                                 <Grid Grid.IsSharedSizeScope=\"True\"> -->\n    <!--                                     <ItemsPresenter x:Name=\"ItemsPresenter\" -->\n    <!--                                                     KeyboardNavigation.DirectionalNavigation=\"Cycle\" -->\n    <!--                                                     Grid.IsSharedSizeScope=\"True\" -->\n    <!--                                                     SnapsToDevicePixels=\"{TemplateBinding SnapsToDevicePixels}\" -->\n    <!--                                                     KeyboardNavigation.TabNavigation=\"Cycle\" /> -->\n    <!--                                 </Grid> -->\n    <!--                             </Border> -->\n    <!--                         </Popup> -->\n    <!--                     </Grid> -->\n    <!--                 </Border> -->\n    <!-- -->\n    <!--                 <ControlTemplate.Triggers> -->\n    <!--                     <Trigger Property=\"IsSuspendingPopupAnimation\" -->\n    <!--                              Value=\"True\"> -->\n    <!--                         <Setter Property=\"PopupAnimation\" -->\n    <!--                                 TargetName=\"PART_Popup\" -->\n    <!--                                 Value=\"None\" /> -->\n    <!--                     </Trigger> -->\n    <!-- -->\n    <!--                     <Trigger Property=\"Role\" -->\n    <!--                              Value=\"SubmenuItem\"> -->\n    <!--                         <Setter Property=\"DockPanel.Dock\" -->\n    <!--                                 Value=\"Top\" /> -->\n    <!--                         <Setter Property=\"Visibility\" -->\n    <!--                                 Value=\"Collapsed\" -->\n    <!--                                 TargetName=\"ArrowPanel\" /> -->\n    <!--                     </Trigger> -->\n    <!-- -->\n    <!--                     <Trigger Property=\"Icon\" -->\n    <!--                              Value=\"{x:Null}\"> -->\n    <!--                         <Setter Property=\"Visibility\" -->\n    <!--                                 Value=\"Collapsed\" -->\n    <!--                                 TargetName=\"Icon\" /> -->\n    <!--                     </Trigger> -->\n    <!-- -->\n    <!--                     <Trigger Property=\"InputGestureText\" -->\n    <!--                              Value=\"{x:Null}\"> -->\n    <!--                         <Setter Property=\"Visibility\" -->\n    <!--                                 Value=\"Collapsed\" -->\n    <!--                                 TargetName=\"InputGestureText\" /> -->\n    <!--                     </Trigger> -->\n    <!-- -->\n    <!--                     <Trigger Property=\"IsHighlighted\" -->\n    <!--                              Value=\"True\"> -->\n    <!--                         <Setter Property=\"BorderBrush\" -->\n    <!--                                 TargetName=\"PART_Border\" -->\n    <!--                                 Value=\"{DynamicResource HighlightedBorderBrush}\" /> -->\n    <!--                         <Setter Property=\"Background\"> -->\n    <!--                             <Setter.Value> -->\n    <!--                                 <SolidColorBrush Opacity=\"0.2\" -->\n    <!--                                                  Color=\"{DynamicResource FocusedBorderColor}\" /> -->\n    <!--                             </Setter.Value> -->\n    <!--                         </Setter> -->\n    <!--                         <Setter Property=\"BorderThickness\" -->\n    <!--                                 TargetName=\"PART_Border\" -->\n    <!--                                 Value=\"1\" /> -->\n    <!--                     </Trigger> -->\n    <!-- -->\n    <!--                     <Trigger Property=\"IsEnabled\" -->\n    <!--                              Value=\"False\"> -->\n    <!--                         <Setter Property=\"Foreground\" -->\n    <!--                                 Value=\"{DynamicResource DisabledForegroundBrush}\" /> -->\n    <!--                     </Trigger> -->\n    <!--                 </ControlTemplate.Triggers> -->\n    <!--             </ControlTemplate> -->\n    <!--         </Setter.Value> -->\n    <!--     </Setter> -->\n    <!-- </ControlTheme> -->\n    <!-- -->\n    <!-- <ControlTheme TargetType=\"{x:Type ContextMenu}\" -->\n    <!--        BasedOn=\"{StaticResource {x:Type ContextMenu}}\"> -->\n    <!--     <Setter Property=\"Background\" -->\n    <!--             Value=\"{DynamicResource ContrastBackgroundBrush}\" /> -->\n    <!--     <Setter Property=\"BorderBrush\" -->\n    <!--             Value=\"{DynamicResource BorderBrush}\" /> -->\n    <!--     <Setter Property=\"Foreground\" -->\n    <!--             Value=\"{DynamicResource ForegroundBrush}\" /> -->\n    <!--     <Setter Property=\"BorderThickness\" -->\n    <!--             Value=\"1\" /> -->\n    <!--     <Setter Property=\"Template\"> -->\n    <!--         <Setter.Value> -->\n    <!--             <ControlTemplate TargetType=\"{x:Type ContextMenu}\"> -->\n    <!--                 <Border SnapsToDevicePixels=\"True\" -->\n    <!--                         Margin=\"0 0 5 5\" -->\n    <!--                         BorderThickness=\"{TemplateBinding BorderThickness}\" -->\n    <!--                         BorderBrush=\"{TemplateBinding BorderBrush}\" -->\n    <!--                         Background=\"{TemplateBinding Background}\"> -->\n    <!--                     <Border.Effect> -->\n    <!--                         <DropShadowEffect BlurRadius=\"5\" -->\n    <!--                                           Opacity=\"0.5\" /> -->\n    <!--                     </Border.Effect> -->\n    <!--                     <ItemsPresenter SnapsToDevicePixels=\"True\" /> -->\n    <!--                 </Border> -->\n    <!--             </ControlTemplate> -->\n    <!--         </Setter.Value> -->\n    <!--     </Setter> -->\n    <!-- </ControlTheme> -->\n    <!-- -->\n    <ControlTheme TargetType=\"{x:Type Expander}\" x:Key=\"{x:Type Expander}\"\n           BasedOn=\"{StaticResource {x:Type Expander}}\">\n        <Setter Property=\"HorizontalAlignment\"\n                Value=\"Stretch\" />\n        <Setter Property=\"HorizontalContentAlignment\"\n                Value=\"Stretch\" />\n        <Setter Property=\"VerticalAlignment\"\n                Value=\"Stretch\" />\n        <Setter Property=\"VerticalContentAlignment\"\n                Value=\"Stretch\" />\n        <Setter Property=\"BorderThickness\"\n                Value=\"1\" />\n        <Setter Property=\"Background\"\n                Value=\"Transparent\" />\n        <Setter Property=\"BorderBrush\"\n                Value=\"Transparent\" />\n        <Setter Property=\"Foreground\"\n                Value=\"{DynamicResource ForegroundBrush}\" />\n        <Setter Property=\"MinWidth\"\n                Value=\"0\" />\n        <Setter Property=\"MinHeight\"\n                Value=\"0\" />\n        <Setter Property=\"Template\">\n            <Setter.Value>\n                <ControlTemplate TargetType=\"{x:Type Expander}\">\n                    <Border Background=\"{TemplateBinding Background}\"\n                            Padding=\"{TemplateBinding Padding}\">\n                        <DockPanel LastChildFill=\"True\">\n                            <CheckBox ContentTemplate=\"{TemplateBinding HeaderTemplate}\"\n                                      Content=\"{TemplateBinding Header}\"\n                                      FontStyle=\"{TemplateBinding FontStyle}\"\n                                      FontStretch=\"{TemplateBinding FontStretch}\"\n                                      FontWeight=\"{TemplateBinding FontWeight}\"\n                                      FontFamily=\"{TemplateBinding FontFamily}\"\n                                      FontSize=\"{TemplateBinding FontSize}\"\n                                      Foreground=\"{TemplateBinding Foreground}\"\n                                      Tag=\"{TemplateBinding Tag}\"\n                                      IsChecked=\"{Binding IsExpanded, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}\"\n                                      HorizontalContentAlignment=\"{TemplateBinding HorizontalContentAlignment}\"\n                                      VerticalContentAlignment=\"{TemplateBinding VerticalContentAlignment}\"\n                                      HorizontalAlignment=\"Stretch\"\n                                      Background=\"{TemplateBinding Background}\"\n                                      BorderThickness=\"{TemplateBinding BorderThickness}\"\n                                      BorderBrush=\"{TemplateBinding BorderBrush}\"\n                                      Cursor=\"Hand\"\n                                      x:Name=\"PART_Header\">\n                                <CheckBox.Template>\n                                    <ControlTemplate TargetType=\"{x:Type CheckBox}\">\n                                        <Border Padding=\"{TemplateBinding Padding}\"\n                                                Background=\"{TemplateBinding Background}\"\n                                                BorderBrush=\"{TemplateBinding BorderBrush}\"\n                                                BorderThickness=\"{TemplateBinding BorderThickness}\">\n                                            <StackPanel Orientation=\"Horizontal\">\n                                                <ContentPresenter Content=\"{TemplateBinding Tag}\" />\n                                                <ContentPresenter Content=\"{TemplateBinding Content}\"\n                                                                  ContentTemplate=\"{TemplateBinding ContentTemplate}\"\n                                                                  VerticalAlignment=\"Center\" />\n                                            </StackPanel>\n                                        </Border>\n                                    </ControlTemplate>\n                                </CheckBox.Template>\n                            </CheckBox>\n                            <ContentPresenter x:Name=\"PART_Content\"\n                                              Focusable=\"False\"\n                                              Margin=\"{TemplateBinding Padding}\"\n                                              Content=\"{TemplateBinding Content}\"\n                                              ContentTemplate=\"{TemplateBinding ContentTemplate}\"\n                                              IsVisible=\"False\" />\n                        </DockPanel>\n                    </Border>\n                </ControlTemplate>\n            </Setter.Value>\n        </Setter>\n        <Style Selector=\"^:expanded /template/ ContentPresenter#PART_Content\">\n            <Setter Property=\"IsVisible\" Value=\"True\" />\n        </Style>\n        <Style Selector=\"^:expanded /template/ CheckBox#PART_Header\">\n            <Setter Property=\"Background\" Value=\"{Binding BorderBrush, RelativeSource={RelativeSource TemplatedParent}}\" />\n        </Style>\n        <Style Selector=\"^:disabled\">\n            <Setter Property=\"Foreground\" Value=\"{DynamicResource DisabledForegroundBrush}\" />\n        </Style>\n        <Style Selector=\"^:right /template/ ContentPresenter#PART_Content\">\n            <Setter Property=\"(DockPanel.Dock)\" Value=\"Right\" />\n        </Style>\n        <Style Selector=\"^:right /template/ CheckBox#PART_Header\">\n            <Setter Property=\"(DockPanel.Dock)\" Value=\"Left\" />\n            <Setter Property=\"RenderTransform\">\n                <Setter.Value>\n                    <RotateTransform Angle=\"90\" />\n                </Setter.Value>\n            </Setter>\n        </Style>\n        <Style Selector=\"^:down /template/ ContentPresenter#PART_Content\">\n            <Setter Property=\"(DockPanel.Dock)\" Value=\"Bottom\" />\n        </Style>\n        <Style Selector=\"^:down /template/ CheckBox#PART_Header\">\n            <Setter Property=\"(DockPanel.Dock)\" Value=\"Top\" />\n        </Style>\n    </ControlTheme>\n\n    <!-- Override system shape defaults -->\n    <CornerRadius x:Key=\"ControlCornerRadius\">0</CornerRadius>\n    <x:Double x:Key=\"ControlContentThemeFontSize\">12</x:Double>\n</ResourceDictionary>\n"
  },
  {
    "path": "Examples/Nodify.Shared/Themes/Dark.xaml",
    "content": "﻿<ResourceDictionary xmlns=\"https://github.com/avaloniaui\"\n                    xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\">\n\n    <ResourceDictionary.MergedDictionaries>\n        <ResourceInclude Source=\"Brushes.xaml\" />\n        <ResourceInclude Source=\"Controls.xaml\" />\n    </ResourceDictionary.MergedDictionaries>\n\n    <Color x:Key=\"ForegroundColor\">White</Color>\n    <Color x:Key=\"DisabledForegroundColor\">Gray</Color>\n\n    <Color x:Key=\"BackgroundColor\">#333337</Color>\n    <Color x:Key=\"ContrastBackgroundColor\">#1B1B1C</Color>\n    <Color x:Key=\"HighlightedBackgroundColor\">#3F3F46</Color>\n\n    <Color x:Key=\"BorderColor\">#3F3F3F</Color>\n    <Color x:Key=\"DisabledBorderColor\">Gray</Color>\n    <Color x:Key=\"HighlightedBorderColor\">#7EB4EA</Color>\n    <Color x:Key=\"FocusedBorderColor\">#569DE5</Color>\n    \n    <Color x:Key=\"GridLinesColor\">#333337</Color>\n\n</ResourceDictionary>"
  },
  {
    "path": "Examples/Nodify.Shared/Themes/Generic.xaml",
    "content": "﻿<ResourceDictionary xmlns=\"https://github.com/avaloniaui\"\n                    xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n                    xmlns:local=\"clr-namespace:Nodify\">\n\n    <ResourceDictionary.MergedDictionaries>\n        <ResourceInclude Source=\"avares://Nodify.Shared/Themes/Controls.xaml\" />\n        <ResourceInclude Source=\"avares://Nodify.Shared/Themes/Icons.xaml\" />\n    </ResourceDictionary.MergedDictionaries>\n\n    <local:ResizeDirectionToVisiblityConverter x:Key=\"ResizeDirectionToVisiblityConverter\" />\n\n    <ControlTheme x:Key=\"EditableTextBlockBaseStyle\"\n           TargetType=\"{x:Type local:EditableTextBlock}\">\n        <Setter Property=\"Background\"\n                Value=\"Transparent\" />\n        <Setter Property=\"BorderThickness\"\n                Value=\"1\" />\n        <Setter Property=\"BorderBrush\"\n                Value=\"DodgerBlue\" />\n        <Setter Property=\"MinHeight\"\n                Value=\"{Binding FontSize, RelativeSource={RelativeSource Self}}\" />\n        <Setter Property=\"Padding\"\n                Value=\"0\" />\n        <Setter Property=\"Foreground\"\n                Value=\"White\" />\n        <Setter Property=\"VerticalContentAlignment\"\n                Value=\"Center\" />\n        <Setter Property=\"HorizontalContentAlignment\"\n                Value=\"Stretch\" />\n        <Setter Property=\"Cursor\"\n                Value=\"IBeam\" />\n        <Setter Property=\"Template\">\n            <Setter.Value>\n                <ControlTemplate TargetType=\"{x:Type local:EditableTextBlock}\">\n                    <Grid VerticalAlignment=\"{TemplateBinding VerticalAlignment}\"\n                          HorizontalAlignment=\"{TemplateBinding HorizontalAlignment}\">\n                        <TextBlock Text=\"{Binding Text, ElementName=PART_TextBox}\"\n                                   Background=\"{TemplateBinding Background}\"\n                                   Foreground=\"{TemplateBinding Foreground}\"\n                                   TextWrapping=\"{TemplateBinding TextWrapping}\"\n                                   Padding=\"{TemplateBinding Padding}\"\n                                   TextTrimming=\"{TemplateBinding TextTrimming}\"\n                                   VerticalAlignment=\"{TemplateBinding VerticalContentAlignment}\"\n                                   HorizontalAlignment=\"{TemplateBinding HorizontalContentAlignment}\"\n                                   x:Name=\"PART_Text\" />\n\n                        <TextBox Text=\"{Binding Text, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}\"\n                                 MinWidth=\"{TemplateBinding MinWidth}\"\n                                 MinHeight=\"{TemplateBinding MinHeight}\"\n                                 Background=\"{TemplateBinding Background}\"\n                                 Foreground=\"{TemplateBinding Foreground}\"\n                                 TextWrapping=\"{TemplateBinding TextWrapping}\"\n                                 BorderBrush=\"{TemplateBinding BorderBrush}\"\n                                 BorderThickness=\"{TemplateBinding BorderThickness}\"\n                                 AcceptsReturn=\"{TemplateBinding AcceptsReturn}\"\n                                 FontSize=\"{TemplateBinding FontSize}\"\n                                 Padding=\"{TemplateBinding Padding}\"\n                                 MaxLength=\"{TemplateBinding MaxLength}\"\n                                 MinLines=\"{TemplateBinding MinLines}\"\n                                 MaxLines=\"{TemplateBinding MaxLines}\"\n                                 VerticalContentAlignment=\"{TemplateBinding VerticalContentAlignment}\"\n                                 HorizontalContentAlignment=\"{TemplateBinding HorizontalContentAlignment}\"\n                                 AcceptsTab=\"True\"\n                                 IsVisible=\"False\"\n                                 Margin=\"-1\"\n                                 x:Name=\"PART_TextBox\" />\n                    </Grid>\n                </ControlTemplate>\n            </Setter.Value>\n        </Setter>\n        <Style Selector=\"^:editing /template/ TextBox#PART_TextBox\">\n            <Setter Property=\"IsVisible\" Value=\"True\" />\n        </Style>\n        <Style Selector=\"^:editing /template/ TextBlock#PART_Text\">\n            <Setter Property=\"IsVisible\" Value=\"False\" />\n        </Style>\n    </ControlTheme>\n\n    <ControlTheme TargetType=\"{x:Type local:EditableTextBlock}\" x:Key=\"{x:Type local:EditableTextBlock}\"\n           BasedOn=\"{StaticResource EditableTextBlockBaseStyle}\" />\n\n    <ControlTheme TargetType=\"{x:Type local:TabControlEx}\" x:Key=\"{x:Type local:TabControlEx}\">\n        <Setter Property=\"Template\">\n            <Setter.Value>\n                <ControlTemplate TargetType=\"{x:Type local:TabControlEx}\">\n                    <Grid KeyboardNavigation.TabNavigation=\"Local\" \n                          ClipToBounds=\"true\">\n                        <Grid.RowDefinitions>\n                            <RowDefinition Height=\"Auto\" />\n                            <RowDefinition Height=\"*\" />\n                        </Grid.RowDefinitions>\n                        <Grid Panel.ZIndex=\"1\"\n                              Margin=\"2,2,2,0\"\n                              Background=\"{TemplateBinding Background}\"\n                              KeyboardNavigation.TabIndex=\"1\">\n                            <ScrollViewer x:Name=\"PART_ScrollViewer\"\n                                          HorizontalScrollBarVisibility=\"Auto\"\n                                          VerticalScrollBarVisibility=\"Auto\">\n                                <Grid>\n                                    <Grid.ColumnDefinitions>\n                                        <ColumnDefinition Width=\"Auto\" />\n                                        <ColumnDefinition Width=\"Auto\" />\n                                    </Grid.ColumnDefinitions>\n                                    <ItemsPresenter Name=\"PART_ItemsPresenter\">\n                                        <ItemsPresenter.ItemsPanel>\n                                            <ItemsPanelTemplate>\n                                                <StackPanel Orientation=\"Horizontal\" />\n                                            </ItemsPanelTemplate>\n                                        </ItemsPresenter.ItemsPanel>\n                                    </ItemsPresenter>\n                                    <Button Theme=\"{StaticResource IconButton}\"\n                                            Command=\"{TemplateBinding AddTabCommand}\"\n                                            Height=\"34\"\n                                            VerticalAlignment=\"Bottom\"\n                                            BorderBrush=\"{DynamicResource HighlightedBackgroundBrush}\"\n                                            Grid.Column=\"1\"\n                                            ToolTip.Tip=\"Add new tab\">\n                                        <Path Width=\"25\" \n                                              Height=\"15\"\n                                              Data=\"{StaticResource AddGeometry}\"\n                                              Fill=\"{TemplateBinding Foreground}\"\n                                              Stretch=\"Uniform\" />\n                                    </Button>\n                                </Grid>\n                            </ScrollViewer>\n                        </Grid>\n                        <Border x:Name=\"ContentPanel\"\n                                Background=\"{TemplateBinding Background}\"\n                                BorderThickness=\"{TemplateBinding BorderThickness}\"\n                                BorderBrush=\"{TemplateBinding BorderBrush}\"\n                                KeyboardNavigation.TabNavigation=\"Local\"\n                                KeyboardNavigation.TabIndex=\"2\"\n                                Grid.Row=\"1\">\n                            <ContentPresenter x:Name=\"PART_SelectedContentHost\"\n                                              Margin=\"{TemplateBinding Padding}\"\n                                              Content=\"{TemplateBinding SelectedContent}\"\n                                              ContentTemplate=\"{TemplateBinding SelectedContentTemplate}\" />\n                        </Border>\n                    </Grid>\n                </ControlTemplate>\n            </Setter.Value>\n        </Setter>\n        <Style Selector=\"^:disabled\">\n            <Setter Property=\"Opacity\"\n                    Value=\".5\" />\n        </Style>\n    </ControlTheme>\n\n    <ControlTheme TargetType=\"{x:Type local:TabItemEx}\" x:Key=\"{x:Type local:TabItemEx}\">\n        <Setter Property=\"Background\"\n                Value=\"{DynamicResource BackgroundBrush}\" />\n        <Setter Property=\"Foreground\"\n                Value=\"{DynamicResource ForegroundBrush}\" />\n        <!-- <Setter Property=\"RenderTransform\"> -->\n        <!--     <Setter.Value> -->\n        <!--         <ScaleTransform ScaleY=\"0\" /> -->\n        <!--     </Setter.Value> -->\n        <!-- </Setter> -->\n        <Setter Property=\"RenderTransformOrigin\"\n                Value=\"1 1\" />\n        <Setter Property=\"VerticalAlignment\" \n                Value=\"Bottom\" />\n        <Setter Property=\"VerticalContentAlignment\" \n                Value=\"Stretch\" />\n        <Setter Property=\"HorizontalContentAlignment\" \n                Value=\"Stretch\" />\n        <Setter Property=\"Padding\" \n                Value=\"7\" />\n        <Setter Property=\"Template\">\n            <Setter.Value>\n                <ControlTemplate TargetType=\"{x:Type local:TabItemEx}\">\n                    <Border x:Name=\"mBorder\"\n                            BorderThickness=\"1\"\n                            CornerRadius=\"5 5 0 0\"\n                            Background=\"{TemplateBinding Background}\"\n                            Padding=\"{TemplateBinding Padding}\">\n                        <Grid>\n                            <StackPanel Orientation=\"Horizontal\">\n                                <local:EditableTextBlock Text=\"{TemplateBinding Header}\"\n                                                         Foreground=\"{TemplateBinding Foreground}\" />\n                                <Button x:Name=\"mCloseBtn\"\n                                        Command=\"{TemplateBinding CloseTabCommand}\"\n                                        CommandParameter=\"{TemplateBinding CloseTabCommandParameter}\"\n                                        Opacity=\"0\"\n                                        Margin=\"5 0 0 0\"\n                                        Background=\"{DynamicResource HighlightedBackgroundBrush}\"\n                                        BorderBrush=\"{DynamicResource BackgroundBrush}\"\n                                        Theme=\"{StaticResource IconButton}\"\n                                        ToolTip.Tip=\"Close Tab\">\n                                    <Path Width=\"16\"\n                                          Height=\"8\"\n                                          Data=\"{StaticResource CloseGeometry}\"\n                                          Fill=\"{TemplateBinding Foreground}\"\n                                          Stretch=\"Uniform\" />\n                                </Button>\n                            </StackPanel>\n                        </Grid>\n                    </Border>\n                </ControlTemplate>\n            </Setter.Value>\n        </Setter>\n        <Style Selector=\"^:pointerover\">\n            <Setter Property=\"Background\" \n                    Value=\"{DynamicResource HighlightedBackgroundBrush}\"/>\n        </Style>\n        <Style Selector=\"^:pointerover /template/ Button#mCloseBtn\">\n            <Setter Property=\"Opacity\" Value=\"1\" />\n        </Style>\n        <Style Selector=\"^:selected\">\n            <Setter Property=\"Background\" \n                    Value=\"{DynamicResource HighlightedBackgroundBrush}\" />\n            <Setter Property=\"Padding\"\n                    Value=\"8\" />\n            <Setter Property=\"TextElement.FontWeight\" \n                    Value=\"Bold\" />\n        </Style>\n        <Style Selector=\"^:selected /template/ Button#mCloseBtn\">\n            <Setter Property=\"Opacity\" Value=\"1\" />\n        </Style>\n        <!-- <Style.Triggers> -->\n        <!--     <EventTrigger RoutedEvent=\"Loaded\"> -->\n        <!--         <BeginStoryboard> -->\n        <!--             <Storyboard> -->\n        <!--                 <DoubleAnimation Duration=\"0:0:.5\" -->\n        <!--                                  From=\"0\" -->\n        <!--                                  To=\"1\"  -->\n        <!--                                  Storyboard.TargetProperty=\"(RenderTransform).(ScaleTransform.ScaleY)\"> -->\n        <!--                     <DoubleAnimation.EasingFunction> -->\n        <!--                         <ElasticEase Oscillations=\"1\" -->\n        <!--                                      Springiness=\"3\" -->\n        <!--                                      EasingMode=\"EaseOut\"/> -->\n        <!--                     </DoubleAnimation.EasingFunction> -->\n        <!--                 </DoubleAnimation> -->\n        <!--             </Storyboard> -->\n        <!--         </BeginStoryboard> -->\n        <!--     </EventTrigger> -->\n        <!-- </Style.Triggers> -->\n    </ControlTheme>\n\n    <ControlTheme TargetType=\"{x:Type local:Swatches}\" x:Key=\"{x:Type local:Swatches}\">\n        <ControlTheme.Resources>\n            <local:MultiValueEqualityConverter x:Key=\"MultiValueEqualityConverter\" />\n            <local:ColorToSolidColorBrushConverter x:Key=\"ColorToSolidColorBrushConverter\" />\n        </ControlTheme.Resources>\n        <Setter Property=\"Template\">\n            <Setter.Value>\n                <ControlTemplate TargetType=\"local:Swatches\">\n                    <Border Background=\"{TemplateBinding Background}\"\n                            BorderBrush=\"{TemplateBinding BorderBrush}\"\n                            BorderThickness=\"{TemplateBinding BorderThickness}\"\n                            Padding=\"{TemplateBinding Padding}\"\n                            CornerRadius=\"8\">\n                        <ItemsControl ItemsSource=\"{TemplateBinding Colors}\">\n                            <ItemsControl.ItemsPanel>\n                                <ItemsPanelTemplate>\n                                    <WrapPanel Orientation=\"Horizontal\" />\n                                </ItemsPanelTemplate>\n                            </ItemsControl.ItemsPanel>\n                            <ItemsControl.ItemTemplate>\n                                <DataTemplate>\n                                    <Border CornerRadius=\"50\"\n                                            Padding=\"1\"\n                                            Margin=\"3\">\n                                        <Border.Theme>\n                                            <ControlTheme TargetType=\"{x:Type Border}\">\n                                                <Setter Property=\"Background\"\n                                                        Value=\"Transparent\" />\n                                                <Setter Property=\"(Interaction.Behaviors)\">\n                                                    <BehaviorCollectionTemplate>\n                                                        <BehaviorCollection>\n                                                            <DataTrigger Property=\".\" Value=\"{Binding Path=SelectedColor, RelativeSource={RelativeSource AncestorType=local:Swatches}}\">\n                                                                <PropertySetter Property=\"Background\"\n                                                                    Value=\"White\" />\n                                                            </DataTrigger>\n                                                        </BehaviorCollection>\n                                                    </BehaviorCollectionTemplate>\n                                                </Setter>\n                                            </ControlTheme>\n                                        </Border.Theme>\n                                        <Ellipse Width=\"24\"\n                                                 Height=\"24\"\n                                                 Stroke=\"#1e293b\"\n                                                 Fill=\"{Binding ., Converter={StaticResource ColorToSolidColorBrushConverter}}\"\n                                                 Cursor=\"Hand\" />\n                                    </Border>\n                                </DataTemplate>\n                            </ItemsControl.ItemTemplate>\n                        </ItemsControl>\n                    </Border>\n                </ControlTemplate>\n            </Setter.Value>\n        </Setter>\n        <Style Selector=\"^:disabled\">\n            <Setter Property=\"Opacity\"\n                    Value=\"0.7\" />\n        </Style>\n    </ControlTheme>\n\n    <ControlTheme TargetType=\"{x:Type local:ResizablePanel}\" x:Key=\"{x:Type local:ResizablePanel}\">\n        <Setter Property=\"BorderBrush\"\n                Value=\"#1e293b\" />\n        <Setter Property=\"BorderThickness\"\n                Value=\"1\" />\n        <Setter Property=\"Template\">\n            <Setter.Value>\n                <ControlTemplate TargetType=\"local:ResizablePanel\">\n                    <Border Background=\"{TemplateBinding Background}\"\n                            BorderBrush=\"{TemplateBinding BorderBrush}\"\n                            BorderThickness=\"{TemplateBinding BorderThickness}\">\n                        <Grid>\n                            <ContentPresenter Margin=\"{TemplateBinding Padding}\" Content=\"{TemplateBinding Content}\" ContentTemplate=\"{TemplateBinding ContentTemplate}\" />\n\n                            <local:Resizer Direction=\"TopLeft\"\n                                           HorizontalAlignment=\"Left\"\n                                           VerticalAlignment=\"Top\"\n                                           IsVisible=\"{TemplateBinding Directions, Converter={StaticResource ResizeDirectionToVisiblityConverter}, ConverterParameter=TopLeft}\" />\n\n                            <local:Resizer Direction=\"TopRight\"\n                                           HorizontalAlignment=\"Right\"\n                                           VerticalAlignment=\"Top\"\n                                           IsVisible=\"{TemplateBinding Directions, Converter={StaticResource ResizeDirectionToVisiblityConverter}, ConverterParameter=TopRight}\" />\n\n                            <local:Resizer Direction=\"BottomLeft\"\n                                           HorizontalAlignment=\"Left\"\n                                           VerticalAlignment=\"Bottom\"\n                                           IsVisible=\"{TemplateBinding Directions, Converter={StaticResource ResizeDirectionToVisiblityConverter}, ConverterParameter=BottomLeft}\" />\n\n                            <local:Resizer Direction=\"BottomRight\"\n                                           HorizontalAlignment=\"Right\"\n                                           VerticalAlignment=\"Bottom\"\n                                           IsVisible=\"{TemplateBinding Directions, Converter={StaticResource ResizeDirectionToVisiblityConverter}, ConverterParameter=BottomRight}\" />\n\n                            <local:Resizer Direction=\"Left\"\n                                           HorizontalAlignment=\"Left\"\n                                           VerticalAlignment=\"Center\"\n                                           IsVisible=\"{TemplateBinding Directions, Converter={StaticResource ResizeDirectionToVisiblityConverter}, ConverterParameter=Left}\" />\n\n                            <local:Resizer Direction=\"Right\"\n                                           HorizontalAlignment=\"Right\"\n                                           VerticalAlignment=\"Center\"\n                                           IsVisible=\"{TemplateBinding Directions, Converter={StaticResource ResizeDirectionToVisiblityConverter}, ConverterParameter=Right}\" />\n\n                            <local:Resizer Direction=\"Top\"\n                                           HorizontalAlignment=\"Center\"\n                                           VerticalAlignment=\"Top\"\n                                           IsVisible=\"{TemplateBinding Directions, Converter={StaticResource ResizeDirectionToVisiblityConverter}, ConverterParameter=Top}\" />\n\n                            <local:Resizer Direction=\"Bottom\"\n                                           HorizontalAlignment=\"Center\"\n                                           VerticalAlignment=\"Bottom\"\n                                           IsVisible=\"{TemplateBinding Directions, Converter={StaticResource ResizeDirectionToVisiblityConverter}, ConverterParameter=Bottom}\" />\n                        </Grid>\n                    </Border>\n                </ControlTemplate>\n            </Setter.Value>\n        </Setter>\n    </ControlTheme>\n\n    <ControlTheme TargetType=\"{x:Type local:Resizer}\" x:Key=\"{x:Type local:Resizer}\">\n        <Setter Property=\"Background\"\n                Value=\"#1e293b\" />\n        <Setter Property=\"Width\"\n                Value=\"6\" />\n        <Setter Property=\"Height\"\n                Value=\"6\" />\n        <Setter Property=\"Template\">\n            <Setter.Value>\n                <ControlTemplate TargetType=\"local:Resizer\">\n                    <Border Background=\"{TemplateBinding Background}\"\n                            BorderBrush=\"{TemplateBinding BorderBrush}\"\n                            BorderThickness=\"{TemplateBinding BorderThickness}\" />\n                </ControlTemplate>\n            </Setter.Value>\n        </Setter>\n        <Style Selector=\"^[Direction=TopLeft]\">\n            <Setter Property=\"Cursor\"\n                    Value=\"SizeAll\" />\n            <Setter Property=\"Margin\"\n                    Value=\"-4 -4 0 0\" />\n        </Style>\n        <Style Selector=\"^[Direction=TopRight]\">\n            <Setter Property=\"Cursor\"\n                    Value=\"SizeAll\" />\n            <Setter Property=\"Margin\"\n                    Value=\"0 -4 -4 0\" />\n        </Style>\n        <Style Selector=\"^[Direction=BottomLeft]\">\n            <Setter Property=\"Cursor\"\n                    Value=\"SizeAll\" />\n            <Setter Property=\"Margin\"\n                    Value=\"-4 0 0 -4\" />\n        </Style>\n        <Style Selector=\"^[Direction=BottomRight]\">\n            <Setter Property=\"Cursor\"\n                    Value=\"SizeAll\" />\n            <Setter Property=\"Margin\"\n                    Value=\"0 0 -4 -4\" />\n        </Style>\n        <Style Selector=\"^[Direction=Left]\">\n            <Setter Property=\"Cursor\"\n                    Value=\"SizeWestEast\" />\n            <Setter Property=\"Margin\"\n                    Value=\"-4 0 0 0\" />\n        </Style>\n        <Style Selector=\"^[Direction=Right]\">\n            <Setter Property=\"Cursor\"\n                    Value=\"SizeWestEast\" />\n            <Setter Property=\"Margin\"\n                    Value=\"0 0 -4 0\" />\n        </Style>\n        <Style Selector=\"^[Direction=Top]\">\n            <Setter Property=\"Cursor\"\n                    Value=\"SizeNorthSouth\" />\n            <Setter Property=\"Margin\"\n                    Value=\"0 -4 0 0\" />\n        </Style>\n        <Style Selector=\"^[Direction=Bottom]\">\n            <Setter Property=\"Cursor\"\n                    Value=\"SizeNorthSouth\" />\n            <Setter Property=\"Margin\"\n                    Value=\"0 0 0 -4\" />\n        </Style>\n    </ControlTheme>\n</ResourceDictionary>\n"
  },
  {
    "path": "Examples/Nodify.Shared/Themes/Icons.xaml",
    "content": "﻿<ResourceDictionary xmlns=\"https://github.com/avaloniaui\"\n                    xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\">\n\n\n            <DrawingBrush x:Key=\"AlignCenterIcon\">\n                <DrawingBrush.Drawing>\n                    <DrawingGroup>\n                        <DrawingGroup.Children>\n                            <GeometryDrawing Brush=\"#00FFFFFF\"\n                                             Geometry=\"F1M16,16L0,16 0,0 16,0z\" />\n                            <GeometryDrawing Brush=\"#FFF6F6F6\"\n                                             Geometry=\"F1M6.0003,-0.000199999999999534L6.0003,3.0008 1.0003,3.0008 1.0003,6.9998 6.0003,6.9998 6.0003,8.9998 1.9993,8.9998 1.9993,13.0008 6.0003,13.0008 6.0003,15.9998 9.0003,15.9998 9.0003,13.0008 12.9993,13.0008 12.9993,8.9998 9.0003,8.9998 9.0003,6.9998 14.0003,6.9998 14.0003,3.0008 9.0003,3.0008 9.0003,-0.000199999999999534z\" />\n                            <GeometryDrawing Brush=\"#FF424242\"\n                                             Geometry=\"F1M8,6L8,10 12,10 12,12 8,12 8,15 7,15 7,12 3,12 3,10 7,10 7,6 2,6 2,4 7,4 7,1 8,1 8,4 13,4 13,6z\" />\n                        </DrawingGroup.Children>\n                    </DrawingGroup>\n                </DrawingBrush.Drawing>\n            </DrawingBrush>\n\n            <DrawingBrush x:Key=\"AlignMiddleIcon\">\n                <DrawingBrush.Drawing>\n                    <DrawingGroup>\n                        <DrawingGroup.Children>\n                            <GeometryDrawing Brush=\"#00FFFFFF\"\n                                             Geometry=\"F1M16,16L0,16 0,0 16,0z\" />\n                            <GeometryDrawing Brush=\"#FFF6F6F6\"\n                                             Geometry=\"F1M2.9997,1.9996L2.9997,6.9996 -0.000300000000000189,6.9996 -0.000300000000000189,9.9996 2.9997,9.9996 2.9997,15.0006 7.0007,15.0006 7.0007,9.9996 8.9997,9.9996 8.9997,14.0006 12.9997,14.0006 12.9997,9.9996 16.0007,9.9996 16.0007,6.9996 12.9997,6.9996 12.9997,3.0006 8.9997,3.0006 8.9997,6.9996 7.0007,6.9996 7.0007,1.9996z\" />\n                            <GeometryDrawing Brush=\"#FF424242\"\n                                             Geometry=\"F1M6,8L10,8 10,4 12,4 12,8 15,8 15,9 12,9 12,13 10,13 10,9 6,9 6,14 4,14 4,9 1,9 1,8 4,8 4,3 6,3z\" />\n                        </DrawingGroup.Children>\n                    </DrawingGroup>\n                </DrawingBrush.Drawing>\n            </DrawingBrush>\n\n            <DrawingBrush x:Key=\"AlignRightIcon\">\n                <DrawingBrush.Drawing>\n                    <DrawingGroup>\n                        <DrawingGroup.Children>\n                            <GeometryDrawing Brush=\"#00FFFFFF\"\n                                             Geometry=\"F1M16,16L0,16 0,0 16,0z\" />\n                            <GeometryDrawing Brush=\"#FFF6F6F6\"\n                                             Geometry=\"F1M12.9996,-0.000199999999999534L12.9996,3.0008 -0.000400000000000844,3.0008 -0.000400000000000844,6.9998 12.9996,6.9998 12.9996,8.9998 2.9996,8.9998 2.9996,13.0008 12.9996,13.0008 12.9996,15.9998 16.0006,15.9998 16.0006,-0.000199999999999534z\" />\n                            <GeometryDrawing Brush=\"#FF424242\"\n                                             Geometry=\"F1M13,10L4,10 4,12 13,12z M13,4L1,4 1,6 13,6z M14,1L15,1 15,15 14,15z\" />\n                        </DrawingGroup.Children>\n                    </DrawingGroup>\n                </DrawingBrush.Drawing>\n            </DrawingBrush>\n\n            <DrawingBrush x:Key=\"AlignBottomIcon\">\n                <DrawingBrush.Drawing>\n                    <DrawingGroup>\n                        <DrawingGroup.Children>\n                            <GeometryDrawing Brush=\"#00FFFFFF\"\n                                             Geometry=\"F1M16,16L0,16 0,0 16,0z\" />\n                            <GeometryDrawing Brush=\"#FFF6F6F6\"\n                                             Geometry=\"F1M2.9997,-0.000199999999999534L2.9997,13.0008 -0.000300000000000189,13.0008 -0.000300000000000189,15.9998 16.0007,15.9998 16.0007,13.0008 12.9997,13.0008 12.9997,3.0008 8.9997,3.0008 8.9997,13.0008 7.0007,13.0008 7.0007,-0.000199999999999534z\" />\n                            <GeometryDrawing Brush=\"#FF424242\"\n                                             Geometry=\"F1M12,4L10,4 10,13 12,13z M6,1L4,1 4,13 6,13z M15,15L1,15 1,14 15,14z\" />\n                        </DrawingGroup.Children>\n                    </DrawingGroup>\n                </DrawingBrush.Drawing>\n            </DrawingBrush>\n\n            <DrawingBrush x:Key=\"AlignLeftIcon\">\n                <DrawingBrush.Drawing>\n                    <DrawingGroup>\n                        <DrawingGroup.Children>\n                            <GeometryDrawing Brush=\"#00FFFFFF\"\n                                             Geometry=\"F1M16,16L0,16 0,0 16,0z\" />\n                            <GeometryDrawing Brush=\"#FFF6F6F6\"\n                                             Geometry=\"F1M0,-0.000199999999999534L0,15.9998 3,15.9998 3,13.0008 13,13.0008 13,8.9998 3,8.9998 3,6.9998 16,6.9998 16,3.0008 3,3.0008 3,-0.000199999999999534z\" />\n                            <GeometryDrawing Brush=\"#FF424242\"\n                                             Geometry=\"F1M12,10L3,10 3,12 12,12z M15,4L3,4 3,6 15,6z M1,1L2,1 2,15 1,15z\" />\n                        </DrawingGroup.Children>\n                    </DrawingGroup>\n                </DrawingBrush.Drawing>\n            </DrawingBrush>\n\n            <DrawingBrush x:Key=\"AlignTopIcon\">\n                <DrawingBrush.Drawing>\n                    <DrawingGroup>\n                        <DrawingGroup.Children>\n                            <GeometryDrawing Brush=\"#00FFFFFF\"\n                                             Geometry=\"F1M16,16L0,16 0,0 16,0z\" />\n                            <GeometryDrawing Brush=\"#FFF6F6F6\"\n                                             Geometry=\"F1M0,-0.000199999999999534L0,4.0008 3,4.0008 3,15.9998 7,15.9998 7,4.0008 9,4.0008 9,12.0008 13,12.0008 13,4.0008 16,4.0008 16,-0.000199999999999534z\" />\n                            <GeometryDrawing Brush=\"#FF424242\"\n                                             Geometry=\"F1M10,11L12,11 12,4 10,4z M4,15L6,15 6,4 4,4z M15,3L1,3 1,1 15,1z\" />\n                        </DrawingGroup.Children>\n                    </DrawingGroup>\n                </DrawingBrush.Drawing>\n            </DrawingBrush>\n\n            <DrawingBrush x:Key=\"ThemeIcon\">\n                <DrawingBrush.Drawing>\n                    <DrawingGroup>\n                        <DrawingGroup.Children>\n                            <GeometryDrawing Brush=\"#FFF6F6F6\"\n                                             Geometry=\"F1M16,8C16,12.418 12.418,16 8,16 3.582,16 0,12.418 0,8 0,3.582 3.582,0 8,0 12.418,0 16,3.582 16,8\" />\n                            <GeometryDrawing Brush=\"#FF414141\"\n                                             Geometry=\"F1M7.999,14C7.657,14 7.325,13.965 6.999,13.91 9.836,13.434 12,10.973 12,8 12,5.027 9.836,2.567 6.999,2.09 7.325,2.035 7.657,2 7.999,2 11.312,2 13.999,4.687 13.999,8 13.999,11.313 11.312,14 7.999,14 M7.999,1C4.133,1 0.999,4.134 0.999,8 0.999,11.866 4.133,15 7.999,15 11.865,15 14.999,11.866 14.999,8 14.999,4.134 11.865,1 7.999,1\" />\n                            <GeometryDrawing Brush=\"#FFF0EFF1\"\n                                             Geometry=\"F1M7.999,2C7.657,2 7.325,2.035 6.999,2.09 9.836,2.567 12,5.027 12,8 12,10.973 9.836,13.434 6.999,13.91 7.325,13.965 7.657,14 7.999,14 11.312,14 13.999,11.313 13.999,8 13.999,4.687 11.312,2 7.999,2\" />\n                        </DrawingGroup.Children>\n                    </DrawingGroup>\n                </DrawingBrush.Drawing>\n            </DrawingBrush>\n\n            <DrawingBrush x:Key=\"DiamondFillIcon\">\n                <DrawingBrush.Drawing>\n                    <DrawingGroup>\n                        <DrawingGroup.Children>\n                            <GeometryDrawing Brush=\"#00FFFFFF\"\n                                             Geometry=\"F1M16,16L0,16 0,0 16,0z\" />\n                            <GeometryDrawing Brush=\"#FFF6F6F6\"\n                                             Geometry=\"F1M1.5859,8L7.9999,1.586 14.4139,8 7.9999,14.414z\" />\n                            <GeometryDrawing Brush=\"#FF414141\"\n                                             Geometry=\"F1M4.4141,8L8.0001,4.414 11.5861,8 8.0001,11.586z M8.0001,3L3.0001,8 8.0001,13 13.0001,8z\" />\n                            <GeometryDrawing Brush=\"#FFFFCC00\"\n                                             Geometry=\"F1M8,4.4141L11.586,8.0001 8,11.5861 4.414,8.0001z\" />\n                        </DrawingGroup.Children>\n                    </DrawingGroup>\n                </DrawingBrush.Drawing>\n            </DrawingBrush>\n\n            <DrawingBrush x:Key=\"DiamondIcon\">\n                <DrawingBrush.Drawing>\n                    <DrawingGroup>\n                        <DrawingGroup.Children>\n                            <GeometryDrawing Brush=\"#00FFFFFF\"\n                                             Geometry=\"F1M16,16L0,16 0,0 16,0z\" />\n                            <GeometryDrawing Brush=\"#FFF6F6F6\"\n                                             Geometry=\"F1M1.5859,8L7.9999,1.586 14.4139,8 7.9999,14.414z\" />\n                            <GeometryDrawing Brush=\"#FFEFEFF0\"\n                                             Geometry=\"F1M8,4.4141L11.586,8.0001 8,11.5861 4.414,8.0001z\" />\n                            <GeometryDrawing Brush=\"#FF424242\"\n                                             Geometry=\"F1M4.4141,8L8.0001,4.414 11.5861,8 8.0001,11.586z M8.0001,3L3.0001,8 8.0001,13 13.0001,8z\" />\n                        </DrawingGroup.Children>\n                    </DrawingGroup>\n                </DrawingBrush.Drawing>\n            </DrawingBrush>\n\n            <DrawingBrush x:Key=\"PlusIcon\">\n                <DrawingBrush.Drawing>\n                    <DrawingGroup>\n                        <DrawingGroup.Children>\n                            <GeometryDrawing Brush=\"#00FFFFFF\"\n                                             Geometry=\"F1M16,16L0,16 0,0 16,0z\" />\n                            <GeometryDrawing Brush=\"#FFF6F6F6\"\n                                             Geometry=\"F1M6.0003,-0.000199999999999534L6.0003,5.9998 0.000300000000000189,5.9998 0.000300000000000189,9.9998 6.0003,9.9998 6.0003,15.9998 10.0003,15.9998 10.0003,9.9998 16.0003,9.9998 16.0003,5.9998 10.0003,5.9998 10.0003,-0.000199999999999534z\" />\n                            <GeometryDrawing Brush=\"#FF424242\"\n                                             Geometry=\"F1M15,9L9,9 9,15 7,15 7,9 1,9 1,7 7,7 7,1 9,1 9,7 15,7z\" />\n                        </DrawingGroup.Children>\n                    </DrawingGroup>\n                </DrawingBrush.Drawing>\n            </DrawingBrush>\n\n            <DrawingBrush x:Key=\"RemoveKeyIcon\">\n                <DrawingBrush.Drawing>\n                    <DrawingGroup>\n                        <DrawingGroup.Children>\n                            <GeometryDrawing Brush=\"#00FFFFFF\"\n                                             Geometry=\"F1M16,16L0,16 0,0 16,0z\" />\n                            <GeometryDrawing Brush=\"#FFF6F6F6\"\n                                             Geometry=\"F1M13,10L4,10 4,6 13,6z\" />\n                            <GeometryDrawing Brush=\"#FF414141\"\n                                             Geometry=\"F1M12,9L5,9 5,7 12,7z\" />\n                        </DrawingGroup.Children>\n                    </DrawingGroup>\n                </DrawingBrush.Drawing>\n            </DrawingBrush>\n\n            <DrawingBrush x:Key=\"AddKeyIcon\">\n                <DrawingBrush.Drawing>\n                    <DrawingGroup>\n                        <DrawingGroup.Children>\n                            <GeometryDrawing Brush=\"#00FFFFFF\"\n                                             Geometry=\"F1M16,16L0,16 0,0 16,0z\" />\n                            <GeometryDrawing Brush=\"#FFF6F6F6\"\n                                             Geometry=\"F1M16,8.4629C16,10.9239 13.997,12.9259 11.537,12.9259 11.118,12.9259 10.704,12.8669 10.3,12.7479L7.704,15.3439C7.595,15.4529 7.003,16.0019 6.147,16.0019 5.767,16.0019 5.2,15.8869 4.657,15.3439 3.538,14.2249 4.043,12.9079 4.657,12.2959L7.252,9.6989C7.133,9.2959 7.073,8.8819 7.073,8.4629 7.073,7.5529 7.35,6.7069 7.82,5.9999L6,5.9999 6,7.9999 2.019,7.9999 2.019,5.9999 0,5.9999 0,2.0179 2.019,2.0179 2.019,-9.99999999979906E-05 6,-9.99999999979906E-05 6,2.0179 8,2.0179 8,5.7719C8.816,4.7019 10.091,3.9999 11.537,3.9999 12.165,3.9999 13.132,4.2889 13.368,4.3999 13.605,4.5099 14.987,5.1659 15.601,6.6309 15.688,6.8399 16,7.8359 16,8.4629\" />\n                            <GeometryDrawing Brush=\"#FF424242\"\n                                             Geometry=\"F1M14.9873,8.5049C14.9873,10.4339 13.4233,11.9969 11.4953,11.9969 10.9823,11.9969 10.4323,11.9349 9.9953,11.7359L7.1303,14.6149C7.1303,14.6149 6.2573,15.4879 5.3843,14.6149 4.5103,13.7419 5.3843,12.8689 5.3843,12.8689L8.3233,10.0179C8.1263,9.5779 8.0033,9.0169 8.0033,8.5049 8.0033,6.5769 9.5663,5.0139 11.4953,5.0139 12.0063,5.0139 12.5553,5.0849 12.9953,5.2829L10.6223,7.6329 12.3683,9.3789 14.7143,7.0179C14.9113,7.4549,14.9873,7.9939,14.9873,8.5049\" />\n                            <GeometryDrawing Brush=\"#FF388A34\"\n                                             Geometry=\"F1M7,3.0176L5,3.0176 5,0.9996 3.019,0.9996 3.019,3.0176 1,3.0176 1,4.9996 3.019,4.9996 3.019,6.9996 5,6.9996 5,4.9996 7,4.9996z\" />\n                        </DrawingGroup.Children>\n                    </DrawingGroup>\n                </DrawingBrush.Drawing>\n            </DrawingBrush>\n\n            <DrawingBrush x:Key=\"ExpandRightIcon\">\n                <DrawingBrush.Drawing>\n                    <DrawingGroup>\n                        <DrawingGroup.Children>\n                            <GeometryDrawing Brush=\"#00FFFFFF\"\n                                             Geometry=\"F1M16,16L0,16 0,0 16,0z\" />\n                            <GeometryDrawing Brush=\"#FF0081E4\"\n                                             Geometry=\"F1M6.3906,13.6943L3.9156,11.2193 7.1356,8.0003 3.9156,4.7803 6.3906,2.3053 12.0836,8.0003z\" />\n                            <GeometryDrawing Brush=\"#FF0081E4\"\n                                             Geometry=\"F1M6.3901,12.2803L5.3291,11.2193 8.5491,8.0003 5.3291,4.7803 6.3901,3.7193 10.6701,8.0003z\" />\n                        </DrawingGroup.Children>\n                    </DrawingGroup>\n                </DrawingBrush.Drawing>\n            </DrawingBrush>\n\n            <DrawingBrush x:Key=\"ExpandLeftIcon\">\n                <DrawingBrush.Transform>\n                    <RotateTransform Angle=\"180\" CenterX=\"8\" CenterY=\"8\" />\n                </DrawingBrush.Transform>\n                <DrawingBrush.TransformOrigin>50%,50%</DrawingBrush.TransformOrigin>\n                <DrawingBrush.Drawing>\n                    <DrawingGroup>\n                        <DrawingGroup.Children>\n                            <GeometryDrawing Brush=\"#00FFFFFF\"\n                                             Geometry=\"F1M16,16L0,16 0,0 16,0z\" />\n                            <GeometryDrawing Brush=\"#FF0081E4\"\n                                             Geometry=\"F1M6.3906,13.6943L3.9156,11.2193 7.1356,8.0003 3.9156,4.7803 6.3906,2.3053 12.0836,8.0003z\" />\n                            <GeometryDrawing Brush=\"#FF0081E4\"\n                                             Geometry=\"F1M6.3901,12.2803L5.3291,11.2193 8.5491,8.0003 5.3291,4.7803 6.3901,3.7193 10.6701,8.0003z\" />\n                        </DrawingGroup.Children>\n                    </DrawingGroup>\n                </DrawingBrush.Drawing>\n            </DrawingBrush>\n\n            <DrawingBrush x:Key=\"ExpandDownIcon\">\n                <DrawingBrush.Drawing>\n                    <DrawingGroup>\n                        <DrawingGroup.Children>\n                            <GeometryDrawing Brush=\"#00FFFFFF\"\n                                             Geometry=\"F1M0,0L16,0 16,16 0,16z\" />\n                            <GeometryDrawing Brush=\"#FF0081E4\"\n                                             Geometry=\"F1M2.3057,6.3906L4.7807,3.9156 7.9997,7.1356 11.2197,3.9156 13.6947,6.3906 7.9997,12.0836z\" />\n                            <GeometryDrawing Brush=\"#FF0081E4\"\n                                             Geometry=\"F1M3.7197,6.3901L4.7807,5.3291 7.9997,8.5491 11.2197,5.3291 12.2807,6.3901 7.9997,10.6701z\" />\n                        </DrawingGroup.Children>\n                    </DrawingGroup>\n                </DrawingBrush.Drawing>\n            </DrawingBrush>\n\n            <DrawingBrush x:Key=\"EditIcon\">\n                <DrawingBrush.Drawing>\n                    <DrawingGroup>\n                        <DrawingGroup.Children>\n                            <GeometryDrawing Brush=\"#00FFFFFF\"\n                                             Geometry=\"F1M16,16L0,16 0,0 16,0z\" />\n                            <GeometryDrawing Brush=\"#FFF6F6F6\"\n                                             Geometry=\"F1M2.5857,-0.000199999999999534L-0.000299999999999745,2.5858 -0.000299999999999745,3.4148 1.0867,4.4998 -0.000299999999999745,5.5858 -0.000299999999999745,12.4148 2.5857,14.9998 13.4147,14.9998 16.0007,12.4148 16.0007,5.5858 13.4147,3.0008 6.4147,3.0008 3.4137,-0.000199999999999534z\" />\n                            <GeometryDrawing Brush=\"#FF424242\"\n                                             Geometry=\"F1M13,4L7.414,4 8.414,5 12.586,5 14,6.414 14,11.586 12.586,13 3.414,13 2,11.586 2,6.414 2.358,6.057 2.5,5.914 1.793,5.207 1.624,5.376 1,6 1,12 3,14 13,14 15,12 15,6z\" />\n                            <GeometryDrawing Brush=\"#FFF0EFF1\"\n                                             Geometry=\"F1M12.5858,5L8.4138,5 10.9998,7.586 10.9998,11 7.5858,11 2.4998,5.914 2.3578,6.057 1.9998,6.414 1.9998,11.586 3.4138,13 7.5858,13 12.5858,13 13.4998,12.086 13.6428,11.943 13.9998,11.586 13.9998,6.414z\" />\n                            <GeometryDrawing Brush=\"#FF00539C\"\n                                             Geometry=\"F1M5,3L3,5 7,8.999 9,6.999z\" />\n                            <GeometryDrawing Brush=\"#FF00539C\"\n                                             Geometry=\"F1M4,2L2,4 1,3 3,1z\" />\n                            <GeometryDrawing Brush=\"#FF00539C\"\n                                             Geometry=\"F1M8,10L10,9.999 10,8z\" />\n                            <GeometryDrawing Brush=\"#FF00539C\"\n                                             Geometry=\"F1M5,3L3,5 7,8.999 9,6.999z\" />\n                            <GeometryDrawing Brush=\"#FF00539C\"\n                                             Geometry=\"F1M4,2L2,4 1,3 3,1z\" />\n                            <GeometryDrawing Brush=\"#FF00539C\"\n                                             Geometry=\"F1M8,10L10,9.999 10,8z\" />\n                        </DrawingGroup.Children>\n                    </DrawingGroup>\n                </DrawingBrush.Drawing>\n            </DrawingBrush>\n\n            <DrawingBrush x:Key=\"UnpauseIcon\">\n                <DrawingBrush.Drawing>\n                    <DrawingGroup>\n                        <DrawingGroup.Children>\n                            <GeometryDrawing Brush=\"#00FFFFFF\"\n                                             Geometry=\"F1M16,16L0,16 0,0 16,0z\" />\n                            <GeometryDrawing Brush=\"#FF0081E4\"\n                                             Geometry=\"F1M4,2L4,14 12,8z\" />\n                        </DrawingGroup.Children>\n                    </DrawingGroup>\n                </DrawingBrush.Drawing>\n            </DrawingBrush>\n\n            <DrawingBrush x:Key=\"PauseIcon\">\n                <DrawingBrush.Drawing>\n                    <DrawingGroup>\n                        <DrawingGroup.Children>\n                            <GeometryDrawing Brush=\"#00f6f6f6\"\n                                             Geometry=\"M16,0V16H0V0Z\" />\n                            <GeometryDrawing Brush=\"Transparent\"\n                                             Geometry=\"M13,2V13H3V2Z\" />\n                            <GeometryDrawing Brush=\"#FF0081E4\"\n                                             Geometry=\"M4,3H7v9H4ZM9,3v9h3V3Z\" />\n                        </DrawingGroup.Children>\n                    </DrawingGroup>\n                </DrawingBrush.Drawing>\n            </DrawingBrush>\n\n            <DrawingBrush x:Key=\"ZoomOutIcon\">\n                <DrawingBrush.Drawing>\n                    <DrawingGroup ClipGeometry=\"M0,0 V16 H16 V0 H0 Z\">\n                        <GeometryDrawing Geometry=\"F1 M16,16z M0,0z M16,16L0,16 0,0 16,0 16,16z\">\n                            <GeometryDrawing.Brush>\n                                <SolidColorBrush Color=\"#FFF6F6F6\"\n                                                 Opacity=\"0\" />\n                            </GeometryDrawing.Brush>\n                        </GeometryDrawing>\n                        <GeometryDrawing Brush=\"#FF252526\"\n                                         Geometry=\"F1 M16,16z M0,0z M8.654,11.548C7.969,11.846 7.237,12 6.5,12 3.467,12 1,9.532 1,6.5 1,3.467 3.467,1 6.5,1 9.532,1 12,3.467 12,6.5 12,7.225 11.849,7.948 11.559,8.625L16,13.066 16,13.433 13.437,16 13.107,16 8.654,11.548z\" />\n                        <GeometryDrawing Brush=\"#FFC5C5C5\"\n                                         Geometry=\"F1 M16,16z M0,0z M14.769,13.25L10.338,8.819C10.75,8.14 11,7.352 11,6.5 11,4.015 8.985,2 6.5,2 4.015,2 2,4.015 2,6.5 2,8.985 4.015,11 6.5,11 7.362,11 8.16,10.745 8.844,10.325L13.271,14.75 14.769,13.25z M3,6.5C3,4.567 4.567,3 6.5,3 8.433,3 10,4.567 10,6.5 10,8.433 8.433,10 6.5,10 4.567,10 3,8.433 3,6.5z\" />\n                        <GeometryDrawing Brush=\"#FF2A292C\"\n                                         Geometry=\"F1 M16,16z M0,0z M6.5,3C4.567,3 3,4.567 3,6.5 3,8.433 4.567,10 6.5,10 8.433,10 10,8.433 10,6.5 10,4.567 8.433,3 6.5,3z M4,7L4,6 9,6 9,7 4,7z\" />\n                        <GeometryDrawing Brush=\"#FF75BEFF\"\n                                         Geometry=\"F1 M16,16z M0,0z M9,7L4,7 4,6 9,6 9,7z\" />\n                    </DrawingGroup>\n                </DrawingBrush.Drawing>\n            </DrawingBrush>\n\n            <DrawingBrush  x:Key=\"ZoomInIcon\">\n                <DrawingBrush.Drawing>\n                    <DrawingGroup ClipGeometry=\"M0,0 V16 H16 V0 H0 Z\">\n                        <GeometryDrawing Geometry=\"F1 M16,16z M0,0z M16,16L0,16 0,0 16,0 16,16z\">\n                            <GeometryDrawing.Brush>\n                                <SolidColorBrush Color=\"#FFF6F6F6\"\n                                                 Opacity=\"0\" />\n                            </GeometryDrawing.Brush>\n                        </GeometryDrawing>\n                        <GeometryDrawing Brush=\"#FF252526\"\n                                         Geometry=\"F1 M16,16z M0,0z M8.654,11.548C7.969,11.846 7.237,12 6.5,12 3.467,12 1,9.532 1,6.5 1,3.467 3.467,1 6.5,1 9.532,1 12,3.467 12,6.5 12,7.225 11.849,7.948 11.559,8.625L16,13.066 16,13.433 13.437,16 13.107,16 8.654,11.548z\" />\n                        <GeometryDrawing Brush=\"#FFC5C5C5\"\n                                         Geometry=\"F1 M16,16z M0,0z M14.769,13.25L10.338,8.819C10.75,8.14 11,7.352 11,6.5 11,4.015 8.985,2 6.5,2 4.015,2 2,4.015 2,6.5 2,8.985 4.015,11 6.5,11 7.362,11 8.16,10.745 8.844,10.325L13.271,14.75 14.769,13.25z M3,6.5C3,4.567 4.567,3 6.5,3 8.433,3 10,4.567 10,6.5 10,8.433 8.433,10 6.5,10 4.567,10 3,8.433 3,6.5z\" />\n                        <GeometryDrawing Brush=\"#FF2A292C\"\n                                         Geometry=\"F1 M16,16z M0,0z M6.5,3C4.567,3 3,4.567 3,6.5 3,8.433 4.567,10 6.5,10 8.433,10 10,8.433 10,6.5 10,4.567 8.433,3 6.5,3z M7,7L7,9 6,9 6,7 4,7 4,6 6,6 6,4 7,4 7,6 9,6 9,7 7,7z\" />\n                        <GeometryDrawing Brush=\"#FF75BEFF\"\n                                         Geometry=\"F1 M16,16z M0,0z M9,6L9,7 7,7 7,9 6,9 6,7 4,7 4,6 6,6 6,4 7,4 7,6 9,6z\" />\n                    </DrawingGroup>\n                </DrawingBrush.Drawing>\n            </DrawingBrush>\n\n            <DrawingBrush x:Key=\"StopIcon\">\n                <DrawingBrush.Drawing>\n                    <DrawingGroup>\n                        <DrawingGroup.Children>\n                            <GeometryDrawing Brush=\"#00FFFFFF\"\n                                             Geometry=\"F1M16,16L0,16 0,0 16,0z\" />\n                            <GeometryDrawing Brush=\"#F38B76\"\n                                             Geometry=\"F1M13,13L3,13 3,3 13,3z\" />\n                            <GeometryDrawing Brush=\"#F38B76\"\n                                             Geometry=\"F1M12,12L4,12 4,4 12,4z\" />\n                        </DrawingGroup.Children>\n                    </DrawingGroup>\n                </DrawingBrush.Drawing>\n            </DrawingBrush>\n\n            <DrawingBrush x:Key=\"RunIcon\">\n                <DrawingBrush.Drawing>\n                    <DrawingGroup>\n                        <DrawingGroup.Children>\n                            <GeometryDrawing Brush=\"#00FFFFFF\"\n                                             Geometry=\"F1M16,16L0,16 0,0 16,0z\" />\n                            <GeometryDrawing Brush=\"#8DD28A\"\n                                             Geometry=\"F1M4,2L4,14 12,8z\" />\n                        </DrawingGroup.Children>\n                    </DrawingGroup>\n                </DrawingBrush.Drawing>\n            </DrawingBrush>\n\n            <DrawingBrush x:Key=\"AddStateIcon\">\n                <DrawingBrush.Drawing>\n                    <DrawingGroup>\n                        <DrawingGroup.Children>\n                            <GeometryDrawing Brush=\"#00FFFFFF\"\n                                             Geometry=\"F1M16,16L0,16 0,0 16,0z\" />\n                            <GeometryDrawing Brush=\"#FFF6F6F6\"\n                                             Geometry=\"F1M1.9998,-0.000199999999999534L1.9998,1.9998 -0.000199999999999978,1.9998 -0.000199999999999978,5.9998 1.9998,5.9998 1.9998,13.4148 3.5858,14.9998 5.4148,14.9998 6.9998,13.4148 6.9998,12.0008 14.4148,12.0008 15.9998,10.4138 15.9998,6.5858 14.4148,4.9998 7.9998,4.9998 7.9998,1.9998 6.0008,1.9998 6.0008,-0.000199999999999534z\" />\n                            <GeometryDrawing Brush=\"#FF424242\"\n                                             Geometry=\"F1M15,7L15,10 14,11 6,11 6,13 5,14 4,14 3,13 3,8 4,8 4,13 5,13 5,10 14,10 14,7 6,7 6,6 14,6z\" />\n                            <GeometryDrawing Brush=\"#FFF0EFF1\"\n                                             Geometry=\"F1M14,7L14,10 5,10 5,13 4,13 4,8 6,8 6,7z\" />\n                            <GeometryDrawing Brush=\"#FF388A34\"\n                                             Geometry=\"F1M3,5L1,5 1,3 3,3 3,1 5,1 5,3 7,3 7,5 5,5 5,7 3,7z\" />\n                        </DrawingGroup.Children>\n                    </DrawingGroup>\n                </DrawingBrush.Drawing>\n            </DrawingBrush>\n\n            <DrawingBrush x:Key=\"RenameIcon\">\n                <DrawingBrush.Drawing>\n                    <DrawingGroup>\n                        <DrawingGroup.Children>\n                            <GeometryDrawing Brush=\"#00FFFFFF\"\n                                             Geometry=\"F1M16,16L0,16 0,0 16,0z\" />\n                            <GeometryDrawing Brush=\"#FFF6F6F6\"\n                                             Geometry=\"F1M0,0.999700000000001L0,14.0007 8,14.0007 8,13.0007 16,13.0007 16,4.0007 8,4.0007 8,0.999700000000001z\" />\n                            <GeometryDrawing Brush=\"#FF414141\"\n                                             Geometry=\"F1M1,13L3,13 3,11 1,11z\" />\n                            <GeometryDrawing Brush=\"#FF414141\"\n                                             Geometry=\"F1M3,11L5,11 5,4 3,4z\" />\n                            <GeometryDrawing Brush=\"#FF414141\"\n                                             Geometry=\"F1M7,11L5,11 5,13 7,13z\" />\n                            <GeometryDrawing Brush=\"#FF414141\"\n                                             Geometry=\"F1M1,4L3,4 3,2 1,2z\" />\n                            <GeometryDrawing Brush=\"#FF414141\"\n                                             Geometry=\"F1M1,6L2,6 2,5 1,5z\" />\n                            <GeometryDrawing Brush=\"#FF414141\"\n                                             Geometry=\"F1M1,8L2,8 2,7 1,7z\" />\n                            <GeometryDrawing Brush=\"#FF414141\"\n                                             Geometry=\"F1M6,6L7,6 7,5 6,5z\" />\n                            <GeometryDrawing Brush=\"#FF414141\"\n                                             Geometry=\"F1M5,4L7,4 7,2 5,2z\" />\n                            <GeometryDrawing Brush=\"#FF414141\"\n                                             Geometry=\"F1M1,10L2,10 2,9 1,9z\" />\n                            <GeometryDrawing Brush=\"#FF414141\"\n                                             Geometry=\"F1M8,6L9,6 9,5 8,5z\" />\n                            <GeometryDrawing Brush=\"#FF414141\"\n                                             Geometry=\"F1M14,10L15,10 15,9 14,9z\" />\n                            <GeometryDrawing Brush=\"#FF414141\"\n                                             Geometry=\"F1M14,8L15,8 15,7 14,7z\" />\n                            <GeometryDrawing Brush=\"#FF414141\"\n                                             Geometry=\"F1M14,12L15,12 15,10.999 14,10.999z\" />\n                            <GeometryDrawing Brush=\"#FF414141\"\n                                             Geometry=\"F1M14,6L15,6 15,5 14,5z\" />\n                            <GeometryDrawing Brush=\"#FF414141\"\n                                             Geometry=\"F1M12,12L13,12 13,10.999 12,10.999z\" />\n                            <GeometryDrawing Brush=\"#FF414141\"\n                                             Geometry=\"F1M12,6L13,6 13,5 12,5z\" />\n                            <GeometryDrawing Brush=\"#FF414141\"\n                                             Geometry=\"F1M10,12L11,12 11,10.999 10,10.999z\" />\n                            <GeometryDrawing Brush=\"#FF414141\"\n                                             Geometry=\"F1M10,6L11,6 11,5 10,5z\" />\n                            <GeometryDrawing Brush=\"#FF414141\"\n                                             Geometry=\"F1M8,12L9,12 9,10.999 8,10.999z\" />\n                        </DrawingGroup.Children>\n                    </DrawingGroup>\n                </DrawingBrush.Drawing>\n            </DrawingBrush>\n\n            <DrawingBrush x:Key=\"DeleteIcon\">\n                <DrawingBrush.Drawing>\n                    <DrawingGroup ClipGeometry=\"M0,0 V511.992 H511.992 V0 H0 Z\">\n                        <DrawingGroup.Transform>\n                            <TranslateTransform X=\"3.5762786865234375E-07\"\n                                                Y=\"3.5762786865234375E-07\" />\n                        </DrawingGroup.Transform>\n                        <GeometryDrawing Brush=\"#FFE76E54\"\n                                         Geometry=\"F1 M511.992,511.992z M0,0z M415.402344,495.421875L255.996094,336.011719 96.589844,495.421875C74.492188,517.515625 38.667969,517.515625 16.570312,495.421875 -5.52343799999997,473.324219 -5.52343799999997,437.5 16.570312,415.402344L175.980469,255.996094 16.570312,96.589844C-5.52343799999997,74.492188 -5.52343799999997,38.667969 16.570312,16.570312 38.667969,-5.52343800000003 74.492188,-5.52343800000003 96.589844,16.570312L255.996094,175.980469 415.402344,16.570312C437.5,-5.52343800000003 473.324219,-5.52343800000003 495.421875,16.570312 517.515625,38.667969 517.515625,74.492188 495.421875,96.589844L336.011719,255.996094 495.421875,415.402344C517.515625,437.5 517.515625,473.324219 495.421875,495.421875 473.324219,517.515625 437.5,517.515625 415.402344,495.421875z M415.402344,495.421875\" />\n                    </DrawingGroup>\n                </DrawingBrush.Drawing>\n            </DrawingBrush>\n\n            <DrawingBrush x:Key=\"DisconnectIcon\">\n                <DrawingBrush.Drawing>\n                    <DrawingGroup>\n                        <DrawingGroup.Children>\n                            <GeometryDrawing Brush=\"#00FFFFFF\"\n                                             Geometry=\"F1M16,16L0,16 0,0 16,0z\" />\n                            <GeometryDrawing Brush=\"#FFF6F6F6\"\n                                             Geometry=\"F1M7.0002,-0.000199999999999534L7.0002,0.6468 6.3532,-0.000199999999999534 5.5252,-0.000199999999999534 3.9702,1.5558 2.4142,-0.000199999999999534 1.5862,-0.000199999999999534 0.000200000000000422,1.5858 0.000200000000000422,2.5348 1.4952,4.0298 0.000200000000000422,5.5258 0.000200000000000422,6.4748 1.5252,7.9998 1.0002,7.9998 1.0002,15.9998 10.0002,15.9998 10.0002,13.9998 14.0002,13.9998 14.0002,7.9998 16.0002,7.9998 16.0002,-0.000199999999999534z M6.4442,4.0298L7.0002,3.4748 7.0002,4.5858z M3.9702,6.5048L5.4652,7.9998 2.4752,7.9998z M7.0002,7.4138L7.0002,7.9998 6.4142,7.9998z M10.0002,7.9998L11.0002,7.9998 11.0002,10.9998 10.0002,10.9998z\" />\n                            <GeometryDrawing Brush=\"#FF424242\"\n                                             Geometry=\"F1M14,6L9,6 9,3 14,3z M8,14L3,14 3,11 8,11z M8,1L8,7 12,7 12,12 9,12 9,9 2,9 2,15 9,15 9,13 13,13 13,7 15,7 15,1z\" />\n                            <GeometryDrawing Brush=\"#FFEFEFF0\"\n                                             Geometry=\"F1M9,6L14,6 14,3 9,3z M3,11L8,11 8,14 3,14z\" />\n                            <GeometryDrawing Brush=\"#FFA1260D\"\n                                             Geometry=\"F1M3.9699,2.9698L1.9999,0.999799999999999 0.9399,2.0608 2.9089,4.0298 0.9399,5.9998 1.9999,7.0598 3.9699,5.0908 5.9389,7.0598 6.9999,5.9998 5.0299,4.0298 6.9999,2.0608 5.9389,0.999799999999999z\" />\n                        </DrawingGroup.Children>\n                    </DrawingGroup>\n                </DrawingBrush.Drawing>\n            </DrawingBrush>\n\n            <DrawingBrush x:Key=\"SelectAllIcon\">\n                <DrawingBrush.Drawing>\n                    <DrawingGroup>\n                        <DrawingGroup.Children>\n                            <GeometryDrawing Brush=\"#00FFFFFF\"\n                                             Geometry=\"F1M16,16L0,16 0,0 16,0z\" />\n                            <GeometryDrawing Brush=\"#FFF6F6F6\"\n                                             Geometry=\"F1M0,-0.000199999999999534L0,13.0008 6,13.0008 6,14.9998 7.649,14.9998 8.337,14.2448 9.115,15.9998 9.983,15.9998 12.269,14.9598 11.406,13.0008 15,13.0008 15,-0.000199999999999534z\" />\n                            <GeometryDrawing Brush=\"#FF414141\"\n                                             Geometry=\"F1M8.4141,7L9.4141,8 11.0001,8 11.0001,7z\" />\n                            <GeometryDrawing Brush=\"#FF414141\"\n                                             Geometry=\"F1M1,12L2,12 2,10.999 1,10.999z\" />\n                            <GeometryDrawing Brush=\"#FF414141\"\n                                             Geometry=\"F1M1,2L2,2 2,1 1,1z\" />\n                            <GeometryDrawing Brush=\"#FF414141\"\n                                             Geometry=\"F1M1,10L2,10 2,9 1,9z\" />\n                            <GeometryDrawing Brush=\"#FF414141\"\n                                             Geometry=\"F1M1,6L2,6 2,5 1,5z\" />\n                            <GeometryDrawing Brush=\"#FF414141\"\n                                             Geometry=\"F1M1,4L2,4 2,3 1,3z\" />\n                            <GeometryDrawing Brush=\"#FF414141\"\n                                             Geometry=\"F1M1,8L2,8 2,7 1,7z\" />\n                            <GeometryDrawing Brush=\"#FF414141\"\n                                             Geometry=\"F1M9,2L10,2 10,1 9,1z\" />\n                            <GeometryDrawing Brush=\"#FF414141\"\n                                             Geometry=\"F1M7,2L8,2 8,1 7,1z\" />\n                            <GeometryDrawing Brush=\"#FF414141\"\n                                             Geometry=\"F1M5,2L6,2 6,1 5,1z\" />\n                            <GeometryDrawing Brush=\"#FF414141\"\n                                             Geometry=\"F1M3,2L4,2 4,1 3,1z\" />\n                            <GeometryDrawing Brush=\"#FF414141\"\n                                             Geometry=\"F1M3,6L10,6 10,5 3,5z\" />\n                            <GeometryDrawing Brush=\"#FF414141\"\n                                             Geometry=\"F1M13,4L14.001,4 14.001,3 13,3z\" />\n                            <GeometryDrawing Brush=\"#FF414141\"\n                                             Geometry=\"F1M13,10L14.001,10 14.001,9 13,9z\" />\n                            <GeometryDrawing Brush=\"#FF414141\"\n                                             Geometry=\"F1M3,12L4,12 4,10.999 3,10.999z\" />\n                            <GeometryDrawing Brush=\"#FF414141\"\n                                             Geometry=\"F1M12.9996,10.9996L12.9996,11.5856 13.4146,12.0006 14.0006,12.0006 14.0006,10.9996z\" />\n                            <GeometryDrawing Brush=\"#FF414141\"\n                                             Geometry=\"F1M13,6L14.001,6 14.001,5 13,5z\" />\n                            <GeometryDrawing Brush=\"#FF414141\"\n                                             Geometry=\"F1M13,2L14.001,2 14.001,1 13,1z\" />\n                            <GeometryDrawing Brush=\"#FF414141\"\n                                             Geometry=\"F1M13,8L14.001,8 14.001,7 13,7z\" />\n                            <GeometryDrawing Brush=\"#FF414141\"\n                                             Geometry=\"F1M11,2L12,2 12,1 11,1z\" />\n                            <GeometryDrawing Brush=\"#FF414141\"\n                                             Geometry=\"F1M3,8L6.001,8 6.001,7 3,7z\" />\n                            <GeometryDrawing Brush=\"#FF414141\"\n                                             Geometry=\"F1M5,12L6,12 6,10.999 5,10.999z\" />\n                            <GeometryDrawing Brush=\"#FF414141\"\n                                             Geometry=\"F1M3,4L12,4 12,3 3,3z\" />\n                            <GeometryDrawing Brush=\"#FF414141\"\n                                             Geometry=\"F1M10.9551,14.459L9.8691,11.994 12.3871,11.994 7.0001,6.996 7.0001,14.228 8.6291,12.438 9.7661,15z\" />\n                        </DrawingGroup.Children>\n                    </DrawingGroup>\n                </DrawingBrush.Drawing>\n            </DrawingBrush>\n\n    <PathGeometry x:Key=\"CloseGeometry\"\n                     Figures=\"M9.15625 6.3125L6.3125 9.15625L22.15625 25L6.21875 40.96875L9.03125 43.78125L25 27.84375L40.9375 43.78125L43.78125 40.9375L27.84375 25L43.6875 9.15625L40.84375 6.3125L25 22.15625Z\"/>\n    \n    <PathGeometry x:Key=\"AddGeometry\"\n                     Figures=\"M15 5L15 15L5 15L5 17L15 17L15 27L17 27L17 17L27 17L27 15L17 15L17 5Z\"/>\n</ResourceDictionary>"
  },
  {
    "path": "Examples/Nodify.Shared/Themes/Light.xaml",
    "content": "﻿<ResourceDictionary xmlns=\"https://github.com/avaloniaui\"\n                    xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\">\n\n    <ResourceDictionary.MergedDictionaries>\n        <ResourceInclude Source=\"Brushes.xaml\" />\n        <ResourceInclude Source=\"Controls.xaml\" />\n    </ResourceDictionary.MergedDictionaries>\n\n    <Color x:Key=\"ForegroundColor\">Black</Color>\n    <Color x:Key=\"DisabledForegroundColor\">Gray</Color>\n\n    <Color x:Key=\"BackgroundColor\">#D4DCF2</Color>\n    <Color x:Key=\"ContrastBackgroundColor\">#CCD6ED</Color>\n    <Color x:Key=\"HighlightedBackgroundColor\">#B1C5FF</Color>\n\n    <Color x:Key=\"BorderColor\">#CBCCDF</Color>\n    <Color x:Key=\"DisabledBorderColor\">Gray</Color>\n    <Color x:Key=\"HighlightedBorderColor\">#7EB4EA</Color>\n    <Color x:Key=\"FocusedBorderColor\">#569DE5</Color>\n\n    <Color x:Key=\"GridLinesColor\">#7EB4EA</Color>\n\n</ResourceDictionary>"
  },
  {
    "path": "Examples/Nodify.Shared/Themes/Nodify.xaml",
    "content": "﻿<ResourceDictionary xmlns=\"https://github.com/avaloniaui\"\n                    xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\">\n    \n    <ResourceDictionary.MergedDictionaries>\n        <ResourceInclude Source=\"Brushes.xaml\" />\n        <ResourceInclude Source=\"Controls.xaml\" />\n    </ResourceDictionary.MergedDictionaries>\n    \n    <Color x:Key=\"ForegroundColor\">White</Color>\n    <Color x:Key=\"DisabledForegroundColor\">Gray</Color>\n\n    <Color x:Key=\"BackgroundColor\">#4C3180</Color>\n    <Color x:Key=\"ContrastBackgroundColor\">#451E63</Color>\n    <Color x:Key=\"HighlightedBackgroundColor\">#662D91</Color>\n\n    <Color x:Key=\"BorderColor\">#662D91</Color>\n    <Color x:Key=\"DisabledBorderColor\">Gray</Color>\n    <Color x:Key=\"HighlightedBorderColor\">#7EB4EA</Color>\n    <Color x:Key=\"FocusedBorderColor\">#569DE5</Color>\n    \n    <Color x:Key=\"GridLinesColor\">#4C3180</Color>\n    \n</ResourceDictionary>"
  },
  {
    "path": "Examples/Nodify.Shared/UndoRedo/ActionsHistory.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.ComponentModel;\n\nnamespace Nodify.UndoRedo\n{\n    public interface IActionsHistory : INotifyPropertyChanged\n    {\n        int MaxSize { get; set; }\n        bool CanUndo { get; }\n        bool CanRedo { get; }\n        bool IsEnabled { get; set; }\n        IAction? Current { get; }\n\n        void Undo();\n        void Redo();\n\n        void Clear();\n\n        /// <summary>\n        /// All future modifications will be merged together to create a single history item until batch is disposed.\n        /// </summary>\n        IDisposable Batch(string? label = default);\n\n        void Record(IAction action);\n\n        /// <summary>\n        /// All future modifications will be merged together to create a single history item until history is resumed.\n        /// </summary>\n        void Pause(string? label = default);\n\n        /// <summary>Each future modifications will create a new history item.</summary>\n        void Resume();\n    }\n\n    public interface IAction\n    {\n        string? Label { get; }\n\n        void Execute();\n        void Undo();\n    }\n\n    public static class ActionsHistoryExtensions\n    {\n        public static void Record(this IActionsHistory history, Action execute, Action unexecute, string? label = default)\n            => history.Record(new DelegateAction(execute, unexecute, label));\n\n        public static void ExecuteAction(this IActionsHistory history, IAction action)\n        {\n            history.Record(action);\n            action.Execute();\n        }\n    }\n\n    public class ActionsHistory : IActionsHistory\n    {\n        private readonly List<IAction> _history = new List<IAction>();\n        private readonly List<IAction> _batchHistory = new List<IAction>();\n        private int _position = -1;\n        private bool _isApplyingOperation = false;\n        private string? _batchLabel;\n        private int _batchDepth;\n\n        private static readonly PropertyChangedEventArgs _canRedoArgs = new PropertyChangedEventArgs(nameof(CanRedo));\n        private static readonly PropertyChangedEventArgs _canUndoArgs = new PropertyChangedEventArgs(nameof(CanUndo));\n\n        public event PropertyChangedEventHandler? PropertyChanged;\n        public static readonly ActionsHistory Global = new ActionsHistory();\n\n        public bool IsBatching { get; private set; }\n\n        public int MaxSize { get; set; } = 50;\n\n        public bool CanRedo => _history.Count > 0 && _position < _history.Count - 1;\n\n        public bool CanUndo => _position > -1;\n\n        public bool IsEnabled { get; set; } = true;\n\n        public IAction? Current => CanUndo ? _history[_position] : null;\n\n        public IDisposable Batch(string? label = default)\n            => new BatchOperation(label, this);\n\n        public void Record(IAction op)\n        {\n            // Prevent recording the undo or redo operation\n            if (_isApplyingOperation || !IsEnabled)\n            {\n                return;\n            }\n\n            if (IsBatching)\n            {\n                _batchHistory.Add(op);\n            }\n            else\n            {\n                AddToUndoStack(op);\n            }\n        }\n\n        private void AddToUndoStack(IAction op)\n        {\n            if (_position < _history.Count - 1)\n            {\n                _history.RemoveRange(_position + 1, _history.Count - _position - 1);\n            }\n\n            if (_history.Count >= MaxSize)\n            {\n                _history.RemoveAt(0);\n                _position--;\n            }\n\n            _history.Add(op);\n            _position++;\n\n            PropertyChanged?.Invoke(this, _canRedoArgs);\n            PropertyChanged?.Invoke(this, _canUndoArgs);\n        }\n\n        public void Undo()\n        {\n            if (IsBatching)\n            {\n                throw new InvalidOperationException($\"{nameof(Undo)} is not allowed during a batch.\");\n            }\n\n            if (CanUndo)\n            {\n                var op = _history[_position];\n                _isApplyingOperation = true;\n                op.Undo();\n                _isApplyingOperation = false;\n                _position--;\n            }\n        }\n\n        public void Redo()\n        {\n            if (IsBatching)\n            {\n                throw new InvalidOperationException($\"{nameof(Redo)} is not allowed during a batch.\");\n            }\n\n            if (CanRedo)\n            {\n                _position++;\n                var op = _history[_position];\n                _isApplyingOperation = true;\n                op.Execute();\n                _isApplyingOperation = false;\n            }\n        }\n\n        public void Clear()\n        {\n            _history.Clear();\n            _batchHistory.Clear();\n        }\n\n        public void Pause(string? label = default)\n        {\n            if (_batchDepth > 0)\n            {\n                return;\n            }\n\n            _batchLabel = label;\n            IsBatching = true;\n        }\n\n        public void Resume()\n        {\n            if (_batchDepth > 0)\n            {\n                return;\n            }\n\n            if (_batchHistory.Count > 0)\n            {\n                AddToUndoStack(new BatchAction(_batchLabel, _batchHistory));\n                _batchHistory.Clear();\n            }\n\n            _batchLabel = null;\n            IsBatching = false;\n        }\n\n        private class BatchOperation : IDisposable\n        {\n            private readonly ActionsHistory _history;\n            private bool _disposed;\n\n            public BatchOperation(string? label, ActionsHistory history)\n            {\n                _history = history;\n                _history.Pause(label);\n                _history._batchDepth++;\n            }\n\n            public void Dispose()\n            {\n                if (!_disposed)\n                {\n                    _disposed = true;\n                    _history._batchDepth--;\n                    _history.Resume();\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.Shared/UndoRedo/BatchAction.cs",
    "content": "﻿using System.Collections.Generic;\nusing System.Linq;\n\nnamespace Nodify.UndoRedo\n{\n    public class BatchAction : IAction\n    {\n        public BatchAction(string? label, IEnumerable<IAction> history)\n        {\n            History = history.Reverse().ToList();\n            Label = label;\n        }\n\n        public IReadOnlyList<IAction> History { get; }\n\n        public string? Label { get; }\n\n        public void Execute()\n        {\n            for (int i = History.Count - 1; i >= 0; i--)\n            {\n                History[i].Execute();\n            }\n        }\n\n        public void Undo()\n        {\n            for (int i = 0; i < History.Count; i++)\n            {\n                History[i].Undo();\n            }\n        }\n\n        public override string? ToString()\n            => Label;\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.Shared/UndoRedo/DelegateAction.cs",
    "content": "﻿using System;\n\nnamespace Nodify.UndoRedo\n{\n    public class DelegateAction : IAction\n    {\n        private readonly Action _execute;\n        private readonly Action _undo;\n\n        public string? Label { get; }\n\n        public DelegateAction(Action apply, Action unapply, string? label)\n        {\n            _execute = apply;\n            _undo = unapply;\n            Label = label;\n        }\n\n        public void Execute() => _execute();\n        public void Undo() => _undo();\n\n        public override string? ToString()\n            => Label;\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.Shared/UndoRedo/PropertyCache.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Reflection;\n\nnamespace Nodify.UndoRedo\n{\n    public interface IPropertyAccessor\n    {\n        object? GetValue(object instance);\n        void SetValue(object instance, object? value);\n        bool CanRead { get; }\n        bool CanWrite { get; }\n    }\n\n    public sealed class PropertyAccessor<TInstanceType, TPropertyType> : IPropertyAccessor where TInstanceType : class\n    {\n        private readonly Func<TInstanceType, TPropertyType> _getter;\n        private readonly Action<TInstanceType, TPropertyType> _setter;\n\n        public bool CanRead { get; }\n        public bool CanWrite { get; }\n\n        public PropertyAccessor(Func<TInstanceType, TPropertyType> getter, Action<TInstanceType, TPropertyType> setter)\n        {\n            _getter = getter;\n            _setter = setter;\n\n            CanRead = getter != null;\n            CanWrite = setter != null;\n        }\n\n        public object? GetValue(object instance)\n            => _getter((TInstanceType)instance);\n\n        public void SetValue(object instance, object? value)\n            => _setter((TInstanceType)instance, (TPropertyType)value!);\n    }\n\n    public class PropertyCache\n    {\n        private static readonly Dictionary<string, IPropertyAccessor> _properties = new Dictionary<string, IPropertyAccessor>();\n\n        public static IPropertyAccessor Get(Type type, string name)\n        {\n            string propKey = $\"{type.FullName}.{name}\";\n            if (!_properties.TryGetValue(propKey, out var result))\n            {\n                var prop = type.GetProperty(name);\n                result = Create(type, prop!);\n\n                _properties.Add(propKey, result);\n            }\n\n            return result;\n        }\n\n        public static IPropertyAccessor Get<T>(string name)\n            => Get(typeof(T), name);\n\n        private static IPropertyAccessor Create(Type type, PropertyInfo property)\n        {\n            Delegate? getterInvocation = default;\n            Delegate? setterInvocation = default;\n\n            if (property.CanRead)\n            {\n                MethodInfo getMethod = property.GetGetMethod(true)!;\n                Type getterType = typeof(Func<,>).MakeGenericType(type, property.PropertyType);\n                getterInvocation = Delegate.CreateDelegate(getterType, getMethod);\n            }\n\n            if (property.CanWrite)\n            {\n                MethodInfo setMethod = property.GetSetMethod(true)!;\n                Type setterType = typeof(Action<,>).MakeGenericType(type, property.PropertyType);\n                setterInvocation = Delegate.CreateDelegate(setterType, setMethod);\n            }\n\n            Type adapterType = typeof(PropertyAccessor<,>).MakeGenericType(type, property.PropertyType);\n\n            return (IPropertyAccessor)Activator.CreateInstance(adapterType, getterInvocation, setterInvocation)!;\n        }\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.Shared/UndoRedo/Undoable.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq.Expressions;\nusing System.Runtime.CompilerServices;\nusing Expression = System.Linq.Expressions.Expression;\n\nnamespace Nodify.UndoRedo\n{\n    [Flags]\n    public enum PropertyFlags\n    {\n        Disable = 0,\n        Enable = 1\n    }\n\n    public abstract class Undoable : ObservableObject\n    {\n        private readonly HashSet<string> _trackedProperties = new HashSet<string>();\n\n        public IActionsHistory History { get; }\n\n        private void RecordHistory<TPropType>(string propName, TPropType previous, TPropType current)\n        {\n            if (_trackedProperties.Contains(propName))\n            {\n                var prop = PropertyCache.Get(GetType(), propName);\n                History.Record(() => prop.SetValue(this, current), () => prop.SetValue(this, previous), propName);\n            }\n        }\n\n        protected void RecordProperty(string propName, PropertyFlags flags = PropertyFlags.Enable)\n        {\n            if (flags == PropertyFlags.Disable)\n            {\n                _trackedProperties.Remove(propName);\n            }\n            else if (flags.HasFlag(PropertyFlags.Enable))\n            {\n                _trackedProperties.Add(propName);\n            }\n        }\n\n        protected void RecordProperty<TType>(Expression<Func<TType, object?>> selector, PropertyFlags flags = PropertyFlags.Enable)\n        {\n            if (!RuntimeFeature.IsDynamicCodeSupported)\n                return;\n\n            string name = GetPropertyName(selector);\n            RecordProperty(name, flags);\n        }\n\n        private static string GetPropertyName(Expression memberAccess)\n            => memberAccess switch\n            {\n                LambdaExpression lambda => GetPropertyName(lambda.Body),\n                MemberExpression mbr => mbr.Member.Name,\n                UnaryExpression unary => GetPropertyName(unary.Operand),\n                _ => throw new Exception($\"Member name could not be extracted from {memberAccess}.\")\n            };\n\n        protected override bool SetProperty<TPropType>(ref TPropType field, TPropType value, [CallerMemberName] string propertyName = \"\")\n        {\n            TPropType prev = field;\n            if (base.SetProperty(ref field, value, propertyName))\n            {\n                RecordHistory(propertyName, prev, value);\n                return true;\n            }\n\n            return false;\n        }\n\n        public Undoable()\n        {\n            History = ActionsHistory.Global;\n        }\n\n        public Undoable(IActionsHistory history)\n        {\n            History = history;\n        }\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.StateMachine/App.xaml",
    "content": "﻿<Application x:Class=\"Nodify.StateMachine.App\"\n             xmlns=\"https://github.com/avaloniaui\"\n             xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n             RequestedThemeVariant=\"Light\">\n    <Application.Resources>\n        <ResourceDictionary>\n            <ResourceDictionary.MergedDictionaries>\n                <ResourceInclude Source=\"avares://Nodify/Themes/Generic.xaml\" />\n                <ResourceInclude Source=\"avares://Nodify/Themes/Light.xaml\" />\n                <ResourceInclude Source=\"avares://Nodify.Shared/Themes/Icons.xaml\" />\n                <ResourceInclude Source=\"avares://Nodify.Shared/Themes/Light.xaml\" />\n                <ResourceInclude Source=\"avares://Nodify.Shared/Themes/Generic.xaml\" />\n                <ResourceInclude Source=\"Themes/Light.xaml\" />\n            </ResourceDictionary.MergedDictionaries>\n        </ResourceDictionary>\n    </Application.Resources>\n    \n    <Application.Styles>\n        <FluentTheme DensityStyle=\"Compact\"/>\n    </Application.Styles>\n    \n    <Application.DataTemplates>\n        <DataTemplate x:DataType=\"DrawingBrush\">\n            <Rectangle Fill=\"{Binding .}\" Width=\"16\" Height=\"16\" />\n        </DataTemplate>\n    </Application.DataTemplates>\n</Application>"
  },
  {
    "path": "Examples/Nodify.StateMachine/App.xaml.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Configuration;\nusing System.Data;\nusing System.Linq;\nusing System.Threading.Tasks;\nusing System.Windows;\n\nnamespace Nodify.StateMachine\n{\n    /// <summary>\n    /// Interaction logic for App.xaml\n    /// </summary>\n    public partial class App : Application\n    {\n        public override void Initialize()\n        {\n            AvaloniaXamlLoader.Load(this);\n        }\n\n        public override void OnFrameworkInitializationCompleted()\n        {\n            if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)\n            {\n                desktop.MainWindow = new MainWindow();\n            }\n\n            base.OnFrameworkInitializationCompleted();\n        }\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.StateMachine/BlackboardItemReferenceViewModel.cs",
    "content": "﻿using System;\n\nnamespace Nodify.StateMachine\n{\n    // Condition or Action reference\n    public class BlackboardItemReferenceViewModel\n    {\n        public string? Name { get; set; }\n        public Type? Type { get; set; }\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.StateMachine/BlackboardItemViewModel.cs",
    "content": "﻿using System;\n\nnamespace Nodify.StateMachine\n{\n    public class BlackboardItemViewModel : ObservableObject\n    {\n        private string? _name;\n        public string? Name\n        {\n            get => _name;\n            set => SetProperty(ref _name, value);\n        }\n\n        private Type? _type;\n        public Type? Type\n        {\n            get => _type;\n            set => SetProperty(ref _type, value);\n        }\n\n        private NodifyObservableCollection<BlackboardKeyViewModel> _input = new NodifyObservableCollection<BlackboardKeyViewModel>();\n        public NodifyObservableCollection<BlackboardKeyViewModel> Input\n        {\n            get => _input;\n            set\n            {\n                if (value == null)\n                {\n                    value = new NodifyObservableCollection<BlackboardKeyViewModel>();\n                }\n\n                SetProperty(ref _input!, value);\n            }\n        }\n\n        private NodifyObservableCollection<BlackboardKeyViewModel> _output = new NodifyObservableCollection<BlackboardKeyViewModel>();\n        public NodifyObservableCollection<BlackboardKeyViewModel> Output\n        {\n            get => _output;\n            set\n            {\n                if (value == null)\n                {\n                    value = new NodifyObservableCollection<BlackboardKeyViewModel>();\n                }\n\n                SetProperty(ref _output!, value);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.StateMachine/BlackboardKeyEditorView.xaml",
    "content": "﻿<UserControl x:Class=\"Nodify.StateMachine.BlackboardKeyEditorView\"\n             xmlns=\"https://github.com/avaloniaui\"\n             xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n             xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\"\n             xmlns:d=\"http://schemas.microsoft.com/expression/blend/2008\"\n             xmlns:local=\"clr-namespace:Nodify.StateMachine\"\n             xmlns:shared=\"clr-namespace:Nodify;assembly=Nodify.Shared\"\n             mc:Ignorable=\"d\"\n             d:DataContext=\"{d:DesignInstance Type={x:Type local:BlackboardKeyEditorViewModel}, IsDesignTimeCreatable=True}\"\n             d:Background=\"{DynamicResource PanelBackgroundBrush}\"\n             d:DesignWidth=\"400\">\n    <UserControl.Resources>\n        <DataTemplate x:Key=\"BooleanTemplate\"\n                      DataType=\"{x:Type local:BlackboardKeyEditorViewModel}\">\n            <CheckBox IsChecked=\"{Binding Target.Value}\"\n                      HorizontalAlignment=\"Left\"\n                      VerticalAlignment=\"Center\" />\n        </DataTemplate>\n        <DataTemplate x:Key=\"IntegerTemplate\"\n                      DataType=\"{x:Type local:BlackboardKeyEditorViewModel}\">\n            <TextBox Text=\"{CompiledBinding Target.Value, UpdateSourceTrigger=LostFocus}\" />\n        </DataTemplate>\n        <DataTemplate x:Key=\"DoubleTemplate\"\n                      DataType=\"{x:Type local:BlackboardKeyEditorViewModel}\">\n            <TextBox Text=\"{CompiledBinding Target.Value, UpdateSourceTrigger=LostFocus}\" />\n        </DataTemplate>\n        <DataTemplate x:Key=\"StringTemplate\"\n                      DataType=\"{x:Type local:BlackboardKeyEditorViewModel}\">\n            <TextBox Text=\"{CompiledBinding Target.Value, UpdateSourceTrigger=LostFocus}\" />\n        </DataTemplate>\n        <DataTemplate x:Key=\"ObjectTemplate\"\n                      DataType=\"{x:Type local:BlackboardKeyEditorViewModel}\">\n            <TextBox Text=\"{CompiledBinding Target.Value, UpdateSourceTrigger=LostFocus}\"\n                     IsEnabled=\"False\" />\n        </DataTemplate>\n        <DataTemplate x:Key=\"KeyTemplate\"\n                      DataType=\"{x:Type local:BlackboardKeyEditorViewModel}\">\n            <ComboBox SelectedItem=\"{Binding Target.Value}\"\n                      DisplayMemberBinding=\"{Binding Name}\">\n                <ComboBox.ItemsSource>\n                    <MultiBinding Converter=\"{local:FilterBlackboardKeysConverter}\">\n                        <Binding Path=\"AvailableKeys\" />\n                        <Binding Path=\"Target.Type\" />\n                        <!--USED TO NOTIFY OF COLLECTION CHANGED-->\n                        <Binding Path=\"AvailableKeys.Count\" />\n                    </MultiBinding>\n                </ComboBox.ItemsSource>\n            </ComboBox>\n        </DataTemplate>\n    </UserControl.Resources>\n\n    <Grid>\n        <Grid.ColumnDefinitions>\n            <ColumnDefinition Width=\"Auto\"\n                              SharedSizeGroup=\"KeyName\" />\n            <ColumnDefinition Width=\"Auto\"\n                              SharedSizeGroup=\"KeyType\" />\n            <ColumnDefinition />\n        </Grid.ColumnDefinitions>\n\n        <Grid.RowDefinitions>\n            <RowDefinition Height=\"Auto\" />\n        </Grid.RowDefinitions>\n\n        <shared:EditableTextBlock Text=\"{Binding Target.Name}\"\n                                  d:Text=\"My blackboard key\"\n                                  IsEditing=\"{Binding IsEditing}\"\n                                  Foreground=\"{DynamicResource ForegroundBrush}\"\n                                  VerticalAlignment=\"Stretch\"\n                                  VerticalContentAlignment=\"Center\"\n                                  Margin=\"1 1 5 1\" />\n\n        <ComboBox ItemsSource=\"{Binding Target.Type, Converter={shared:EnumValuesConverter}}\"\n                  IsEnabled=\"{Binding CanChangeKeyType}\"\n                  SelectedValue=\"{Binding Target.Type}\"\n                  SelectedValueBinding=\"{Binding Value}\"\n                  DisplayMemberBinding=\"{Binding Name}\"\n                  Grid.Column=\"1\"\n                  Margin=\"0 0 5 0\" />\n\n        <Grid Grid.Column=\"2\">\n            <Grid.ColumnDefinitions>\n                <ColumnDefinition MaxWidth=\"150\" />\n                <ColumnDefinition Width=\"Auto\" />\n            </Grid.ColumnDefinitions>\n\n            <ContentControl Content=\"{Binding}\">\n                <ContentControl.Theme>\n                    <ControlTheme TargetType=\"{x:Type ContentControl}\">\n                        <Setter Property=\"(Interaction.Behaviors)\">\n                            <BehaviorCollectionTemplate>\n                                <BehaviorCollection>\n                                    <DataTrigger Property=\"Target.Type\" Value=\"Boolean\">\n                                        <PropertySetter Property=\"ContentTemplate\" Value=\"{StaticResource BooleanTemplate}\" />\n                                    </DataTrigger>\n                                    <DataTrigger Property=\"Target.Type\" Value=\"Integer\">\n                                        <PropertySetter Property=\"ContentTemplate\" Value=\"{StaticResource IntegerTemplate}\" />\n                                    </DataTrigger>\n\n                                    <DataTrigger Property=\"Target.Type\" Value=\"Double\">\n                                        <PropertySetter Property=\"ContentTemplate\" Value=\"{StaticResource DoubleTemplate}\" />\n                                    </DataTrigger>\n\n                                    <DataTrigger Property=\"Target.Type\" Value=\"String\">\n                                        <PropertySetter Property=\"ContentTemplate\" Value=\"{StaticResource StringTemplate}\" />\n                                    </DataTrigger>\n\n                                    <DataTrigger Property=\"Target.Type\" Value=\"Object\">\n                                        <PropertySetter Property=\"ContentTemplate\" Value=\"{StaticResource ObjectTemplate}\" />\n                                    </DataTrigger>\n\n                                    <!-- there is no Key type, some leftover in Nodify? -->\n                                    <!-- <DataTrigger Binding=\"Target.Type\" Value=\"Key\"> -->\n                                    <!--     <PropertySetter Property=\"ContentTemplate\" Value=\"{StaticResource KeyTemplate}\" /> -->\n                                    <!-- </DataTrigger> -->\n                                    <DataTrigger Property=\"Target.ValueIsKey\" Value=\"True\">\n                                        <PropertySetter Property=\"ContentTemplate\" Value=\"{StaticResource KeyTemplate}\" />\n                                    </DataTrigger>\n                                </BehaviorCollection>\n                            </BehaviorCollectionTemplate>\n                        </Setter>\n                    </ControlTheme>\n                </ContentControl.Theme>\n            </ContentControl>\n\n            <CheckBox IsVisible=\"{Binding CanChangeInputType, Converter={shared:BooleanToVisibilityConverter}}\"\n                      IsChecked=\"{Binding Target.ValueIsKey}\"\n                      ToolTip.Tip=\"Toggle input type\"\n                      Grid.Column=\"1\">\n                <CheckBox.Theme>\n                    <ControlTheme TargetType=\"{x:Type CheckBox}\"\n                           BasedOn=\"{StaticResource IconCheckBox}\">\n                        <Setter Property=\"Content\"\n                                Value=\"{StaticResource DiamondIcon}\" />\n                        <Setter Property=\"(Interaction.Behaviors)\">\n                            <BehaviorCollectionTemplate>\n                                <BehaviorCollection>\n                                    <DataTrigger Property=\"IsChecked\"\n                                                 UseDataContext=\"False\"\n                                                 Value=\"True\">\n                                        <PropertySetter Property=\"Content\"\n                                                        Value=\"{StaticResource DiamondFillIcon}\" />\n                                    </DataTrigger>\n                                </BehaviorCollection>\n                            </BehaviorCollectionTemplate>\n                        </Setter>\n                    </ControlTheme>\n                </CheckBox.Theme>\n            </CheckBox>\n        </Grid>\n    </Grid>\n</UserControl>\n"
  },
  {
    "path": "Examples/Nodify.StateMachine/BlackboardKeyEditorView.xaml.cs",
    "content": "﻿using System.Windows.Controls;\n\nnamespace Nodify.StateMachine\n{\n    public partial class BlackboardKeyEditorView : UserControl\n    {\n        public BlackboardKeyEditorView()\n        {\n            InitializeComponent();\n        }\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.StateMachine/BlackboardKeyEditorViewModel.cs",
    "content": "﻿using System.Collections.Generic;\n\nnamespace Nodify.StateMachine\n{\n    public class BlackboardKeyEditorViewModel : ObservableObject\n    {\n        private ICollection<BlackboardKeyViewModel>? _availableKeys;\n        public ICollection<BlackboardKeyViewModel>? AvailableKeys\n        {\n            get => _availableKeys;\n            set => SetProperty(ref _availableKeys, value);\n        }\n\n        private BlackboardKeyViewModel? _target;\n        public BlackboardKeyViewModel? Target\n        {\n            get => _target;\n            set => SetProperty(ref _target, value);\n        }\n\n        private bool _canChangeInputType;\n        public bool CanChangeInputType\n        {\n            get => _canChangeInputType;\n            set => SetProperty(ref _canChangeInputType, value);\n        }\n        \n        private bool _canChangeKeyType = true;\n        public bool CanChangeKeyType\n        {\n            get => _canChangeKeyType;\n            set => SetProperty(ref _canChangeKeyType, value);\n        }\n\n        private bool _isEditing;\n        public bool IsEditing\n        {\n            get => _isEditing;\n            set => SetProperty(ref _isEditing, value);\n        }\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.StateMachine/BlackboardKeyViewModel.cs",
    "content": "﻿using System.Collections.Generic;\n\nnamespace Nodify.StateMachine\n{\n    public class BlackboardKeyViewModel : ObservableObject\n    {\n        // Cache the key and the input value so we can restore them when swapping input types\n        private readonly Dictionary<bool, object?> _values = new Dictionary<bool, object?>();\n\n        public string? PropertyName { get; set; }\n\n        private string _name = \"New key\";\n        public string Name\n        {\n            get => _name;\n            set\n            {\n                if (!string.IsNullOrWhiteSpace(value))\n                {\n                    SetProperty(ref _name, value);\n                }\n            }\n        }\n\n        private BlackboardKeyType _type;\n        public BlackboardKeyType Type\n        {\n            get => _type;\n            set\n            {\n                if (SetProperty(ref _type, value))\n                {\n                    Value = GetDefaultValue(_type);\n                }\n            }\n        }\n\n        private object? _value = BoxValue.False;\n        public object? Value\n        {\n            get => _value;\n            set => SetProperty(ref _value, GetRealValue(value)).Then(() => _values[ValueIsKey] = Value);\n        }\n\n        private bool _valueIsKey;\n        public bool ValueIsKey\n        {\n            get => _valueIsKey;\n            set\n            {\n                if (SetProperty(ref _valueIsKey, value) && _values.TryGetValue(_valueIsKey, out var existingValue))\n                {\n                    Value = existingValue;\n                }\n            }\n        }\n\n        private bool _canChangeType = true;\n        public bool CanChangeType\n        {\n            get => _canChangeType;\n            set => SetProperty(ref _canChangeType, value);\n        }\n\n        private object? GetRealValue(object? value)\n        {\n            if (value is string str)\n            {\n                switch (Type)\n                {\n                    case BlackboardKeyType.Boolean:\n                        bool.TryParse(str, out var b);\n                        value = b;\n                        break;\n\n                    case BlackboardKeyType.Integer:\n                        int.TryParse(str, out var i);\n                        value = i;\n                        break;\n\n                    case BlackboardKeyType.Double:\n                        double.TryParse(str, out var d);\n                        value = d;\n                        break;\n\n                    case BlackboardKeyType.String:\n                    case BlackboardKeyType.Object:\n                        value = str;\n                        break;\n                }\n            }\n\n            return value;\n        }\n\n        public static object? GetDefaultValue(BlackboardKeyType type)\n            => type switch\n            {\n                BlackboardKeyType.Boolean => BoxValue.False,\n                BlackboardKeyType.Integer => BoxValue.Int0,\n                BlackboardKeyType.Double => BoxValue.Double0,\n                BlackboardKeyType.String => null,\n                BlackboardKeyType.Object => null,\n                _ => null\n            };\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.StateMachine/BlackboardViewModel.cs",
    "content": "﻿using System.Linq;\n\nnamespace Nodify.StateMachine\n{\n    public class BlackboardViewModel : ObservableObject\n    {\n        private NodifyObservableCollection<BlackboardKeyViewModel> _keys = new NodifyObservableCollection<BlackboardKeyViewModel>();\n        public NodifyObservableCollection<BlackboardKeyViewModel> Keys\n        {\n            get => _keys;\n            set => SetProperty(ref _keys, value);\n        }\n\n        private NodifyObservableCollection<BlackboardItemReferenceViewModel> _actions = new NodifyObservableCollection<BlackboardItemReferenceViewModel>();\n        public NodifyObservableCollection<BlackboardItemReferenceViewModel> Actions\n        {\n            get => _actions;\n            set => SetProperty(ref _actions, value);\n        }\n\n        private NodifyObservableCollection<BlackboardItemReferenceViewModel> _conditions = new NodifyObservableCollection<BlackboardItemReferenceViewModel>();\n        public NodifyObservableCollection<BlackboardItemReferenceViewModel> Conditions\n        {\n            get => _conditions;\n            set => SetProperty(ref _conditions, value);\n        }\n\n        public INodifyCommand AddKeyCommand { get; }\n        public INodifyCommand RemoveKeyCommand { get; }\n\n        public BlackboardViewModel()\n        {\n            AddKeyCommand = new DelegateCommand(() => Keys.Add(new BlackboardKeyViewModel\n            {\n                Name = \"New Key \"\n            }));\n\n            RemoveKeyCommand = new DelegateCommand<BlackboardKeyViewModel>(key => Keys.Remove(key));\n\n            Keys.WhenAdded(key =>\n            {\n                var existingKeyNames = Keys.Where(k => k != key).Select(k => k.Name).ToList();\n                key.Name = existingKeyNames.GetUnique(key.Name);\n            });\n        }\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.StateMachine/Converters/BlackboardKeyEditorConverter.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Globalization;\nusing System.Windows.Data;\nusing System.Windows.Markup;\n\nnamespace Nodify.StateMachine\n{\n    public class BlackboardKeyEditorConverter : MarkupExtension, IMultiValueConverter\n    {\n        public bool CanChangeInputType { get; set; }\n\n        public object? Convert(IList<object?> values, Type targetType, object? parameter, CultureInfo culture)\n        {\n            if (values.Count >= 2 && values[0] is ICollection<BlackboardKeyViewModel> availableKeys && values[1] is BlackboardKeyViewModel target)\n            {\n                return new BlackboardKeyEditorViewModel\n                {\n                    AvailableKeys = availableKeys,\n                    Target = target,\n                    IsEditing = values.Count >= 3 && values[2] is bool b && b,\n                    CanChangeInputType = CanChangeInputType && (target.Type != BlackboardKeyType.Object || target.CanChangeType),\n                    CanChangeKeyType = target.CanChangeType\n                };\n            }\n\n            return values;\n        }\n\n        public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)\n        {\n            throw new NotImplementedException();\n        }\n\n        public override object ProvideValue(IServiceProvider serviceProvider) => this;\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.StateMachine/Converters/ConnectorOffsetConverter.cs",
    "content": "﻿using System;\nusing System.Windows;\nusing System.Globalization;\nusing System.Windows.Data;\n\nnamespace Nodify.StateMachine\n{\n    public class ConnectorOffsetConverter : IValueConverter\n    {\n        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)\n        {\n            double offset = System.Convert.ToDouble(parameter);\n            if (value is Size s)\n            {\n                return new Size((s.Width + offset) / 2, (s.Height + offset) / 2);\n            }\n\n            return new Size(offset / 2, offset / 2);\n        }\n\n        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)\n        {\n            double offset = System.Convert.ToDouble(parameter);\n            if (value is Size s)\n            {\n                return new Size((s.Width + offset) / 2, (s.Height + offset) / 2);\n            }\n\n            return new Size(offset / 2, offset / 2);\n        }\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.StateMachine/Converters/DrawingBrushToRectangleConverter.cs",
    "content": "using System;\nusing System.Globalization;\nusing Avalonia.Controls.Shapes;\nusing Avalonia.Media;\n\nnamespace Nodify.StateMachine;\n\npublic class DrawingBrushToRectangleConverter : IValueConverter\n{\n    public static DrawingBrushToRectangleConverter Instance { get; } = new DrawingBrushToRectangleConverter();\n    \n    public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)\n    {\n        if (value is IBrush brush)\n        {\n            return new Rectangle()\n            {\n                Fill = brush,\n                Width = 16,\n                Height = 16\n            };\n        }\n\n        return null;\n    }\n\n    public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)\n    {\n        throw new NotImplementedException();\n    }\n}"
  },
  {
    "path": "Examples/Nodify.StateMachine/Converters/FilterBlackboardKeysConverter.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Globalization;\nusing System.Linq;\nusing System.Windows.Data;\nusing System.Windows.Markup;\n\nnamespace Nodify.StateMachine\n{\n    public class FilterBlackboardKeysConverter : MarkupExtension, IMultiValueConverter\n    {\n        public object? Convert(IList<object?> values, Type targetType, object? parameter, CultureInfo culture)\n        {\n            if (values.Count >= 2 && values[0] is IEnumerable<BlackboardKeyViewModel> keys && values[1] is BlackboardKeyType filter)\n            {\n                return keys.Where(k => k.Type == filter || filter == BlackboardKeyType.Object);\n            }\n\n            return values;\n        }\n\n        public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)\n        {\n            throw new NotImplementedException();\n        }\n\n        public override object ProvideValue(IServiceProvider serviceProvider) => this;\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.StateMachine/GlobalUsings.cs",
    "content": "global using Avalonia.Controls;\nglobal using Avalonia;\nglobal using Avalonia.Data.Converters;\nglobal using Avalonia.Markup.Xaml;\nglobal using System.Windows.Input;\nglobal using Avalonia.Interactivity;\nglobal using Avalonia.Controls.ApplicationLifetimes;"
  },
  {
    "path": "Examples/Nodify.StateMachine/Helpers/BlackboardDescriptor.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Reflection;\n\nnamespace Nodify.StateMachine\n{\n    public static class BlackboardDescriptor\n    {\n        private class KeyDescription\n        {\n            public KeyDescription(string displayName, string propertyName, BlackboardKeyType type, bool canChangeType)\n            {\n                DisplayName = displayName;\n                PropertyName = propertyName;\n                Type = type;\n                CanChangeType = canChangeType;\n            }\n\n            public string DisplayName { get; }\n            public string PropertyName { get; }\n            public BlackboardKeyType Type { get; }\n            public bool CanChangeType { get; }\n        }\n\n        private class ItemDescription\n        {\n            public string? Name { get; set; }\n            public List<KeyDescription> Input { get; } = new List<KeyDescription>();\n            public List<KeyDescription> Output { get; } = new List<KeyDescription>();\n        }\n\n        public static BlackboardItemViewModel? GetItem(BlackboardItemReferenceViewModel? actionRef)\n        {\n            if (actionRef?.Type != null)\n            {\n                var description = GetDescription(actionRef.Type);\n\n                var input = description.Input.Select(d => new BlackboardKeyViewModel\n                {\n                    Name = d.DisplayName,\n                    Type = d.Type,\n                    PropertyName = d.PropertyName,\n                    CanChangeType = d.CanChangeType,\n                    ValueIsKey = true\n                });\n\n                var output = description.Output.Select(d => new BlackboardKeyViewModel\n                {\n                    Name = d.DisplayName,\n                    Type = d.Type,\n                    PropertyName = d.PropertyName,\n                    CanChangeType = d.CanChangeType,\n                    ValueIsKey = true\n                });\n\n                return new BlackboardItemViewModel\n                {\n                    Name = actionRef.Name,\n                    Type = actionRef.Type,\n                    Input = new NodifyObservableCollection<BlackboardKeyViewModel>(input),\n                    Output = new NodifyObservableCollection<BlackboardKeyViewModel>(output),\n                };\n            }\n\n            return default;\n        }\n\n        public static BlackboardItemReferenceViewModel GetReference(Type type)\n        {\n            var desc = GetDescription(type);\n\n            return new BlackboardItemReferenceViewModel\n            {\n                Name = desc.Name,\n                Type = type\n            };\n        }\n\n        private static readonly Dictionary<Type, ItemDescription> _descriptions = new Dictionary<Type, ItemDescription>();\n        private static ItemDescription GetDescription(Type type)\n        {\n            if (!_descriptions.TryGetValue(type, out var description))\n            {\n                var actionAttr = type.GetCustomAttribute<BlackboardItemAttribute>();\n\n                var desc = new ItemDescription\n                {\n                    Name = actionAttr?.DisplayName ?? type.Name\n                };\n\n                var props = type.GetProperties();\n                for (int i = 0; i < props.Length; i++)\n                {\n                    var prop = props[i];\n                    var keyAttr = prop.GetCustomAttribute<BlackboardPropertyAttribute>();\n\n                    if (keyAttr != null)\n                    {\n                        var key = new KeyDescription(keyAttr.Name ?? prop.Name, prop.Name, keyAttr.Type, keyAttr.CanChangeType);\n\n                        if (keyAttr.Usage == BlackboardKeyUsage.Input)\n                        {\n                            desc.Input.Add(key);\n                        }\n                        else\n                        {\n                            desc.Output.Add(key);\n                        }\n                    }\n                }\n\n                _descriptions.Add(type, desc);\n\n                return desc;\n            }\n\n            return description;\n        }\n\n        public static List<BlackboardItemReferenceViewModel> GetAvailableItems<T>()\n        {\n            var result = new List<BlackboardItemReferenceViewModel>();\n            var ourType = typeof(T);\n\n            var types = ourType.Assembly.GetTypes();\n\n            for (int i = 0; i < types.Length; i++)\n            {\n                var type = types[i];\n                if (type.IsClass && !type.IsAbstract && ourType.IsAssignableFrom(type))\n                {\n                    result.Add(GetReference(type));\n                }\n            }\n\n            return result;\n        }\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.StateMachine/MainWindow.xaml",
    "content": "﻿<Window x:Class=\"Nodify.StateMachine.MainWindow\"\n        xmlns=\"https://github.com/avaloniaui\"\n        xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n        xmlns:d=\"http://schemas.microsoft.com/expression/blend/2008\"\n        xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\"\n        xmlns:local=\"clr-namespace:Nodify.StateMachine\"\n        xmlns:shared=\"clr-namespace:Nodify;assembly=Nodify.Shared\"\n        xmlns:nodify=\"https://miroiu.github.io/nodify\"\n        xmlns:behaviours=\"clr-namespace:Nodify.Shared.Behaviours;assembly=Nodify.Shared\"\n        xmlns:stateMachine=\"clr-namespace:Nodify.StateMachine;assembly=Nodify.Shared\"\n        xmlns:compatibility=\"clr-namespace:Nodify.Compatibility;assembly=Nodify\"\n        CanResize=\"True\"\n        mc:Ignorable=\"d\"\n        Background=\"{DynamicResource NodifyEditor.BackgroundBrush}\"\n        Foreground=\"{DynamicResource ForegroundBrush}\"\n        Title=\"State Machine Editor\"\n        Height=\"500\"\n        Width=\"930\">\n    <Window.DataContext>\n        <local:StateMachineViewModel />\n    </Window.DataContext>\n\n    <Window.Resources>\n        <shared:BindingProxy x:Key=\"EditorProxy\"\n                             DataContext=\"{Binding}\" />\n        <shared:BindingProxy x:Key=\"BlackboardProxy\"\n                             DataContext=\"{Binding Blackboard}\" />\n        <local:ConnectorOffsetConverter x:Key=\"ConnectorOffsetConverter\" />\n    </Window.Resources>\n\n    <Window.Styles>\n        <Style Selector=\"MenuItem\">\n            <Setter Property=\"Icon\">\n                <Template>\n                    <Rectangle Width=\"16\" Height=\"16\" Fill=\"{Binding $parent[MenuItem].Tag}\" />\n                </Template>\n            </Setter>\n        </Style>\n    </Window.Styles>\n    \n    <Grid>\n        <Grid.ColumnDefinitions>\n            <ColumnDefinition Width=\"Auto\" />\n            <ColumnDefinition />\n        </Grid.ColumnDefinitions>\n\n        <ScrollViewer x:Name=\"PART_ScrollViewer\"\n                      HorizontalScrollBarVisibility=\"Auto\"\n                      VerticalScrollBarVisibility=\"Auto\"\n                      Grid.Column=\"1\">\n            <nodify:NodifyEditor x:Name=\"Editor\"\n                                 ItemsSource=\"{Binding States}\"\n                                 SelectedItem=\"{Binding SelectedState}\"\n                                 SelectedItems=\"{Binding SelectedStates}\"\n                                 Connections=\"{Binding Transitions}\"\n                                 PendingConnection=\"{Binding PendingTransition}\"\n                                 DisconnectConnectorCommand=\"{Binding DisconnectStateCommand}\"\n                                 ConnectionCompletedCommand=\"{Binding CreateTransitionCommand}\"\n                                 RemoveConnectionCommand=\"{Binding DeleteTransitionCommand}\"\n                                 Grid.Column=\"1\">\n                <nodify:NodifyEditor.PendingConnectionTemplate>\n                    <DataTemplate DataType=\"{x:Type local:TransitionViewModel}\">\n                        <nodify:PendingConnection Source=\"{Binding Source, Mode=OneWayToSource}\"\n                                                  Target=\"{Binding Target, Mode=OneWayToSource}\"\n                                                  StrokeDashArray=\"0\"\n                                                  EnablePreview=\"True\">\n                            <nodify:PendingConnection.Template>\n                                <ControlTemplate TargetType=\"{x:Type nodify:PendingConnection}\">\n                                    <nodify:LineConnection Source=\"{TemplateBinding SourceAnchor}\"\n                                                                  Target=\"{TemplateBinding TargetAnchor}\"\n                                                                  StrokeThickness=\"{TemplateBinding StrokeThickness}\"\n                                                                  StrokeDashArray=\"{TemplateBinding StrokeDashArray}\"\n                                                                  SourceOffset=\"{Binding Source.Size, Converter={StaticResource ConnectorOffsetConverter}, ConverterParameter=5}\"\n                                                                  Spacing=\"0\"\n                                                                  SourceOffsetMode=\"Edge\"\n                                                                  TargetOffsetMode=\"None\" />\n                                </ControlTemplate>\n                            </nodify:PendingConnection.Template>\n                        </nodify:PendingConnection>\n                    </DataTemplate>\n                </nodify:NodifyEditor.PendingConnectionTemplate>\n\n                <nodify:NodifyEditor.ConnectionTemplate>\n                    <DataTemplate DataType=\"{x:Type local:TransitionViewModel}\">\n                        <nodify:LineConnection Source=\"{Binding Source.Anchor}\"\n                                                      Target=\"{Binding Target.Anchor}\"\n                                                      SourceOffset=\"{Binding Source.Size, Converter={StaticResource ConnectorOffsetConverter}, ConverterParameter=5}\"\n                                                      TargetOffset=\"{Binding Target.Size, Converter={StaticResource ConnectorOffsetConverter}, ConverterParameter=5}\"\n                                                      Spacing=\"0\"\n                                                      SourceOffsetMode=\"Edge\"\n                                                      TargetOffsetMode=\"Edge\"\n                                                      Tag=\"{Binding}\">\n                            <nodify:LineConnection.Theme>\n                                <ControlTheme TargetType=\"{x:Type nodify:LineConnection}\"\n                                       BasedOn=\"{StaticResource {x:Type nodify:LineConnection}}\">\n                                    <Setter Property=\"StrokeThickness\"\n                                            Value=\"3\" />\n                                    <Setter Property=\"(Interaction.Behaviors)\">\n                                        <BehaviorCollectionTemplate>\n                                            <BehaviorCollection>\n                                                <DataTrigger Property=\"IsActive\"\n                                                                        Value=\"True\">\n                                                    <PropertySetter Property=\"Stroke\"\n                                                                    Value=\"{DynamicResource ActiveStateBrush}\" />\n                                                    <PropertySetter Property=\"StrokeThickness\"\n                                                                    Value=\"6\" />\n                                                </DataTrigger>\n                                            </BehaviorCollection>\n                                        </BehaviorCollectionTemplate>\n                                    </Setter>\n                                </ControlTheme>\n                            </nodify:LineConnection.Theme>\n                            <nodify:LineConnection.ContextMenu>\n                                <ContextMenu DataContext=\"{Binding DataContext, Source={StaticResource EditorProxy}}\">\n                                    <MenuItem Header=\"_Delete\"\n                                              Icon=\"{StaticResource DeleteIcon}\"\n                                              Command=\"{Binding DeleteTransitionCommand}\"\n                                              CommandParameter=\"{Binding PlacementTarget.Tag, RelativeSource={RelativeSource AncestorType={x:Type ContextMenu}}}\" />\n                                </ContextMenu>\n                            </nodify:LineConnection.ContextMenu>\n                        </nodify:LineConnection>\n                    </DataTemplate>\n                </nodify:NodifyEditor.ConnectionTemplate>\n\n                <nodify:NodifyEditor.ItemTemplate>\n                    <DataTemplate DataType=\"{x:Type local:StateViewModel}\">\n                        <!--If IsConnected is false, Anchor won't be updated-->\n                        <nodify:StateNode Content=\"{Binding}\"\n                                          IsConnected=\"True\"\n                                          Anchor=\"{Binding Anchor, Mode=OneWayToSource}\">\n                            <nodify:StateNode.ContentTemplate>\n                                <DataTemplate DataType=\"{x:Type local:StateViewModel}\">\n                                    <shared:EditableTextBlock Text=\"{Binding Name}\"\n                                                              IsEditing=\"{Binding IsRenaming}\"\n                                                              IsEditable=\"{Binding IsEditable}\"\n                                                              MaxLength=\"30\" />\n                                </DataTemplate>\n                            </nodify:StateNode.ContentTemplate>\n                            <nodify:StateNode.Theme>\n                                <ControlTheme TargetType=\"{x:Type nodify:StateNode}\"\n                                       BasedOn=\"{StaticResource {x:Type nodify:StateNode}}\">\n                                    <Setter Property=\"(Interaction.Behaviors)\">\n                                        <BehaviorCollectionTemplate>\n                                            <BehaviorCollection>\n                                                <DataTrigger Property=\"IsEditable\"\n                                                             Value=\"False\">\n                                                    <PropertySetter Property=\"BorderBrush\"\n                                                                    Value=\"{DynamicResource ReadOnlyStateBrush}\" />\n                                                </DataTrigger>\n                                                <DataTrigger Property=\"IsActive\"\n                                                             Value=\"True\">\n                                                    <PropertySetter Property=\"BorderBrush\"\n                                                                    Value=\"{DynamicResource ActiveStateBrush}\" />\n                                                </DataTrigger>\n                                            </BehaviorCollection>\n                                        </BehaviorCollectionTemplate>\n                                    </Setter>\n                                </ControlTheme>\n                            </nodify:StateNode.Theme>\n                        </nodify:StateNode>\n                    </DataTemplate>\n                </nodify:NodifyEditor.ItemTemplate>\n\n                <nodify:NodifyEditor.ItemContainerTheme>\n                    <ControlTheme TargetType=\"{x:Type nodify:ItemContainer}\"\n                           BasedOn=\"{StaticResource {x:Type nodify:ItemContainer}}\">\n                        <Setter Property=\"BorderBrush\"\n                                Value=\"Transparent\" />\n                        <Setter Property=\"Location\"\n                                Value=\"{Binding Location}\" />\n                        <Setter Property=\"ActualSize\"\n                                Value=\"{Binding Size, Mode=OneWayToSource}\" />\n                        <Setter Property=\"ContextMenu\">\n                            <Setter.Value>\n                                <ContextMenu DataContext=\"{Binding DataContext, Source={StaticResource EditorProxy}}\">\n                                    <MenuItem Header=\"_Delete\"\n                                              Tag=\"{StaticResource DeleteIcon}\"\n                                              Command=\"{Binding DeleteSelectionCommand}\" />\n                                    <MenuItem Header=\"Di_sconnect\"\n                                              Tag=\"{StaticResource DisconnectIcon}\"\n                                              Command=\"{Binding DisconnectSelectionCommand}\" />\n                                    <MenuItem Header=\"_Rename\"\n                                              Tag=\"{StaticResource RenameIcon}\"\n                                              Command=\"{Binding RenameStateCommand}\" />\n                                    <MenuItem Header=\"_Alignment\"\n                                              Tag=\"{StaticResource AlignTopIcon}\">\n                                        <MenuItem Header=\"_Top\"\n                                                  Tag=\"{StaticResource AlignTopIcon}\"\n                                                  Command=\"{x:Static nodify:EditorCommands.Align}\"\n                                                  CommandParameter=\"Top\" />\n                                        <MenuItem Header=\"_Left\"\n                                                  Tag=\"{StaticResource AlignLeftIcon}\"\n                                                  Command=\"{x:Static nodify:EditorCommands.Align}\"\n                                                  CommandParameter=\"Left\" />\n                                        <MenuItem Header=\"_Bottom\"\n                                                  Tag=\"{StaticResource AlignBottomIcon}\"\n                                                  Command=\"{x:Static nodify:EditorCommands.Align}\"\n                                                  CommandParameter=\"Bottom\" />\n                                        <MenuItem Header=\"_Right\"\n                                                  Tag=\"{StaticResource AlignRightIcon}\"\n                                                  Command=\"{x:Static nodify:EditorCommands.Align}\"\n                                                  CommandParameter=\"Right\" />\n                                        <MenuItem Header=\"_Middle\"\n                                                  Tag=\"{StaticResource AlignMiddleIcon}\"\n                                                  Command=\"{x:Static nodify:EditorCommands.Align}\"\n                                                  CommandParameter=\"Middle\" />\n                                        <MenuItem Header=\"_Center\"\n                                                  Tag=\"{StaticResource AlignCenterIcon}\"\n                                                  Command=\"{x:Static nodify:EditorCommands.Align}\"\n                                                  CommandParameter=\"Center\" />\n                                    </MenuItem>\n                                </ContextMenu>\n                            </Setter.Value>\n                        </Setter>\n                    </ControlTheme>\n                </nodify:NodifyEditor.ItemContainerTheme>\n\n                <nodify:NodifyEditor.ContextMenu>\n                    <ContextMenu DataContext=\"{Binding DataContext, Source={StaticResource EditorProxy}}\">\n                        <MenuItem Header=\"_Add State\"\n                                  Tag=\"{StaticResource AddStateIcon}\"\n                                  InputGesture=\"Shift+A\"\n                                  Command=\"{Binding AddStateCommand}\"\n                                  CommandParameter=\"{Binding #Editor.MouseLocation}\" />\n                        <MenuItem Header=\"_Delete\"\n                                  Tag=\"{StaticResource DeleteIcon}\"\n                                  InputGesture=\"Delete\"\n                                  Command=\"{Binding DeleteSelectionCommand}\" />\n                        <Separator Background=\"{DynamicResource BorderBrush}\" />\n                        <MenuItem Header=\"_Select All\"\n                                  Tag=\"{StaticResource SelectAllIcon}\"\n                                  InputGesture=\"Ctrl+A\"\n                                  Command=\"{x:Static nodify:EditorCommands.SelectAll}\" />\n                    </ContextMenu>\n                </nodify:NodifyEditor.ContextMenu>\n\n                <nodify:NodifyEditor.KeyBindings>\n                    <KeyBinding Gesture=\"Delete\"\n                                Command=\"{Binding DeleteSelectionCommand}\" />\n                    <KeyBinding Gesture=\"Shift+A\"\n                                Command=\"{Binding AddStateCommand}\"\n                                CommandParameter=\"{Binding MouseLocation, RelativeSource={RelativeSource AncestorType={x:Type nodify:NodifyEditor}}}\" />\n                </nodify:NodifyEditor.KeyBindings>\n            </nodify:NodifyEditor>\n        </ScrollViewer>\n\n        <!--TOOLBAR-->\n        <Border CornerRadius=\"2\"\n                Background=\"{DynamicResource PanelBackgroundBrush}\"\n                BorderThickness=\"0 0 0 1\"\n                HorizontalAlignment=\"Center\"\n                VerticalAlignment=\"Top\"\n                Margin=\"10 0\"\n                Grid.Column=\"1\">\n            <StackPanel Orientation=\"Horizontal\">\n                <Button Command=\"{Binding PauseCommand}\">\n                    <Button.Theme>\n                        <ControlTheme TargetType=\"{x:Type Button}\"\n                               BasedOn=\"{StaticResource IconButton}\">\n                            <Setter Property=\"Content\"\n                                    Value=\"{StaticResource PauseIcon}\" />\n                            <Setter Property=\"(ToolTip.Tip)\"\n                                    Value=\"Pause\" />\n                            <Setter Property=\"(Interaction.Behaviors)\">\n                                <BehaviorCollectionTemplate>\n                                    <BehaviorCollection>\n                                        <DataTrigger Property=\"Runner.State\"\n                                                     Value=\"Stopped\">\n                                            <PropertySetter Property=\"IsVisible\"\n                                                            Value=\"False\" />\n                                        </DataTrigger>\n                                        <DataTrigger Property=\"Runner.State\"\n                                                     Value=\"Paused\">\n                                            <PropertySetter Property=\"Content\"\n                                                            Value=\"{StaticResource UnpauseIcon}\" />\n                                            <PropertySetter Property=\"(ToolTip.Tip)\"\n                                                            Value=\"Continue\" />\n                                        </DataTrigger>\n                                    </BehaviorCollection>\n                                </BehaviorCollectionTemplate>\n                            </Setter>\n                        </ControlTheme>\n                    </Button.Theme>\n                </Button>\n\n                <Button Command=\"{Binding RunCommand}\"\n                        Theme=\"{StaticResource IconButton}\">\n                    <StackPanel Orientation=\"Horizontal\">\n                        <ContentControl Margin=\"0 0 4 0\">\n                            <ContentControl.Theme>\n                                <ControlTheme TargetType=\"{x:Type ContentControl}\">\n                                    <Setter Property=\"Content\"\n                                            Value=\"{StaticResource StopIcon}\" />\n                                    <Setter Property=\"(ToolTip.Tip)\"\n                                            Value=\"Stop\" />\n                                    <Setter Property=\"(Interaction.Behaviors)\">\n                                        <BehaviorCollectionTemplate>\n                                            <BehaviorCollection>\n                                                <DataTrigger Property=\"Runner.State\"\n                                                             Value=\"Stopped\">\n                                                    <PropertySetter Property=\"Content\"\n                                                                    Value=\"{StaticResource RunIcon}\" />\n                                                    <PropertySetter Property=\"(ToolTip.Tip)\"\n                                                                    Value=\"Run\" />\n                                                </DataTrigger>\n                                            </BehaviorCollection>\n                                        </BehaviorCollectionTemplate>\n                                    </Setter>\n                                </ControlTheme>\n                            </ContentControl.Theme>\n                        </ContentControl>\n                        <TextBlock Text=\"{Binding Name}\" />\n                    </StackPanel>\n                </Button>\n\n                <Separator Width=\"1\" Height=\"20\" />\n\n                <WpfBtn Content=\"{StaticResource ZoomInIcon}\"\n                        Command=\"{x:Static nodify:EditorCommands.ZoomIn}\"\n                        CommandTarget=\"{Binding ElementName=Editor}\"\n                        ToolTip.Tip=\"Zoom In\"\n                        Theme=\"{StaticResource IconButton}\" />\n\n                <WpfBtn Content=\"{StaticResource ZoomOutIcon}\"\n                        Command=\"{x:Static nodify:EditorCommands.ZoomOut}\"\n                        CommandTarget=\"{Binding ElementName=Editor}\"\n                        ToolTip.Tip=\"Zoom Out\"\n                        Theme=\"{StaticResource IconButton}\" />\n\n                <Button Theme=\"{StaticResource IconButton}\"\n                        Content=\"{StaticResource ThemeIcon}\"\n                        Command=\"{Binding Source={x:Static shared:ThemeManager.SetNextThemeCommand}}\"\n                        ToolTip.Tip=\"Change theme\" />\n            </StackPanel>\n        </Border>\n\n        <!--Settings-->\n        <Expander HorizontalContentAlignment=\"Left\"\n                  VerticalContentAlignment=\"Center\"\n                  HorizontalAlignment=\"Left\"\n                  Background=\"{DynamicResource PanelBackgroundBrush}\"\n                  Padding=\"0 1 4 3\"\n                  IsExpanded=\"True\"\n                  ExpandDirection=\"Left\">\n            <Expander.Theme>\n                <ControlTheme TargetType=\"{x:Type Expander}\"\n                       BasedOn=\"{StaticResource {x:Type Expander}}\">\n                    <Setter Property=\"Tag\"\n                            Value=\"{StaticResource ExpandRightIcon}\" />\n                    <Setter Property=\"(Interaction.Behaviors)\">\n                        <BehaviorCollectionTemplate>\n                            <BehaviorCollection>\n                                <DataTrigger Property=\"IsExpanded\"\n                                             UseDataContext=\"False\"\n                                             Value=\"True\">\n                                    <PropertySetter Property=\"Tag\"\n                                                    Value=\"{StaticResource ExpandLeftIcon}\" />\n                                </DataTrigger>\n                            </BehaviorCollection>\n                        </BehaviorCollectionTemplate>\n                    </Setter>\n                </ControlTheme>\n            </Expander.Theme>\n\n            <Border BorderBrush=\"{DynamicResource BackgroundBrush}\"\n                    BorderThickness=\"1\"\n                    Width=\"350\"\n                    Padding=\"10\"\n                    HorizontalAlignment=\"Stretch\">\n                <Grid>\n                    <Grid.RowDefinitions>\n                        <RowDefinition />\n                        <RowDefinition Height=\"2*\" />\n                    </Grid.RowDefinitions>\n\n                    <!--TRANSITIONS-->\n                    <ScrollViewer IsVisible=\"{Binding SelectedState, Converter={shared:BooleanToVisibilityConverter Negate=True}}\"\n                                  VerticalScrollBarVisibility=\"Auto\"\n                                  Grid.Row=\"1\">\n                        <Grid>\n                            <Grid.RowDefinitions>\n                                <RowDefinition Height=\"Auto\" />\n                                <RowDefinition Height=\"Auto\" />\n                                <RowDefinition />\n                            </Grid.RowDefinitions>\n\n                            <TextBlock Text=\"Transitions\"\n                                       Foreground=\"{DynamicResource ForegroundBrush}\"\n                                       FontWeight=\"Bold\"\n                                       FontSize=\"16\" />\n\n                            <Separator Height=\"2\"\n                                       Margin=\"0 2 0 5\"\n                                       Grid.Row=\"1\" />\n\n                            <ItemsControl ItemsSource=\"{Binding Transitions}\"\n                                          Grid.Row=\"2\">\n                                <ItemsControl.ItemTemplate>\n                                    <DataTemplate DataType=\"{x:Type local:TransitionViewModel}\">\n                                        <Expander BorderThickness=\"0 0 0 1\"\n                                                  Padding=\"0 5 0 0\"\n                                                  IsExpanded=\"True\"\n                                                  BorderBrush=\"{DynamicResource BackgroundBrush}\">\n                                            <Expander.Theme>\n                                                <ControlTheme TargetType=\"{x:Type Expander}\"\n                                                       BasedOn=\"{StaticResource {x:Type Expander}}\">\n                                                    <Setter Property=\"Tag\"\n                                                            Value=\"{StaticResource ExpandRightIcon}\" />\n                                                    <Setter Property=\"(Interaction.Behaviors)\">\n                                                        <BehaviorCollectionTemplate>\n                                                            <BehaviorCollection>\n                                                                <DataTrigger Property=\"IsExpanded\"\n                                                                             UseDataContext=\"False\"\n                                                                             Value=\"True\">\n                                                                    <PropertySetter Property=\"Tag\"\n                                                                        Value=\"{StaticResource ExpandDownIcon}\" />\n                                                                </DataTrigger>\n                                                            </BehaviorCollection>\n                                                        </BehaviorCollectionTemplate>\n                                                    </Setter>\n                                                </ControlTheme>\n                                            </Expander.Theme>\n                                            <Expander.Header>\n                                                <TextBlock>\n                                                    <TextBlock.Theme>\n                                                        <ControlTheme TargetType=\"{x:Type TextBlock}\">\n                                                            <Setter Property=\"Foreground\"\n                                                                    Value=\"{DynamicResource ForegroundBrush}\" />\n                                                            <Setter Property=\"(Interaction.Behaviors)\">\n                                                                <BehaviorCollectionTemplate>\n                                                                    <BehaviorCollection>\n                                                                        <DataTrigger Property=\"IsActive\"\n                                                                                     Value=\"True\">\n                                                                            <PropertySetter Property=\"Foreground\"\n                                                                                            Value=\"{DynamicResource ActiveStateBrush}\" />\n                                                                        </DataTrigger>\n                                                                    </BehaviorCollection>\n                                                                </BehaviorCollectionTemplate>\n                                                            </Setter>\n                                                        </ControlTheme>\n                                                    </TextBlock.Theme>\n                                                    <Run Text=\"{Binding Source.Name, Mode=OneWay}\" />\n                                                    <Run Text=\"🠚\" />\n                                                    <Run Text=\"{Binding Target.Name, Mode=OneWay}\" />\n                                                </TextBlock>\n                                            </Expander.Header>\n\n                                            <Border HorizontalAlignment=\"Stretch\">\n                                                <Grid IsSharedSizeScope=\"True\">\n                                                    <Grid.ColumnDefinitions>\n                                                        <ColumnDefinition Width=\"Auto\"\n                                                                          SharedSizeGroup=\"ConditionName\" />\n                                                        <ColumnDefinition />\n                                                    </Grid.ColumnDefinitions>\n\n                                                    <Grid.RowDefinitions>\n                                                        <RowDefinition />\n                                                        <RowDefinition />\n                                                        <RowDefinition Height=\"Auto\" />\n                                                    </Grid.RowDefinitions>\n\n                                                    <!--CONDITION-->\n                                                    <TextBlock Text=\"Condition\"\n                                                               Margin=\"0 0 10 0\"\n                                                               VerticalAlignment=\"Center\" />\n\n                                                    <ComboBox ItemsSource=\"{Binding DataContext.Blackboard.Conditions, Source={StaticResource EditorProxy}}\"\n                                                              SelectedItem=\"{Binding ConditionReference}\"\n                                                              DisplayMemberBinding=\"{Binding Name}\"\n                                                              Grid.Column=\"1\" />\n\n                                                    <!--INPUT-->\n                                                    <ItemsControl ItemsSource=\"{Binding Condition.Input}\"\n                                                                  Padding=\"0 5 0 0\"\n                                                                  Grid.Row=\"1\"\n                                                                  Grid.ColumnSpan=\"2\">\n                                                        <ItemsControl.ItemTemplate>\n                                                            <DataTemplate DataType=\"{x:Type local:BlackboardKeyViewModel}\">\n                                                                <local:BlackboardKeyEditorView Margin=\"0 0 0 2\">\n                                                                    <local:BlackboardKeyEditorView.DataContext>\n                                                                        <MultiBinding Converter=\"{local:BlackboardKeyEditorConverter CanChangeInputType=True}\">\n                                                                            <Binding Source=\"{StaticResource BlackboardProxy}\"\n                                                                                     Path=\"DataContext.Keys\" />\n                                                                            <Binding Path=\"DataContext\" RelativeSource=\"{RelativeSource FindAncestor, AncestorType={x:Type ContentPresenter}}\" />\n                                                                        </MultiBinding>\n                                                                    </local:BlackboardKeyEditorView.DataContext>\n                                                                </local:BlackboardKeyEditorView>\n                                                            </DataTemplate>\n                                                        </ItemsControl.ItemTemplate>\n                                                    </ItemsControl>\n\n                                                    <Grid Grid.Row=\"2\"\n                                                          Grid.ColumnSpan=\"2\">\n                                                        <Grid.ColumnDefinitions>\n                                                            <ColumnDefinition />\n                                                            <ColumnDefinition />\n                                                        </Grid.ColumnDefinitions>\n\n                                                        <StackPanel Orientation=\"Horizontal\">\n                                                            <TextBlock Text=\"From: \"\n                                                                       VerticalAlignment=\"Center\" />\n                                                            <WpfBtn Theme=\"{StaticResource IconButton}\"\n                                                                    Command=\"{x:Static nodify:EditorCommands.BringIntoView}\"\n                                                                    CommandParameter=\"{Binding Source.Location}\"\n                                                                    CommandTarget=\"{Binding ElementName=Editor}\"\n                                                                    Foreground=\"DodgerBlue\">\n                                                                <TextBlock Text=\"{Binding Source.Name}\"\n                                                                           TextDecorations=\"Underline\" />\n                                                            </WpfBtn>\n                                                        </StackPanel>\n\n                                                        <StackPanel Orientation=\"Horizontal\"\n                                                                    Grid.Column=\"1\">\n                                                            <TextBlock Text=\"To: \"\n                                                                       VerticalAlignment=\"Center\" />\n                                                            <WpfBtn Theme=\"{StaticResource IconButton}\"\n                                                                    Command=\"{x:Static nodify:EditorCommands.BringIntoView}\"\n                                                                    CommandParameter=\"{Binding Target.Location}\"\n                                                                    CommandTarget=\"{Binding ElementName=Editor}\"\n                                                                    Foreground=\"DodgerBlue\">\n                                                                <TextBlock Text=\"{Binding Target.Name}\"\n                                                                           TextDecorations=\"Underline\" />\n                                                            </WpfBtn>\n                                                        </StackPanel>\n                                                    </Grid>\n                                                </Grid>\n                                            </Border>\n                                        </Expander>\n                                    </DataTemplate>\n                                </ItemsControl.ItemTemplate>\n                            </ItemsControl>\n                        </Grid>\n                    </ScrollViewer>\n\n                    <!--STATES-->\n                    <Grid IsVisible=\"{Binding SelectedState, Converter={x:Static ObjectConverters.IsNotNull}}\"\n                          Grid.Row=\"1\">\n                        <Grid.RowDefinitions>\n                            <RowDefinition Height=\"Auto\" />\n                            <RowDefinition Height=\"Auto\" />\n                            <RowDefinition />\n                        </Grid.RowDefinitions>\n\n                        <!--STATE NAME-->\n                        <Grid>\n                            <Grid.ColumnDefinitions>\n                                <ColumnDefinition Width=\"*\" />\n                                <ColumnDefinition Width=\"Auto\" />\n                            </Grid.ColumnDefinitions>\n                            <shared:EditableTextBlock Text=\"{Binding SelectedState.Name}\"\n                                                      IsEditing=\"{Binding IsChecked, ElementName=EditStateName}\"\n                                                      IsEditable=\"{Binding SelectedState.IsEditable}\"\n                                                      Foreground=\"{DynamicResource ForegroundBrush}\"\n                                                      FontWeight=\"Bold\"\n                                                      FontSize=\"16\"\n                                                      MaxLength=\"20\" />\n                            <CheckBox x:Name=\"EditStateName\"\n                                      IsVisible=\"{Binding SelectedState.IsEditable}\"\n                                      Content=\"{StaticResource EditIcon}\"\n                                      Theme=\"{StaticResource IconCheckBox}\"\n                                      Grid.Column=\"1\" />\n                        </Grid>\n\n                        <Separator Height=\"2\"\n                                   Margin=\"0 2 0 10\"\n                                   Grid.Row=\"1\" />\n\n                        <ScrollViewer IsVisible=\"{Binding SelectedState.Action}\"\n                                      VerticalScrollBarVisibility=\"Auto\"\n                                      Grid.Row=\"2\">\n                            <Grid IsSharedSizeScope=\"True\">\n                                <Grid.ColumnDefinitions>\n                                    <ColumnDefinition Width=\"Auto\" />\n                                    <ColumnDefinition />\n                                </Grid.ColumnDefinitions>\n                                <Grid.RowDefinitions>\n                                    <RowDefinition Height=\"Auto\" />\n                                    <RowDefinition Height=\"Auto\" />\n                                    <RowDefinition />\n                                </Grid.RowDefinitions>\n\n                                <!--ACTION-->\n                                <TextBlock Text=\"Action\"\n                                           Margin=\"0 0 10 0\"\n                                           VerticalAlignment=\"Center\" />\n                                <ComboBox ItemsSource=\"{Binding Blackboard.Actions}\"\n                                          SelectedItem=\"{Binding SelectedState.ActionReference}\"\n                                          IsEnabled=\"{Binding SelectedState.IsEditable}\"\n                                          DisplayMemberBinding=\"{Binding Name}\"\n                                          Grid.Column=\"1\" />\n\n                                <!--INPUT-->\n                                <Expander Margin=\"0 5 0 0\"\n                                          Padding=\"0 5 0 0\"\n                                          Grid.Row=\"1\"\n                                          Grid.ColumnSpan=\"2\"\n                                          BorderThickness=\"0 0 0 1\"\n                                          Header=\"Input\"\n                                          FontWeight=\"Bold\"\n                                          IsExpanded=\"True\"\n                                          BorderBrush=\"{DynamicResource BackgroundBrush}\"\n                                          VerticalAlignment=\"Stretch\"\n                                          VerticalContentAlignment=\"Stretch\"\n                                          IsVisible=\"{Binding SelectedState.Action.Input.Count, Converter={shared:BooleanToVisibilityConverter}}\">\n                                    <Expander.Theme>\n                                        <ControlTheme TargetType=\"{x:Type Expander}\"\n                                               BasedOn=\"{StaticResource {x:Type Expander}}\">\n                                            <Setter Property=\"Tag\"\n                                                    Value=\"{StaticResource ExpandRightIcon}\" />\n                                            <Setter Property=\"(Interaction.Behaviors)\">\n                                                <BehaviorCollectionTemplate>\n                                                    <BehaviorCollection>\n                                                        <DataTrigger Property=\"IsExpanded\" UseDataContext=\"False\" Value=\"True\">\n                                                            <PropertySetter Property=\"Tag\" Value=\"{StaticResource ExpandDownIcon}\" />\n                                                        </DataTrigger>\n                                                    </BehaviorCollection>\n                                                </BehaviorCollectionTemplate>\n                                            </Setter>\n                                        </ControlTheme>\n                                    </Expander.Theme>\n                                    <ItemsControl ItemsSource=\"{Binding SelectedState.Action.Input}\"\n                                                  FontWeight=\"Normal\">\n                                        <ItemsControl.ItemTemplate>\n                                            <DataTemplate DataType=\"{x:Type local:BlackboardKeyViewModel}\">\n                                                <local:BlackboardKeyEditorView Margin=\"0 0 0 2\">\n                                                    <local:BlackboardKeyEditorView.DataContext>\n                                                        <MultiBinding Converter=\"{local:BlackboardKeyEditorConverter CanChangeInputType=True}\">\n                                                            <Binding Source=\"{StaticResource BlackboardProxy}\"\n                                                                     Path=\"DataContext.Keys\" />\n                                                            <Binding Path=\"DataContext\" RelativeSource=\"{RelativeSource FindAncestor, AncestorType={x:Type ContentPresenter}}\" /> <!-- BindsDirectlyToSource=\"True\" -->\n                                                        </MultiBinding>\n                                                    </local:BlackboardKeyEditorView.DataContext>\n                                                </local:BlackboardKeyEditorView>\n                                            </DataTemplate>\n                                        </ItemsControl.ItemTemplate>\n                                    </ItemsControl>\n                                </Expander>\n\n                                <!--OUTPUT-->\n                                <Expander Margin=\"0 5 0 0\"\n                                          Padding=\"0 5 0 0\"\n                                          Grid.Row=\"2\"\n                                          Grid.ColumnSpan=\"2\"\n                                          Header=\"Output\"\n                                          FontWeight=\"Bold\"\n                                          BorderThickness=\"0 0 0 1\"\n                                          IsExpanded=\"True\"\n                                          BorderBrush=\"{DynamicResource BackgroundBrush}\"\n                                          IsVisible=\"{Binding SelectedState.Action.Output.Count, Converter={shared:BooleanToVisibilityConverter}}\">\n                                    <Expander.Theme>\n                                        <ControlTheme TargetType=\"{x:Type Expander}\"\n                                               BasedOn=\"{StaticResource {x:Type Expander}}\">\n                                            <Setter Property=\"Tag\"\n                                                    Value=\"{StaticResource ExpandRightIcon}\" />\n                                            <Setter Property=\"(Interaction.Behaviors)\">\n                                                <BehaviorCollectionTemplate>\n                                                    <BehaviorCollection>\n                                                        <DataTrigger Property=\"IsExpanded\" UseDataContext=\"False\" Value=\"True\">\n                                                            <PropertySetter Property=\"Tag\" Value=\"{StaticResource ExpandDownIcon}\" />\n                                                        </DataTrigger>\n                                                    </BehaviorCollection>\n                                                </BehaviorCollectionTemplate>\n                                            </Setter>\n                                        </ControlTheme>\n                                    </Expander.Theme>\n                                    <ItemsControl ItemsSource=\"{Binding SelectedState.Action.Output}\"\n                                                  FontWeight=\"Normal\">\n                                        <ItemsControl.ItemTemplate>\n                                            <DataTemplate DataType=\"{x:Type local:BlackboardKeyViewModel}\">\n                                                <local:BlackboardKeyEditorView Margin=\"0 0 0 2\">\n                                                    <local:BlackboardKeyEditorView.DataContext>\n                                                        <MultiBinding Converter=\"{local:BlackboardKeyEditorConverter CanChangeInputType=False}\">\n                                                            <Binding Source=\"{StaticResource BlackboardProxy}\"\n                                                                     Path=\"DataContext.Keys\" />\n                                                            <Binding Path=\"DataContext\" RelativeSource=\"{RelativeSource FindAncestor, AncestorType={x:Type ContentPresenter}}\" /> <!-- BindsDirectlyToSource=\"True\" -->\n                                                        </MultiBinding>\n                                                    </local:BlackboardKeyEditorView.DataContext>\n                                                </local:BlackboardKeyEditorView>\n                                            </DataTemplate>\n                                        </ItemsControl.ItemTemplate>\n                                    </ItemsControl>\n                                </Expander>\n                            </Grid>\n                        </ScrollViewer>\n                    </Grid>\n\n                    <!--BLACKBOARD-->\n                    <Grid IsSharedSizeScope=\"True\">\n                        <Grid.RowDefinitions>\n                            <RowDefinition Height=\"Auto\" />\n                            <RowDefinition Height=\"Auto\" />\n                            <RowDefinition />\n                        </Grid.RowDefinitions>\n\n                        <Grid>\n                            <Grid.ColumnDefinitions>\n                                <ColumnDefinition Width=\"*\" />\n                                <ColumnDefinition Width=\"Auto\" />\n                            </Grid.ColumnDefinitions>\n                            <shared:EditableTextBlock Text=\"{Binding Name}\"\n                                                      IsEditing=\"{Binding IsChecked, ElementName=EditName}\"\n                                                      Foreground=\"{DynamicResource ForegroundBrush}\"\n                                                      FontWeight=\"Bold\"\n                                                      FontSize=\"16\"\n                                                      MaxLength=\"20\" />\n\n                            <StackPanel Orientation=\"Horizontal\"\n                                        Grid.Column=\"1\">\n                                <CheckBox x:Name=\"EditName\"\n                                          Content=\"{StaticResource EditIcon}\"\n                                          ToolTip.Tip=\"Edit Name\"\n                                          Theme=\"{StaticResource IconCheckBox}\" />\n                                <Button Content=\"{StaticResource AddKeyIcon}\"\n                                        Command=\"{Binding Blackboard.AddKeyCommand}\"\n                                        ToolTip.Tip=\"Add New Key\"\n                                        Theme=\"{StaticResource IconButton}\" />\n                            </StackPanel>\n                        </Grid>\n\n                        <Separator Height=\"2\"\n                                   Margin=\"0 2 0 10\"\n                                   Grid.Row=\"1\" />\n\n                        <ScrollViewer VerticalScrollBarVisibility=\"Auto\"\n                                      HorizontalScrollBarVisibility=\"Auto\"\n                                      Grid.Row=\"2\">\n                            <ItemsControl ItemsSource=\"{Binding Blackboard.Keys}\">\n                                <ItemsControl.ItemTemplate>\n                                    <DataTemplate DataType=\"{x:Type local:BlackboardKeyViewModel}\">\n                                        <Grid Margin=\"0 0 0 2\">\n                                            <Grid.ColumnDefinitions>\n                                                <ColumnDefinition />\n                                                <ColumnDefinition SharedSizeGroup=\"Actions\" />\n                                            </Grid.ColumnDefinitions>\n\n                                            <local:BlackboardKeyEditorView>\n                                                <local:BlackboardKeyEditorView.DataContext>\n                                                    <MultiBinding Converter=\"{local:BlackboardKeyEditorConverter CanChangeInputType=False}\">\n                                                        <Binding Source=\"{StaticResource BlackboardProxy}\"\n                                                                 Path=\"DataContext.Keys\" />\n                                                        <Binding Path=\"DataContext\" RelativeSource=\"{RelativeSource FindAncestor, AncestorType={x:Type ContentPresenter}}\" /> <!-- BindsDirectlyToSource=\"True\" -->\n                                                        <Binding ElementName=\"EditKeyName\"\n                                                                 Path=\"IsChecked\" />\n                                                    </MultiBinding>\n                                                </local:BlackboardKeyEditorView.DataContext>\n                                            </local:BlackboardKeyEditorView>\n\n                                            <StackPanel Orientation=\"Horizontal\"\n                                                        Grid.Column=\"3\">\n                                                <CheckBox x:Name=\"EditKeyName\"\n                                                          Content=\"{StaticResource EditIcon}\"\n                                                          ToolTip.Tip=\"Edit Name\"\n                                                          Theme=\"{StaticResource IconCheckBox}\" />\n                                                <Button Content=\"{StaticResource RemoveKeyIcon}\"\n                                                        Command=\"{Binding DataContext.Blackboard.RemoveKeyCommand, Source={StaticResource EditorProxy}}\"\n                                                        CommandParameter=\"{Binding}\"\n                                                        Theme=\"{StaticResource IconButton}\" />\n                                            </StackPanel>\n                                        </Grid>\n                                    </DataTemplate>\n                                </ItemsControl.ItemTemplate>\n                            </ItemsControl>\n                        </ScrollViewer>\n                    </Grid>\n                </Grid>\n            </Border>\n        </Expander>\n    </Grid>\n</Window>\n"
  },
  {
    "path": "Examples/Nodify.StateMachine/MainWindow.xaml.cs",
    "content": "﻿using System.Windows;\nusing Avalonia.Input;\nusing MouseWheelEventArgs = Avalonia.Input.PointerWheelEventArgs;\nusing ModifierKeys = Avalonia.Input.KeyModifiers;\n\nnamespace Nodify.StateMachine\n{\n    public partial class MainWindow : Window\n    {\n        public MainWindow()\n        {\n            InitializeComponent();\n\n            Connector.EnableStickyConnections = true;\n            NodifyEditor.EnableCuttingLinePreview = true;\n\n            EditorGestures.Mappings.Connection.Disconnect.Value = MultiGesture.None;\n            EditorGestures.Mappings.Editor.ZoomModifierKey = ModifierKeys.Control;\n\n            PART_ScrollViewer.AddHandler(PointerWheelChangedEvent, ScrollViewer_MouseWheel, RoutingStrategies.Tunnel);\n            PART_ScrollViewer.AddHandler(KeyDownEvent, ScrollViewer_PreviewKeyDown, RoutingStrategies.Tunnel);\n        }\n\n        private void ScrollViewer_MouseWheel(object sender, MouseWheelEventArgs e)\n        {\n            if (e.KeyModifiers != ModifierKeys.Shift)\n                return;\n\n            var scrollViewer = (ScrollViewer)sender;\n\n            if (e.Delta.Length < 0)\n            {\n                scrollViewer.LineRight();\n            }\n            else\n            {\n                scrollViewer.LineLeft();\n            }\n\n            e.Handled = true;\n        }\n\n        private void ScrollViewer_PreviewKeyDown(object sender, KeyEventArgs e)\n        {\n            if (e.KeyModifiers != ModifierKeys.Shift)\n                return;\n\n            var scrollViewer = (ScrollViewer)sender;\n\n            if (e.Key == Key.PageUp)\n            {\n                scrollViewer.PageLeft();\n                e.Handled = true;\n            }\n            else if (e.Key == Key.PageDown)\n            {\n                scrollViewer.PageRight();\n                e.Handled = true;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.StateMachine/Nodify.StateMachine.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <OutputType>WinExe</OutputType>\n    <TargetFrameworks>net9</TargetFrameworks>\n    <Nullable>enable</Nullable>\n    <AssemblyOriginatorKeyFile>..\\..\\build\\Nodify.snk</AssemblyOriginatorKeyFile>\n    <SignAssembly>true</SignAssembly>\n  </PropertyGroup>\n  <ItemGroup>\n    <ProjectReference Include=\"..\\..\\Nodify\\Nodify.csproj\" />\n    <ProjectReference Include=\"..\\Nodify.Shared\\Nodify.Shared.csproj\" />\n  </ItemGroup>\n\n  <ItemGroup>\n    <PackageReference Include=\"Avalonia\" Version=\"$(AvaloniaVersion)\"/>\n    <PackageReference Include=\"Avalonia.Desktop\" Version=\"$(AvaloniaVersion)\"/>\n    <PackageReference Include=\"Avalonia.Themes.Fluent\" Version=\"$(AvaloniaVersion)\"/>\n    <PackageReference Include=\"Avalonia.Fonts.Inter\" Version=\"$(AvaloniaVersion)\"/>\n    <PackageReference Include=\"Avalonia.Xaml.Behaviors\" Version=\"11.1.0.4\"/>\n    <!--Condition below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.-->\n    <PackageReference Condition=\"'$(Configuration)' == 'Debug'\" Include=\"Avalonia.Diagnostics\" Version=\"$(AvaloniaVersion)\"/>\n  </ItemGroup>\n\n  <ItemGroup>\n    <AdditionalFiles SourceItemGroup=\"AvaloniaXaml\" Include=\"**/*.xaml\"/>\n    <AvaloniaResource Include=\"**/*.xaml\" />\n  </ItemGroup>\n</Project>\n"
  },
  {
    "path": "Examples/Nodify.StateMachine/Program.cs",
    "content": "﻿using System;\nusing Avalonia;\n\nnamespace Nodify.StateMachine;\n\nclass Program\n{\n    // Initialization code. Don't use any Avalonia, third-party APIs or any\n    // SynchronizationContext-reliant code before AppMain is called: things aren't initialized\n    // yet and stuff might break.\n    [STAThread]\n    public static void Main(string[] args) => BuildAvaloniaApp()\n        .StartWithClassicDesktopLifetime(args);\n\n    // Avalonia configuration, don't remove; also used by visual designer.\n    public static AppBuilder BuildAvaloniaApp()\n        => AppBuilder.Configure<App>()\n            .UsePlatformDetect()\n            .WithInterFont()\n            .LogToTrace();\n}"
  },
  {
    "path": "Examples/Nodify.StateMachine/Runner/Actions/CopyKeyAction.cs",
    "content": "﻿using System.Threading.Tasks;\n\nnamespace Nodify.StateMachine\n{\n    [BlackboardItem(\"Copy Key\")]\n    public class CopyKeyAction : IBlackboardAction\n    {\n        [BlackboardProperty(\"Source\", BlackboardKeyType.Object)]\n        public BlackboardProperty Source { get; set; }\n\n        [BlackboardProperty(\"Target\", BlackboardKeyType.Object)]\n        public BlackboardProperty Target { get; set; }\n\n        public Task Execute(Blackboard blackboard)\n        {\n            if (Source != Target && Source.IsKey && Target.IsKey)\n            {\n                var value = blackboard[Source];\n                blackboard[Target] = value;\n            }\n\n            return Task.CompletedTask;\n        }\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.StateMachine/Runner/Actions/SetKeyValueAction.cs",
    "content": "﻿using System.Threading.Tasks;\n\nnamespace Nodify.StateMachine\n{\n    [BlackboardItem(\"Set Value\")]\n    public class SetKeyValueAction : IBlackboardAction\n    {\n        [BlackboardProperty(BlackboardKeyType.Object)]\n        public BlackboardProperty Key { get; set; }\n\n        [BlackboardProperty(BlackboardKeyType.Object, CanChangeType = true)]\n        public BlackboardProperty Value { get; set; }\n\n        public Task Execute(Blackboard blackboard)\n        {\n            var value = blackboard.GetValue<int>(Value);\n            blackboard[Key] = value;\n\n            return Task.CompletedTask;\n        }\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.StateMachine/Runner/Actions/SetStateDelayAction.cs",
    "content": "﻿using System.Threading.Tasks;\n\nnamespace Nodify.StateMachine\n{\n    [BlackboardItem(\"Set State Delay\")]\n    public class SetStateDelayAction : IBlackboardAction\n    {\n        [BlackboardProperty(\"Delay\", BlackboardKeyType.Integer)]\n        public BlackboardProperty Delay { get; set; }\n\n        [BlackboardProperty(\"Success\", BlackboardKeyType.Boolean, Usage = BlackboardKeyUsage.Output)]\n        public BlackboardProperty Success { get; set; }\n\n        public Task Execute(Blackboard blackboard)\n        {\n            var delay = blackboard.GetValue<int>(Delay);\n\n            if (delay.HasValue)\n            {\n                blackboard[DebugBlackboardDecorator.StateDelayKey] = delay;\n            }\n\n            blackboard[Success] = delay.HasValue;\n\n            return Task.CompletedTask;\n        }\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.StateMachine/Runner/Blackboard/Blackboard.cs",
    "content": "﻿using System.Collections.Generic;\n\nnamespace Nodify.StateMachine\n{\n    public class Blackboard\n    {\n        private readonly Dictionary<BlackboardKey, object?> _objects = new Dictionary<BlackboardKey, object?>();\n\n        public virtual IReadOnlyCollection<BlackboardKey> Keys\n            => _objects.Keys;\n\n        public virtual T? GetValue<T>(BlackboardKey key)\n            where T : struct\n        {\n            if (_objects.TryGetValue(key, out var value) && value is T result)\n            {\n                return result;\n            }\n\n            return default;\n        }\n\n        public virtual T? GetObject<T>(BlackboardKey key)\n            where T : class\n        {\n            if (_objects.TryGetValue(key, out var value))\n            {\n                return value as T;\n            }\n\n            return default;\n        }\n\n        public virtual object? GetObject(BlackboardKey key)\n        {\n            if (_objects.TryGetValue(key, out var value))\n            {\n                return value;\n            }\n\n            return default;\n        }\n\n        public virtual void Set(BlackboardKey key, object? value)\n            => _objects[key] = value;\n\n        public virtual bool HasKey(BlackboardKey key)\n            => _objects.ContainsKey(key);\n\n        public virtual void Remove(BlackboardKey key)\n            => _objects.Remove(key);\n\n        public virtual void Clear()\n            => _objects.Clear();\n\n        public void CopyTo(Blackboard newBlackboard)\n        {\n            foreach (var kvp in _objects)\n            {\n                newBlackboard.Set(kvp.Key, kvp.Value);\n            }\n        }\n\n        public object? this[BlackboardKey key]\n        {\n            get => GetObject(key);\n            set => Set(key, value);\n        }\n\n        public T? GetValue<T>(BlackboardProperty value) where T : struct\n            => value.IsValue ? value.GetValue<T>() : GetValue<T>(value.Key);\n\n        public T? GetObject<T>(BlackboardProperty value) where T : class\n            => value.IsValue ? value.GetObject<T>() : GetObject<T>(value.Key);\n\n        public object? GetObject(BlackboardProperty value)\n            => value.IsValue ? value.Value : GetObject(value.Key);\n    }\n\n    public static class BlackboardExtensions\n    {\n        public static bool IsValid(this BlackboardKey key)\n            => key != BlackboardKey.Invalid;\n\n        public static bool IsValid(this BlackboardProperty action)\n            => action != BlackboardProperty.Invalid;\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.StateMachine/Runner/Blackboard/BlackboardConditionSet.cs",
    "content": "﻿using System.Collections.Generic;\nusing System.Threading.Tasks;\n\nnamespace Nodify.StateMachine\n{\n    public enum BooleanOperator\n    {\n        And,\n        Or\n    }\n\n    public class BlackboardConditionSet : IBlackboardCondition\n    {\n        public BlackboardConditionSet(IEnumerable<IBlackboardCondition> conditions, BooleanOperator op)\n        {\n            Conditions = new List<IBlackboardCondition>(conditions);\n            Operator = op;\n        }\n\n        public IReadOnlyList<IBlackboardCondition> Conditions { get; }\n        public BooleanOperator Operator { get; set; }\n\n        public async Task<bool> Evaluate(Blackboard blackboard)\n        {\n            bool result = true;\n\n            if (Operator == BooleanOperator.And)\n            {\n                for (int i = 0; i < Conditions.Count; i++)\n                {\n                    result &= await Conditions[i].Evaluate(blackboard);\n                }\n            }\n            else if (Operator == BooleanOperator.Or)\n            {\n                for (int i = 0; i < Conditions.Count; i++)\n                {\n                    result |= await Conditions[i].Evaluate(blackboard);\n                }\n            }\n\n            return result;\n        }\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.StateMachine/Runner/Blackboard/BlackboardItemAttribute.cs",
    "content": "﻿using System;\n\nnamespace Nodify.StateMachine\n{\n    [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]\n    public sealed class BlackboardItemAttribute : Attribute\n    {\n        public BlackboardItemAttribute(string displayName)\n        {\n            DisplayName = displayName;\n        }\n\n        public string DisplayName { get; }\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.StateMachine/Runner/Blackboard/BlackboardKey.cs",
    "content": "﻿using System;\nusing System.Diagnostics;\n\nnamespace Nodify.StateMachine\n{\n    public enum BlackboardKeyType\n    {\n        Boolean,\n        Integer,\n        Double,\n        String,\n        Object\n    }\n\n    [DebuggerDisplay(\"{Name}: {Type}\")]\n    public readonly struct BlackboardKey : IEquatable<BlackboardKey>\n    {\n        public static BlackboardKey Invalid { get; } = new BlackboardKey();\n\n        public BlackboardKey(string name, BlackboardKeyType type)\n        {\n            Name = name ?? throw new ArgumentException(nameof(name));\n            Type = type;\n        }\n\n        public BlackboardKey(string name) : this(name, BlackboardKeyType.Object)\n        {\n        }\n\n        public readonly string Name;\n        public readonly BlackboardKeyType Type;\n\n        public static implicit operator BlackboardKey(string name)\n            => new BlackboardKey(name);\n\n        public static implicit operator string(BlackboardKey key)\n            => key.Name;\n\n        public override bool Equals(object? obj)\n            => obj is BlackboardKey bk && bk.Equals(this);\n\n        public override int GetHashCode()\n            => Name?.GetHashCode() ?? -1;\n\n        public bool Equals(BlackboardKey other)\n            => other.Name == Name;\n\n        public static bool operator ==(BlackboardKey left, BlackboardKey right)\n            => left.Equals(right);\n\n        public static bool operator !=(BlackboardKey left, BlackboardKey right)\n            => !(left == right);\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.StateMachine/Runner/Blackboard/BlackboardProperty.cs",
    "content": "﻿using System;\nusing System.Diagnostics;\n\nnamespace Nodify.StateMachine\n{\n    [DebuggerDisplay(\"{IsKey ? Key : Value}\")]\n    public struct BlackboardProperty : IEquatable<BlackboardProperty>\n    {\n        public static BlackboardProperty Invalid { get; } = new BlackboardProperty();\n\n        public BlackboardProperty(BlackboardKey key)\n        {\n            Key = key;\n            Value = default;\n        }\n\n        public BlackboardProperty(object? value)\n        {\n            Key = BlackboardKey.Invalid;\n            Value = value;\n        }\n\n        public BlackboardKey Key { get; }\n        public object? Value { get; }\n\n        public bool IsKey => Key.IsValid();\n        public bool IsValue => !IsKey;\n\n        public static implicit operator BlackboardKey(BlackboardProperty action)\n            => action.Key;\n\n        public override bool Equals(object? obj)\n            => obj is BlackboardProperty action && action.Equals(this);\n\n        public override int GetHashCode()\n            => IsKey ? Key.GetHashCode() : Value?.GetHashCode() ?? -1;\n\n        public bool Equals(BlackboardProperty other)\n            => IsKey == other.IsKey && IsValue == other.IsValue && Key == other.Key && Value == other.Value;\n\n        public static bool operator ==(BlackboardProperty left, BlackboardProperty right)\n            => left.Equals(right);\n\n        public static bool operator !=(BlackboardProperty left, BlackboardProperty right)\n            => !(left == right);\n\n        public T? GetValue<T>() where T : struct\n            => Value is T result ? result : default;\n\n        public T? GetObject<T>() where T : class\n            => Value as T;\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.StateMachine/Runner/Blackboard/BlackboardPropertyAttribute.cs",
    "content": "﻿using System;\n\nnamespace Nodify.StateMachine\n{\n    public enum BlackboardKeyUsage\n    {\n        Input,\n        Output\n    }\n\n    /// <summary>\n    /// Properties decorated with this attribute must always be of type <see cref=\"BlackboardProperty\"/>.\n    /// </summary>\n    [AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]\n    public sealed class BlackboardPropertyAttribute : Attribute\n    {\n        /// <summary>\n        /// Properties decorated with this attribute must always be of type <see cref=\"BlackboardProperty\"/>.\n        /// </summary>\n        /// <param name=\"name\">The display name of the key.</param>\n        /// <param name=\"type\">The data type of the value that the key refers to.</param>\n        public BlackboardPropertyAttribute(string? name, BlackboardKeyType type = BlackboardKeyType.Object)\n        {\n            Name = name;\n            Type = type;\n        }\n\n        /// <summary>\n        /// Properties decorated with this attribute must always be of type <see cref=\"BlackboardProperty\"/>.\n        /// </summary>\n        /// <param name=\"type\">The data type of the value that the key refers to.</param>\n        public BlackboardPropertyAttribute(BlackboardKeyType type = BlackboardKeyType.Object) : this(null, type)\n        {\n\n        }\n\n        public string? Name { get; }\n        public BlackboardKeyType Type { get; }\n        public BlackboardKeyUsage Usage { get; set; }\n        public bool CanChangeType { get; set; }\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.StateMachine/Runner/Blackboard/IBlackboardAction.cs",
    "content": "﻿using System.Threading.Tasks;\n\nnamespace Nodify.StateMachine\n{\n    public interface IBlackboardAction\n    {\n        Task Execute(Blackboard blackboard);\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.StateMachine/Runner/Blackboard/IBlackboardCondition.cs",
    "content": "﻿using System.Threading.Tasks;\n\nnamespace Nodify.StateMachine\n{\n    public interface IBlackboardCondition\n    {\n        Task<bool> Evaluate(Blackboard blackboard);\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.StateMachine/Runner/Conditions/AreEqualCondition.cs",
    "content": "﻿using System.Threading.Tasks;\n\nnamespace Nodify.StateMachine\n{\n    [BlackboardItem(\"Are Equal\")]\n    public class AreEqualCondition : IBlackboardCondition\n    {\n        [BlackboardProperty(BlackboardKeyType.Object, CanChangeType = true)]\n        public BlackboardProperty Left { get; set; }\n\n        [BlackboardProperty(BlackboardKeyType.Object, CanChangeType = true)]\n        public BlackboardProperty Right { get; set; }\n\n        public Task<bool> Evaluate(Blackboard blackboard)\n        {\n            var left = blackboard.GetObject(Left);\n            var right = blackboard.GetObject(Right);\n\n            // TODO: Equality\n            return Task.FromResult(Equals(left, right));\n        }\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.StateMachine/Runner/Conditions/HasKeyCondition.cs",
    "content": "﻿using System.Threading.Tasks;\n\nnamespace Nodify.StateMachine\n{\n    [BlackboardItem(\"Has Key\")]\n    public class HasKeyCondition : IBlackboardCondition\n    {\n        [BlackboardProperty(\"Key Name\", BlackboardKeyType.String)]\n        public BlackboardProperty Key { get; set; }\n\n        public Task<bool> Evaluate(Blackboard blackboard)\n        {\n            var keyName = blackboard.GetObject<string>(Key);\n\n            if (keyName != null)\n            {\n                return Task.FromResult(blackboard.HasKey(keyName));\n            }\n\n            return Task.FromResult(false);\n        }\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.StateMachine/Runner/Conditions/HasValueCondition.cs",
    "content": "﻿using System.Threading.Tasks;\n\nnamespace Nodify.StateMachine\n{\n    [BlackboardItem(\"Has Value\")]\n    public class HasValueCondition : IBlackboardCondition\n    {\n        [BlackboardProperty(BlackboardKeyType.Object)]\n        public BlackboardKey Key { get; set; }\n\n        public Task<bool> Evaluate(Blackboard blackboard)\n            => Task.FromResult(blackboard.GetObject(Key) != null);\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.StateMachine/Runner/Debugging/DebugBlackboardDecorator.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\n\nnamespace Nodify.StateMachine\n{\n    public class DebugBlackboardDecorator : Blackboard\n    {\n        public static BlackboardKey StateDelayKey { get; } = \"__state.delay\";\n        public static BlackboardKey TransitionDelayKey { get; } = \"__transition.delay\";\n\n        private Blackboard? _blackboard;\n\n        public event Action<BlackboardKey, object?>? ValueChanged;\n\n        public DebugBlackboardDecorator(Blackboard? blackboard = default)\n            => Attach(blackboard);\n\n        public override IReadOnlyCollection<BlackboardKey> Keys => _blackboard?.Keys ?? Array.Empty<BlackboardKey>();\n\n        public override void Remove(BlackboardKey key)\n            => _blackboard?.Remove(key);\n\n        public override void Clear()\n            => _blackboard?.Clear();\n\n        public override T? GetObject<T>(BlackboardKey key) where T : class\n            => _blackboard?.GetObject<T>(key);\n\n        public override T? GetValue<T>(BlackboardKey key)\n            => _blackboard?.GetValue<T>(key);\n\n        public override void Set(BlackboardKey key, object? value)\n        {\n            _blackboard?.Set(key, value);\n            ValueChanged?.Invoke(key, value);\n        }\n\n        public override bool HasKey(BlackboardKey key)\n            => _blackboard?.HasKey(key) ?? false;\n\n        public override object? GetObject(BlackboardKey key)\n            => _blackboard?.GetObject(key);\n\n        public virtual void Attach(Blackboard? blackboard)\n        {\n            _blackboard = blackboard;\n\n            Set(StateDelayKey, 100);\n        }\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.StateMachine/Runner/Debugging/DebugStateDecorator.cs",
    "content": "﻿using System;\nusing System.Threading.Tasks;\n\nnamespace Nodify.StateMachine\n{\n    public class DebugStateDecorator : State\n    {\n        private readonly State _state;\n\n        public DebugStateDecorator(State state) : base(state.Id, state.Transitions)\n        {\n            _state = state;\n        }\n\n        public override async Task Activate(Blackboard blackboard)\n        {\n            int? delay = blackboard.GetValue<int>(DebugBlackboardDecorator.StateDelayKey);\n\n            await Task.Delay(Math.Max(10, delay ?? 10));\n\n            await _state.Activate(blackboard);\n        }\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.StateMachine/Runner/Debugging/DebugTransitionDecorator.cs",
    "content": "﻿using System.Threading.Tasks;\n\nnamespace Nodify.StateMachine\n{\n    public class DebugTransitionDecorator : Transition\n    {\n        private readonly Transition _transition;\n\n        public DebugTransitionDecorator(Transition transition) : base(transition.From, transition.To)\n        {\n            _transition = transition;\n        }\n\n        public override async Task<bool> CanActivate(Blackboard blackboard)\n        {\n            int? delay = blackboard.GetValue<int>(DebugBlackboardDecorator.TransitionDelayKey);\n\n            if (delay > 0)\n            {\n                await Task.Delay(delay.Value);\n            }\n\n            return await _transition.CanActivate(blackboard);\n        }\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.StateMachine/Runner/State.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Threading.Tasks;\n\nnamespace Nodify.StateMachine\n{\n    public class State\n    {\n        public Guid Id { get; }\n        public IBlackboardAction? Action { get; }\n\n        public State(Guid id, IEnumerable<Transition> transitions, IBlackboardAction? action = default)\n        {\n            Id = id;\n            Action = action;\n            Transitions = new List<Transition>(transitions);\n        }\n\n        public IReadOnlyList<Transition> Transitions { get; }\n\n        public virtual Task Activate(Blackboard blackboard)\n            => Action?.Execute(blackboard) ?? Task.CompletedTask;\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.StateMachine/Runner/StateMachine.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Threading.Tasks;\n\nnamespace Nodify.StateMachine\n{\n    public enum MachineState\n    {\n        Stopped,\n        Running,\n        Paused,\n    }\n\n    public delegate void StateTransitionEventHandler(Guid from, Guid to);\n    public delegate void StateChangedEventHandler(MachineState newStatus);\n\n    public class StateMachine\n    {\n        private readonly Dictionary<Guid, State> _states;\n\n        public State Root { get; }\n        public MachineState? State { get; private set; }\n        public Blackboard Blackboard { get; } = new Blackboard();\n\n        // param = aborted\n        public event StateChangedEventHandler? StateChanged;\n        public event StateTransitionEventHandler? StateTransition;\n\n        public StateMachine(Guid root, IEnumerable<State> states, Blackboard? blackboard = default)\n        {\n            _states = states.ToDictionary(x => x.Id, x => x);\n\n            if (!_states.ContainsKey(root))\n            {\n                throw new ArgumentException(nameof(root));\n            }\n\n            Root = _states[root];\n\n            if (blackboard != null)\n            {\n                Blackboard = blackboard;\n            }\n        }\n\n        public async Task Start()\n        {\n            if (ChangeState(MachineState.Running))\n            {\n                // Skip root state\n                State? previous = Root;\n                State? current = await GetNext(Root);\n\n                while (State != MachineState.Stopped && current != null)\n                {\n                    if (State == MachineState.Paused)\n                    {\n                        await Task.Delay(10);\n                    }\n                    else\n                    {\n                        StateTransition?.Invoke(previous.Id, current.Id);\n                        previous = current;\n\n                        await current.Activate(Blackboard);\n                        current = await GetNext(current);\n                    }\n                }\n\n                ChangeState(MachineState.Stopped);\n            }\n        }\n\n        private async Task<State?> GetNext(State current)\n        {\n            var transitions = current.Transitions;\n            for (int i = 0; i < transitions.Count; i++)\n            {\n                var transition = transitions[i];\n                if (_states.TryGetValue(transition.To, out var result) && await transition.CanActivate(Blackboard))\n                {\n                    return result;\n                }\n            }\n\n            return default;\n        }\n\n        public void Stop()\n            => ChangeState(MachineState.Stopped);\n\n        public void Pause()\n            => ChangeState(MachineState.Paused);\n\n        public void Unpause()\n            => ChangeState(MachineState.Running);\n\n        private bool ChangeState(MachineState newState)\n        {\n            if (newState == MachineState.Running || (State != null && State != newState))\n            {\n                State = newState;\n                StateChanged?.Invoke(newState);\n                return true;\n            }\n\n            return false;\n        }\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.StateMachine/Runner/Transition.cs",
    "content": "﻿using System;\nusing System.Threading.Tasks;\n\nnamespace Nodify.StateMachine\n{\n    public class Transition\n    {\n        public Transition(Guid from, Guid to, IBlackboardCondition? condition = default)\n        {\n            From = from;\n            To = to;\n            Condition = condition;\n        }\n\n        public Guid From { get; }\n        public Guid To { get; }\n        public IBlackboardCondition? Condition { get; }\n\n        public virtual Task<bool> CanActivate(Blackboard blackboard)\n            => Condition?.Evaluate(blackboard) ?? Task.FromResult(true);\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.StateMachine/StateMachineRunnerViewModel.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\n\nnamespace Nodify.StateMachine\n{\n    public class StateMachineRunnerViewModel : ObservableObject\n    {\n        private StateMachine? _stateMachine;\n        private StateViewModel? _activeState;\n        private TransitionViewModel? _activeTransition;\n        private readonly DebugBlackboardDecorator _debugger = new DebugBlackboardDecorator();\n        private readonly Blackboard _original = new Blackboard();\n\n        protected StateMachineViewModel StateMachineViewModel { get; }\n\n        private MachineState _state;\n        public MachineState State\n        {\n            get => _state;\n            protected set => SetProperty(ref _state, value);\n        }\n\n        private int _nodesVisited;\n        public int NodesVisited\n        {\n            get => _nodesVisited;\n            protected set => SetProperty(ref _nodesVisited, value);\n        }\n\n        public StateMachineRunnerViewModel(StateMachineViewModel stateMachineViewModel)\n        {\n            StateMachineViewModel = stateMachineViewModel;\n            _debugger.ValueChanged += OnBlackboardKeyValueChanged;\n        }\n\n        private void OnBlackboardKeyValueChanged(BlackboardKey key, object? newValue)\n        {\n            if (_stateMachine != null && _stateMachine.State != MachineState.Stopped)\n            {\n                var existing = StateMachineViewModel.Blackboard.Keys.FirstOrDefault(k => k.Name == key.Name && k.Type == key.Type);\n                if (existing != null)\n                {\n                    existing.Value = newValue;\n                }\n            }\n        }\n\n        #region State Machine Actions\n\n        public async void Start()\n        {\n            NodesVisited = 0;\n\n            _stateMachine = new StateMachine(StateMachineViewModel.States[0].Id, CreateStates(StateMachineViewModel.States), CreateBlackboard(StateMachineViewModel.Blackboard));\n\n            _stateMachine.StateTransition += HandleStateTransition;\n            _stateMachine.StateChanged += HandleStateChange;\n\n            await _stateMachine.Start();\n        }\n\n        public void Stop()\n        {\n            _stateMachine?.Stop();\n            _stateMachine = null;\n        }\n\n        private void HandleStateTransition(Guid from, Guid to)\n        {\n            NodesVisited++;\n\n            SetActiveStateAndTransition(false);\n\n            _activeTransition = StateMachineViewModel.Transitions.FirstOrDefault(t => t.Source.Id == from);\n            _activeState = StateMachineViewModel.States.FirstOrDefault(st => st.Id == to);\n\n            SetActiveStateAndTransition(true);\n        }\n\n        private void SetActiveStateAndTransition(bool value)\n        {\n            if (_activeState != null)\n            {\n                _activeState.IsActive = value;\n            }\n\n            if (_activeTransition != null)\n            {\n                _activeTransition.IsActive = value;\n            }\n        }\n\n        private void HandleStateChange(MachineState newState)\n        {\n            if (newState == MachineState.Stopped)\n            {\n                SetActiveStateAndTransition(false);\n                ResetBlackboardToOriginal();\n            }\n\n            State = newState;\n        }\n\n        private void ResetBlackboardToOriginal()\n        {\n            var keys = StateMachineViewModel.Blackboard.Keys;\n            for (int i = 0; i < keys.Count; i++)\n            {\n                var key = keys[i];\n                key.Value = _original.GetObject(key.Name);\n            }\n        }\n\n        public void TogglePause()\n        {\n            if (State == MachineState.Paused)\n            {\n                _stateMachine?.Unpause();\n            }\n            else if (State != MachineState.Stopped)\n            {\n                _stateMachine?.Pause();\n            }\n        }\n\n        #endregion\n\n        #region Initialize State Machine\n\n        private IEnumerable<State> CreateStates(IEnumerable<StateViewModel> states)\n            => states.Select(s => new DebugStateDecorator(new State(s.Id, CreateTransitions(s), CreateAction(s.Action))));\n\n        private IEnumerable<Transition> CreateTransitions(StateViewModel state)\n        {\n            var transitions = StateMachineViewModel.Transitions.Where(t => t.Source == state).ToList();\n            var result = new List<Transition>(transitions.Count);\n\n            for (int i = 0; i < transitions.Count; i++)\n            {\n                var transition = transitions[i];\n                var tr = new Transition(transition.Source.Id, transition.Target.Id, CreateCondition(transition.Condition));\n                result.Add(new DebugTransitionDecorator(tr));\n            }\n\n            return result;\n        }\n\n        private IBlackboardCondition? CreateCondition(BlackboardItemViewModel? condition)\n        {\n            if (condition?.Type != null && typeof(IBlackboardCondition).IsAssignableFrom(condition.Type))\n            {\n                // TODO: DI Container\n                var result = (IBlackboardCondition?)Activator.CreateInstance(condition.Type);\n\n                InitializeKeys(condition.Input, result, condition.Type);\n\n                return result;\n            }\n\n            return default;\n        }\n\n        private IBlackboardAction? CreateAction(BlackboardItemViewModel? action)\n        {\n            if (action?.Type != null && typeof(IBlackboardAction).IsAssignableFrom(action.Type))\n            {\n                // TODO: DI Container\n                var result = (IBlackboardAction?)Activator.CreateInstance(action.Type);\n\n                InitializeKeys(action.Input, result, action.Type);\n                InitializeKeys(action.Output, result, action.Type);\n\n                return result;\n            }\n\n            return default;\n        }\n\n        private void InitializeKeys(NodifyObservableCollection<BlackboardKeyViewModel> keys, object? instance, Type type)\n        {\n            for (int i = 0; i < keys.Count; i++)\n            {\n                var vm = keys[i];\n                var key = CreateActionValue(vm);\n\n                // TODO: Property cache\n                if (vm.PropertyName != null)\n                {\n                    var prop = type.GetProperty(vm.PropertyName);\n\n                    if (prop?.CanWrite ?? false)\n                    {\n                        prop.SetValue(instance, key);\n                    }\n                }\n            }\n        }\n\n        private Blackboard CreateBlackboard(BlackboardViewModel blackboard)\n        {\n            Blackboard result = new Blackboard();\n            for (int i = 0; i < blackboard.Keys.Count; i++)\n            {\n                var key = blackboard.Keys[i];\n                if (!string.IsNullOrWhiteSpace(key.Name))\n                {\n                    result.Set(new BlackboardKey(key.Name, key.Type), key.Value);\n                }\n            }\n\n            result.CopyTo(_original);\n\n            _debugger.Attach(result);\n            return _debugger;\n        }\n\n        private BlackboardProperty CreateActionValue(BlackboardKeyViewModel key)\n        {\n            if (key.Value is BlackboardKeyViewModel bkv)\n            {\n                return new BlackboardProperty(new BlackboardKey(bkv.Name, bkv.Type));\n            }\n\n            return new BlackboardProperty(key.Value);\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.StateMachine/StateMachineViewModel.cs",
    "content": "﻿using System;\nusing System.Linq;\nusing System.Windows;\n\nnamespace Nodify.StateMachine\n{\n    public class StateMachineViewModel : ObservableObject\n    {\n        public StateMachineViewModel()\n        {\n            PendingTransition = new TransitionViewModel();\n            Runner = new StateMachineRunnerViewModel(this);\n\n            Blackboard = new BlackboardViewModel()\n            {\n                Actions = new NodifyObservableCollection<BlackboardItemReferenceViewModel>(BlackboardDescriptor.GetAvailableItems<IBlackboardAction>()),\n                Conditions = new NodifyObservableCollection<BlackboardItemReferenceViewModel>(BlackboardDescriptor.GetAvailableItems<IBlackboardCondition>())\n            };\n\n            Transitions.WhenAdded(c => c.Source.Transitions.Add(c.Target))\n            .WhenRemoved(c => c.Source.Transitions.Remove(c.Target))\n            .WhenCleared(c => c.ForEach(i =>\n            {\n                i.Source.Transitions.Clear();\n                i.Target.Transitions.Clear();\n            }));\n\n            States.WhenAdded(x => x.Graph = this)\n                 .WhenRemoved(x => DisconnectState(x))\n                 .WhenCleared(x =>\n                 {\n                     Transitions.Clear();\n                     OnCreateDefaultNodes();\n                 });\n\n            OnCreateDefaultKeys();\n            OnCreateDefaultNodes();\n\n            RenameStateCommand = new RequeryCommand(() => SelectedStates[0].IsRenaming = true, () => SelectedStates.Count == 1 && SelectedStates[0].IsEditable);\n            DisconnectStateCommand = new RequeryCommand<StateViewModel>(x => DisconnectState(x), x => !IsRunning && x.Transitions.Count > 0);\n            DisconnectSelectionCommand = new RequeryCommand(() => SelectedStates.ForEach(x => DisconnectState(x)), () => !IsRunning && SelectedStates.Count > 0 && Transitions.Count > 0);\n            DeleteSelectionCommand = new RequeryCommand(() => SelectedStates.ToList().ForEach(x => x.IsEditable.Then(() => States.Remove(x))), () => !IsRunning && (SelectedStates.Count > 1 || (SelectedStates.Count == 1 && SelectedStates[0].IsEditable)));\n\n            AddStateCommand = new RequeryCommand<Point>(p => States.Add(new StateViewModel\n            {\n                Name = \"New State\",\n                IsRenaming = true,\n                Location = p,\n                ActionReference = Blackboard.Actions.Count > 0 ? Blackboard.Actions[0] : null\n            }), p => !IsRunning);\n\n            CreateTransitionCommand = new DelegateCommand<(object Source, object? Target)>(s => Transitions.Add(new TransitionViewModel\n            {\n                Source = (StateViewModel)s.Source,\n                Target = (StateViewModel)s.Target!\n            }), s => !IsRunning && s.Source is StateViewModel source && s.Target is StateViewModel target && target != s.Source && target != States[0] && !source.Transitions.Contains(s.Target));\n\n            DeleteTransitionCommand = new RequeryCommand<TransitionViewModel>(t => Transitions.Remove(t), t => !IsRunning);\n\n            RunCommand = new RequeryCommand(() => IsRunning.Then(Runner.Stop).Else(Runner.Start), () => Transitions.Count > 0);\n            PauseCommand = new RequeryCommand(Runner.TogglePause, () => IsRunning);\n        }\n\n        private NodifyObservableCollection<StateViewModel> _states = new NodifyObservableCollection<StateViewModel>();\n        public NodifyObservableCollection<StateViewModel> States\n        {\n            get => _states;\n            set => SetProperty(ref _states, value);\n        }\n\n        private NodifyObservableCollection<StateViewModel> _selectedStates = new NodifyObservableCollection<StateViewModel>();\n        public NodifyObservableCollection<StateViewModel> SelectedStates\n        {\n            get => _selectedStates;\n            set => SetProperty(ref _selectedStates, value);\n        }\n\n        private NodifyObservableCollection<TransitionViewModel> _connections = new NodifyObservableCollection<TransitionViewModel>();\n        public NodifyObservableCollection<TransitionViewModel> Transitions\n        {\n            get => _connections;\n            set => SetProperty(ref _connections, value);\n        }\n\n        private StateViewModel? _selectedState;\n        public StateViewModel? SelectedState\n        {\n            get => _selectedState;\n            set => SetProperty(ref _selectedState, value);\n        }\n\n        private string? _name = \"State Machine\";\n        public string? Name\n        {\n            get => _name;\n            set => SetProperty(ref _name, value);\n        }\n\n        public bool IsRunning => Runner.State != MachineState.Stopped;\n        public bool IsPaused => Runner.State == MachineState.Paused;\n\n        public TransitionViewModel PendingTransition { get; }\n        public StateMachineRunnerViewModel Runner { get; }\n        public BlackboardViewModel Blackboard { get; }\n\n        public INodifyCommand DeleteTransitionCommand { get; }\n        public INodifyCommand DeleteSelectionCommand { get; }\n        public INodifyCommand DisconnectStateCommand { get; }\n        public INodifyCommand DisconnectSelectionCommand { get; }\n        public INodifyCommand RenameStateCommand { get; }\n        public INodifyCommand AddStateCommand { get; }\n        public INodifyCommand CreateTransitionCommand { get; }\n        public INodifyCommand RunCommand { get; }\n        public INodifyCommand PauseCommand { get; }\n\n        public void DisconnectState(StateViewModel state)\n        {\n            var transitions = Transitions.Where(t => t.Source == state || t.Target == state).ToList();\n            transitions.ForEach(t => Transitions.Remove(t));\n        }\n\n        protected virtual void OnCreateDefaultNodes()\n        {\n            States.Insert(0, new StateViewModel\n            {\n                Name = \"Enter\",\n                Location = new Point(100, 100),\n                IsEditable = false\n            });\n\n            var currentDelayKey = Blackboard.Keys.First(k => k.Name == \"Current Delay\");\n            var originalDelayKey = Blackboard.Keys.First(k => k.Name == \"Original Delay\");\n            var welcomeKey = Blackboard.Keys.First(k => k.Name == \"Welcome\");\n\n            States.Add(new StateViewModel\n            {\n                Name = \"Set delay value\",\n                Location = new Point(300, 100),\n                ActionReference = Blackboard.Actions.FirstOrDefault(a => a.Type == typeof(SetKeyValueAction))\n            });\n\n            States[1].Action!.Input[0].Value = currentDelayKey;\n            States[1].Action!.Input[1].ValueIsKey = false;\n            States[1].Action!.Input[1].Type = BlackboardKeyType.Integer;\n            States[1].Action!.Input[1].Value = 100;\n\n            States.Add(new StateViewModel\n            {\n                Name = \"Set new delay\",\n                Location = new Point(380, 250),\n                ActionReference = Blackboard.Actions.FirstOrDefault(a => a.Type == typeof(SetStateDelayAction))\n            });\n\n            States[2].Action!.Input[0].Value = currentDelayKey;\n\n            States.Add(new StateViewModel\n            {\n                Name = \"Reset delay\",\n                Location = new Point(300, 350),\n                ActionReference = Blackboard.Actions.FirstOrDefault(a => a.Type == typeof(CopyKeyAction))\n            });\n\n            States[3].Action!.Input[0].Value = originalDelayKey;\n            States[3].Action!.Input[1].Value = currentDelayKey;\n\n            States.Add(new StateViewModel\n            {\n                Name = \"Set original delay\",\n                Location = new Point(200, 250),\n                ActionReference = Blackboard.Actions.FirstOrDefault(a => a.Type == typeof(SetStateDelayAction))\n            });\n\n            States[4].Action!.Input[0].Value = originalDelayKey;\n\n            Transitions.Add(new TransitionViewModel\n            {\n                Source = States[0],\n                Target = States[1],\n                ConditionReference = Blackboard.Conditions.FirstOrDefault(c => c.Type == typeof(HasKeyCondition))\n            });\n\n            Transitions[0].Condition!.Input[0].Value = welcomeKey;\n\n            Transitions.Add(new TransitionViewModel\n            {\n                Source = States[1],\n                Target = States[2],\n                ConditionReference = Blackboard.Conditions.FirstOrDefault(c => c.Type == typeof(AreEqualCondition))\n            });\n\n            Transitions[1].Condition!.Input[0].Value = welcomeKey;\n            Transitions[1].Condition!.Input[1].ValueIsKey = false;\n            Transitions[1].Condition!.Input[1].Type = BlackboardKeyType.String;\n            Transitions[1].Condition!.Input[1].Value = currentDelayKey.Name;\n\n            Transitions.Add(new TransitionViewModel\n            {\n                Source = States[2],\n                Target = States[3]\n            });\n\n            Transitions.Add(new TransitionViewModel\n            {\n                Source = States[3],\n                Target = States[4]\n            });\n\n            Transitions.Add(new TransitionViewModel\n            {\n                Source = States[4],\n                Target = States[1]\n            });\n        }\n\n        protected virtual void OnCreateDefaultKeys()\n        {\n            Blackboard.Keys.Add(new BlackboardKeyViewModel\n            {\n                Name = \"Current Delay\",\n                Type = BlackboardKeyType.Integer,\n                Value = 1000\n            });\n\n            Blackboard.Keys.Add(new BlackboardKeyViewModel\n            {\n                Name = \"Original Delay\",\n                Type = BlackboardKeyType.Integer,\n                Value = 1000\n            });\n\n            Blackboard.Keys.Add(new BlackboardKeyViewModel\n            {\n                Name = \"Welcome\",\n                Type = BlackboardKeyType.String,\n                Value = \"Current Delay\"\n            });\n        }\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.StateMachine/StateViewModel.cs",
    "content": "﻿using System;\nusing System.Windows;\n\nnamespace Nodify.StateMachine\n{\n    public class StateViewModel : ObservableObject\n    {\n        public Guid Id { get; }\n\n        public StateViewModel(Guid id)\n            => Id = id;\n\n        public StateViewModel() : this(Guid.NewGuid()) { }\n\n        // TODO: Can remove when auto layout is added\n        private Point _location;\n        public Point Location\n        {\n            get => _location;\n            set => SetProperty(ref _location, value);\n        }\n\n        private Point _anchor;\n        public Point Anchor\n        {\n            get => _anchor;\n            set => SetProperty(ref _anchor, value);\n        }\n\n        private Size _size;\n        public Size Size\n        {\n            get => _size;\n            set => SetProperty(ref _size, value);\n        }\n\n        private string? _name;\n        public string? Name\n        {\n            get => _name;\n            set => SetProperty(ref _name, value);\n        }\n\n        private bool _isRenaming;\n        public bool IsRenaming\n        {\n            get => _isRenaming;\n            set => SetProperty(ref _isRenaming, value);\n        }\n\n        private bool _isActive;\n        public bool IsActive\n        {\n            get => _isActive;\n            set => SetProperty(ref _isActive, value);\n        }\n\n        private BlackboardItemReferenceViewModel? _actionReference;\n        public BlackboardItemReferenceViewModel? ActionReference\n        {\n            get => _actionReference;\n            set\n            {\n                if (SetProperty(ref _actionReference, value))\n                {\n                    SetAction(_actionReference);\n                }\n            }\n        }\n\n        public BlackboardItemViewModel? Action { get; private set; }\n\n        public bool IsEditable { get; set; } = true;\n        public StateMachineViewModel Graph { get; internal set; } = default!;\n        public NodifyObservableCollection<StateViewModel> Transitions { get; } = new NodifyObservableCollection<StateViewModel>();\n\n        private void SetAction(BlackboardItemReferenceViewModel? actionRef)\n        {\n            Action = BlackboardDescriptor.GetItem(actionRef);\n\n            OnPropertyChanged(nameof(Action));\n        }\n    }\n}\n"
  },
  {
    "path": "Examples/Nodify.StateMachine/Themes/Brushes.xaml",
    "content": "﻿<ResourceDictionary xmlns=\"https://github.com/avaloniaui\"\n                    xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n                    xmlns:o=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation/options\">\n    \n    <SolidColorBrush x:Key=\"PanelBackgroundBrush\"\n                     Color=\"{DynamicResource PanelBackgroundColor}\" />\n    \n    <SolidColorBrush x:Key=\"ActiveStateBrush\"\n                     Color=\"{DynamicResource ActiveStateColor}\" />\n    \n    <SolidColorBrush x:Key=\"ReadOnlyStateBrush\"\n                     Color=\"{DynamicResource ReadOnlyStateColor}\" />\n    \n</ResourceDictionary>"
  },
  {
    "path": "Examples/Nodify.StateMachine/Themes/Dark.xaml",
    "content": "﻿<ResourceDictionary xmlns=\"https://github.com/avaloniaui\"\n                    xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\">\n    \n    <ResourceDictionary.MergedDictionaries>\n        <ResourceInclude Source=\"Brushes.xaml\" />\n    </ResourceDictionary.MergedDictionaries>\n    \n    <Color x:Key=\"PanelBackgroundColor\">#2D2D30</Color>\n\n    <Color x:Key=\"ActiveStateColor\">#8DD28A</Color>\n    <Color x:Key=\"ReadOnlyStateColor\">#E6AF86</Color>\n    \n</ResourceDictionary>"
  },
  {
    "path": "Examples/Nodify.StateMachine/Themes/Light.xaml",
    "content": "﻿<ResourceDictionary xmlns=\"https://github.com/avaloniaui\"\n                    xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\">\n\n    <ResourceDictionary.MergedDictionaries>\n        <ResourceInclude Source=\"Brushes.xaml\" />\n    </ResourceDictionary.MergedDictionaries>\n    \n    <Color x:Key=\"PanelBackgroundColor\">#E9EEFE</Color>\n\n    <Color x:Key=\"ActiveStateColor\">#8DD28A</Color>\n    <Color x:Key=\"ReadOnlyStateColor\">#E6AF86</Color>\n\n</ResourceDictionary>"
  },
  {
    "path": "Examples/Nodify.StateMachine/Themes/Nodify.xaml",
    "content": "﻿<ResourceDictionary xmlns=\"https://github.com/avaloniaui\"\n                    xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\">\n    \n    <ResourceDictionary.MergedDictionaries>\n        <ResourceInclude Source=\"Brushes.xaml\" />\n    </ResourceDictionary.MergedDictionaries>\n\n    <Color x:Key=\"PanelBackgroundColor\">#2A1B47</Color>\n\n    <Color x:Key=\"ActiveStateColor\">#A6D99C</Color>\n    <Color x:Key=\"ReadOnlyStateColor\">#FD5618</Color>\n    \n</ResourceDictionary>"
  },
  {
    "path": "Examples/Nodify.StateMachine/TransitionViewModel.cs",
    "content": "﻿namespace Nodify.StateMachine\n{\n    public class TransitionViewModel : ObservableObject\n    {\n        private StateViewModel _source = default!;\n        public StateViewModel Source\n        {\n            get => _source;\n            set => SetProperty(ref _source, value);\n        }\n\n        private StateViewModel _target = default!;\n        public StateViewModel Target\n        {\n            get => _target;\n            set => SetProperty(ref _target, value);\n        }\n\n        private BlackboardItemReferenceViewModel? _conditionReference;\n        public BlackboardItemReferenceViewModel? ConditionReference\n        {\n            get => _conditionReference;\n            set\n            {\n                if (SetProperty(ref _conditionReference, value))\n                {\n                    SetCondition(_conditionReference);\n                }\n            }\n        }\n\n        public BlackboardItemViewModel? Condition { get; private set; }\n\n        private bool _isActive;\n        public bool IsActive\n        {\n            get => _isActive;\n            set => SetProperty(ref _isActive, value);\n        }\n\n        private void SetCondition(BlackboardItemReferenceViewModel? conditionRef)\n        {\n            Condition = BlackboardDescriptor.GetItem(conditionRef);\n\n            OnPropertyChanged(nameof(Condition));\n        }\n    }\n}\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2020 Miroiu Emanuel\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "Nodify/Compatibility/Attributes/ContentPropertyAttribute.cs",
    "content": "namespace Nodify.Compatibility;\n\ninternal class ContentPropertyAttribute : Attribute\n{\n    public ContentPropertyAttribute(string name)\n    {\n        // in Avalonia use [Content] attribute on the property itself\n    }\n}"
  },
  {
    "path": "Nodify/Compatibility/Attributes/StyleTypedPropertyAttribute.cs",
    "content": "namespace Nodify.Compatibility;\n\n[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]\ninternal class StyleTypedPropertyAttribute : Attribute\n{\n    public string Property { get; set; }\n\n    public Type StyleTargetType { get; set; }\n}"
  },
  {
    "path": "Nodify/Compatibility/Commands/ApplicationCommands.cs",
    "content": "using System.Runtime.InteropServices;\n\nnamespace Nodify.Compatibility;\n\ninternal static class ApplicationCommands\n{\n    private static readonly KeyModifiers PlatformCommandKey = GetPlatformCommandKey();\n\n    public static RoutedUICommand Delete { get; } = new RoutedUICommand(\"Delete\", nameof(Delete), typeof(ApplicationCommands), new InputKeyGesture(Key.Delete));\n    public static RoutedUICommand Copy { get; } = new RoutedUICommand(\"Copy\", nameof(Copy), typeof(ApplicationCommands), new InputKeyGesture(Key.C, PlatformCommandKey));\n    public static RoutedUICommand Cut { get; } = new RoutedUICommand(\"Cut\", nameof(Cut), typeof(ApplicationCommands), new InputKeyGesture(Key.X, PlatformCommandKey));\n    public static RoutedUICommand Paste { get; } = new RoutedUICommand(\"Paste\", nameof(Paste), typeof(ApplicationCommands), new InputKeyGesture(Key.V, PlatformCommandKey));\n    public static RoutedUICommand SelectAll { get; } = new RoutedUICommand(\"Select all\", nameof(SelectAll), typeof(ApplicationCommands), new InputKeyGesture(Key.A, PlatformCommandKey));\n    public static RoutedUICommand Undo { get; } = new RoutedUICommand(\"Undo\", nameof(Undo), typeof(ApplicationCommands), new InputKeyGesture(Key.Z, PlatformCommandKey));\n    public static RoutedUICommand Redo { get; } = new RoutedUICommand(\"Redo\", nameof(Redo), typeof(ApplicationCommands), new InputKeyGesture(Key.Y, PlatformCommandKey));\n    public static RoutedUICommand Find { get; } = new RoutedUICommand(\"Find\", nameof(Find), typeof(ApplicationCommands), new InputKeyGesture(Key.F, PlatformCommandKey));\n    public static RoutedUICommand Replace { get; } = new RoutedUICommand(\"Replace\", nameof(Replace), typeof(ApplicationCommands), GetReplaceKeyGesture());\n        \n    private static KeyModifiers GetPlatformCommandKey()\n    {            \n        if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))\n        {\n            return KeyModifiers.Meta;\n        }\n\n        return KeyModifiers.Control;\n    }\n\n    private static InputKeyGesture GetReplaceKeyGesture()\n    {\n        if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))\n        {\n            return new InputKeyGesture(Key.F, KeyModifiers.Meta | KeyModifiers.Alt);\n        }\n\n        return new InputKeyGesture(Key.H, PlatformCommandKey);\n    }\n}"
  },
  {
    "path": "Nodify/Compatibility/Commands/CanExecuteRoutedEventArgs.cs",
    "content": "namespace Nodify.Compatibility;\n\npublic sealed class CanExecuteRoutedEventArgs : RoutedEventArgs\n{\n    public ICommand Command { get; }\n\n    public object? Parameter { get; }\n\n    public bool CanExecute { get; set; }\n\n    public CanExecuteRoutedEventArgs(ICommand command, object? parameter)\n    {\n        Command = command ?? throw new ArgumentNullException(nameof(command));\n        Parameter = parameter;\n        RoutedEvent = RoutedCommand.CanExecuteEvent;\n    }\n}"
  },
  {
    "path": "Nodify/Compatibility/Commands/CommandBinding.cs",
    "content": "namespace Nodify.Compatibility;\n\ninternal class CommandBinding\n{\n    public CommandBinding(RoutedCommand command,\n        EventHandler<ExecutedRoutedEventArgs> executed = null,\n        EventHandler<CanExecuteRoutedEventArgs> canExecute = null)\n    {\n        Command = command;\n        if (executed != null) Executed += executed;\n        if (canExecute != null) CanExecute += canExecute;\n    }\n\n    public RoutedCommand Command { get; }\n\n    public event EventHandler<CanExecuteRoutedEventArgs> CanExecute;\n\n    public event EventHandler<ExecutedRoutedEventArgs> Executed;\n\n    internal bool DoCanExecute(object sender, CanExecuteRoutedEventArgs e)\n    {\n        if (e.Handled) return true;\n\n        var canExecute = CanExecute;\n        if (canExecute == null)\n        {\n            if (Executed != null)\n            {\n                e.Handled = true;\n                e.CanExecute = true;\n            }\n        }\n        else\n        {\n            canExecute(sender, e);\n\n            if (e.CanExecute)\n            {\n                e.Handled = true;\n            }\n        }\n\n        return e.CanExecute;\n    }\n\n    internal bool DoExecuted(object sender, ExecutedRoutedEventArgs e)\n    {\n        if (!e.Handled)\n        {\n            var executed = Executed;\n\n            if (executed != null)\n            {\n                if (DoCanExecute(sender, new CanExecuteRoutedEventArgs(e.Command, e.Parameter)))\n                {\n                    executed(sender, e);\n                    e.Handled = true;\n                    return true;\n                }\n            }\n        }\n\n        return false;\n    }\n}"
  },
  {
    "path": "Nodify/Compatibility/Commands/CommandManager.cs",
    "content": "namespace Nodify.Compatibility;\n\ninternal static class CommandManager\n{\n    public static Dictionary<Type, List<CommandBinding>> staticCommandBindings = new Dictionary<Type, List<CommandBinding>>();\n        \n    public static void RegisterClassCommandBinding(Type type, CommandBinding cmd)\n    {\n        if (!staticCommandBindings.ContainsKey(type))\n        {\n            staticCommandBindings.Add(type, new List<CommandBinding>());\n        }\n        staticCommandBindings[type].Add(cmd);\n    }\n\n    public static event EventHandler? RequerySuggested;\n        \n    public static void InvalidateRequerySuggested()\n    {\n        RequerySuggested?.Invoke(null, EventArgs.Empty);\n    }\n\n    static CommandManager()\n    {\n        InputElement.GotFocusEvent.AddClassHandler<Interactive>(GotFocusEventHandler);\n        InputElement.LostFocusEvent.AddClassHandler<Interactive>(LostFocusEventHandler);\n        InputElement.PointerPressedEvent.AddClassHandler<Interactive>(PointerPressedEventHandler);\n        InputElement.PointerReleasedEvent.AddClassHandler<Interactive>(PointerReleasedEventHandler);\n        InputElement.KeyUpEvent.AddClassHandler<Interactive>(KeyUpEventHandler);\n        InputElement.KeyDownEvent.AddClassHandler<Interactive>(KeyDownEventHandler);\n    }\n\n    private static void KeyDownEventHandler(Interactive sender, KeyEventArgs e)\n    {\n        InvalidateRequerySuggested();\n    }\n\n    private static void KeyUpEventHandler(Interactive sender, KeyEventArgs e)\n    {\n        InvalidateRequerySuggested();\n    }\n\n    private static void PointerReleasedEventHandler(Interactive sender, PointerReleasedEventArgs e)\n    {\n        InvalidateRequerySuggested();\n    }\n\n    private static void PointerPressedEventHandler(Interactive sender, PointerPressedEventArgs e)\n    {\n        InvalidateRequerySuggested();\n    }\n\n    private static void GotFocusEventHandler(Interactive sender, GotFocusEventArgs e)\n    {\n        InvalidateRequerySuggested();\n    }\n\n    private static void LostFocusEventHandler(Interactive sender, RoutedEventArgs e)\n    {\n        InvalidateRequerySuggested();\n    }\n}"
  },
  {
    "path": "Nodify/Compatibility/Commands/ExecutedRoutedEventArgs.cs",
    "content": "namespace Nodify.Compatibility;\n\npublic sealed class ExecutedRoutedEventArgs : RoutedEventArgs\n{\n    public ICommand Command { get; }\n\n    public object? Parameter { get; }\n\n    internal ExecutedRoutedEventArgs(ICommand command, object? parameter)\n    {\n        Command = command ?? throw new ArgumentNullException(nameof(command));\n        Parameter = parameter;\n        RoutedEvent = RoutedCommand.ExecutedEvent;\n    }\n}"
  },
  {
    "path": "Nodify/Compatibility/Commands/RoutedCommand.cs",
    "content": "namespace Nodify.Compatibility;\n\n// Ported from https://github.com/AvaloniaUI/AvaloniaEdit/blob/master/src/AvaloniaEdit/RoutedCommand.cs\npublic class RoutedCommand : ICommand\n{\n    private static IInputElement? _focusedElement;\n\n    public string Name { get; }\n    public Type? OwnerType { get; }\n    public InputGestureCollection InputGestures { get; }\n        \n    public RoutedCommand(string name, Type? ownerType)\n        : this(name, ownerType, new InputGestureCollection())\n    {\n    }\n        \n    public RoutedCommand(string name, Type? ownerType, InputGesture inputGesture)\n        : this(name, ownerType, new InputGestureCollection { inputGesture })\n    {\n    }\n\n    public RoutedCommand(string name, Type? ownerType, InputGestureCollection inputGestures)\n    {\n        Name = name;\n        OwnerType = ownerType;\n        InputGestures = inputGestures;\n    }\n\n    static RoutedCommand()\n    {\n        CanExecuteEvent.AddClassHandler<Interactive>(CanExecuteEventHandler);\n        ExecutedEvent.AddClassHandler<Interactive>(ExecutedEventHandler);\n        InputElement.GotFocusEvent.AddClassHandler<Interactive>(GotFocusEventHandler);\n        InputElement.LostFocusEvent.AddClassHandler<Interactive>(LostFocusEventHandler);\n        Popup.IsOpenProperty.Changed.AddClassHandler<Popup>(PopupIsOpenChanged);\n    }\n\n    private static void PopupIsOpenChanged(Popup popup, AvaloniaPropertyChangedEventArgs e)\n    {\n        if (e.GetNewValue<bool>())\n            _focusedElement = popup;\n        else if (ReferenceEquals(_focusedElement, popup) || popup.IsVisualAncestorOf(_focusedElement as Visual))\n            _focusedElement = null;\n        CommandManager.InvalidateRequerySuggested();\n    }\n\n    private static void GotFocusEventHandler(Interactive focused, GotFocusEventArgs e)\n    {\n        _focusedElement = focused as IInputElement;\n    }\n\n    private static void LostFocusEventHandler(Interactive arg1, RoutedEventArgs arg2)\n    {\n        if (ReferenceEquals(_focusedElement, arg1))\n            _focusedElement = null;\n    }\n\n    private static void CanExecuteEventHandler(Interactive? control, CanExecuteRoutedEventArgs args)\n    {\n        while (control != null)\n        {\n            if (GetCommandBindings(control) is {} commandBindings)\n            {\n                var command = commandBindings.Where(c => c != null).FirstOrDefault(c => c.Command == args.Command);\n                if (command != null)\n                {\n                    args.CanExecute = command.DoCanExecute(control, args);\n                    break;\n                }\n            }\n\n            if (CommandManager.staticCommandBindings.TryGetValue(control.GetType(), out var commands))\n            {\n                var command = commands.Where(c => c != null).FirstOrDefault(c => c.Command == args.Command);\n                if (command != null)\n                {\n                    args.CanExecute = command.DoCanExecute(control, args);\n                    break;\n                }\n            }\n\n            if (control is PopupRoot popup)\n                control = ((IHostedVisualTreeRoot)popup).Host as Interactive;\n            else\n                control = control.Parent as Interactive;\n        }\n    }\n\n    private static void ExecutedEventHandler(Interactive? control, ExecutedRoutedEventArgs args)\n    {\n        while (control != null)\n        {\n            if (GetCommandBindings(control) is {} commandBindings)\n            {\n                var command = commandBindings.Where(c => c != null).FirstOrDefault(c => c.Command == args.Command);\n                if (command != null)\n                {\n                    command.DoExecuted(control, args);\n                    break;\n                }\n            }\n\n            if (CommandManager.staticCommandBindings.TryGetValue(control.GetType(), out var commands))\n            {\n                var command = commands.Where(c => c != null).FirstOrDefault(c => c.Command == args.Command);\n                if (command != null)\n                {\n                    command.DoExecuted(control, args);\n                    break;\n                }\n            }\n\n            if (control is PopupRoot popup)\n                control = ((IHostedVisualTreeRoot)popup).Host as Interactive;\n            else\n                control = control.Parent as Interactive;\n        }\n    }\n\n    public static RoutedEvent<CanExecuteRoutedEventArgs> CanExecuteEvent { get; }\n        = RoutedEvent.Register<CanExecuteRoutedEventArgs>(nameof(CanExecuteEvent), RoutingStrategies.Bubble, typeof(RoutedCommand));\n\n    public bool CanExecute(object? parameter, IInputElement? target)\n    {\n        if (target == null) return false;\n\n        var args = new CanExecuteRoutedEventArgs(this, parameter);\n        target.RaiseEvent(args);\n\n        return args.CanExecute;\n    }\n\n    bool ICommand.CanExecute(object? parameter)\n    {\n        return CanExecute(parameter, _focusedElement);\n    }\n\n    public static RoutedEvent<ExecutedRoutedEventArgs> ExecutedEvent { get; }\n        = RoutedEvent.Register<ExecutedRoutedEventArgs>(nameof(ExecutedEvent), RoutingStrategies.Bubble, typeof(RoutedCommand));\n\n    public void Execute(object? parameter, IInputElement? target)\n    {\n        if (target == null) \n            return;\n\n        var args = new ExecutedRoutedEventArgs(this, parameter);\n        target.RaiseEvent(args);\n    }\n\n    void ICommand.Execute(object? parameter)\n    {\n        Execute(parameter, _focusedElement);\n    }\n\n    public event EventHandler? CanExecuteChanged\n    {\n        add => CommandManager.RequerySuggested += value;\n        remove => CommandManager.RequerySuggested -= value;\n    }\n        \n    private static readonly AttachedProperty<IList<CommandBinding>?> CommandBindingsProperty \n        = AvaloniaProperty.RegisterAttached<RoutedCommand, Interactive, IList<CommandBinding>?>(\"CommandBinding\", null);\n        \n    internal static IList<CommandBinding>? GetCommandBindings(Interactive elem)\n        => elem.GetValue(CommandBindingsProperty);\n\n    internal static void SetCommandBindings(Interactive elem, IList<CommandBinding>? value)\n        => elem.SetValue(CommandBindingsProperty, value);\n}"
  },
  {
    "path": "Nodify/Compatibility/Commands/RoutedUICommand.cs",
    "content": "namespace Nodify.Compatibility;\n\npublic class RoutedUICommand : RoutedCommand\n{\n    public string Text { get; }\n    \n    public RoutedUICommand(string text, string name, Type? ownerType) : base(name, ownerType)\n    {\n        Text = text;\n    }\n\n    public RoutedUICommand(string text, string name, Type? ownerType, InputGesture inputGesture) : base(name, ownerType, inputGesture)\n    {\n        Text = text;\n    }\n\n    public RoutedUICommand(string text, string name, Type? ownerType, InputGestureCollection inputGestures) : base(name, ownerType, inputGestures)\n    {\n        Text = text;\n    }\n}"
  },
  {
    "path": "Nodify/Compatibility/Controls/GroupStyle.cs",
    "content": "namespace System.Windows.Controls;\n\npublic class GroupStyle\n{\n\n}"
  },
  {
    "path": "Nodify/Compatibility/Controls/MultiSelector.cs",
    "content": "namespace Nodify.Compatibility;\n\npublic class MultiSelector : SelectingItemsControl\n{\n    protected override Type StyleKeyOverride => typeof(SelectingItemsControl);\n\n    public MultiSelector()\n    {\n        Selection.SelectionChanged += OnSelectionChanged;\n        SelectionMode = SelectionMode.Multiple;\n    }\n\n    private void OnSelectionChanged(object? sender, SelectionModelSelectionChangedEventArgs e)\n    {\n        OnSelectionChanged(e);\n    }\n    \n    protected virtual void OnSelectionChanged(SelectionModelSelectionChangedEventArgs e) { }\n\n    public bool CanSelectMultipleItems\n    {\n        get => SelectionMode == SelectionMode.Multiple; \n        set => SelectionMode = value ? SelectionMode.Multiple : SelectionMode.Single;\n    }\n\n    protected void BeginUpdateSelectedItems()\n    {\n        Selection.BeginBatchUpdate();\n    }\n    \n    protected void EndUpdateSelectedItems()\n    {\n        Selection.EndBatchUpdate();\n    }\n    \n    public void UnselectAll()\n    {\n        Selection.Clear();\n    }\n\n    public bool HasItems => Items.Count > 0;\n        \n    public bool HasManyItemsSelected => Selection.SelectedItems.Count > 1;\n    \n    public IReadOnlyList<object?> InternalSelectedItems => Selection.SelectedItems;\n\n    public void SelectAll()\n    {\n        Selection.SelectAll();\n    }\n\n    protected virtual DependencyObject GetContainerForItemOverride()\n    {\n        return new ContentPresenter();\n    }\n\n    protected override Control CreateContainerForItemOverride(object? item, int index, object? recycleKey)\n    {\n        return (GetContainerForItemOverride() as Control)!;\n    }\n    \n    protected override bool NeedsContainerOverride(object? item, int index, out object? recycleKey)\n    {\n        recycleKey = null;\n        return !IsItemItsOwnContainerOverride(item);\n    }\n\n    protected virtual bool IsItemItsOwnContainerOverride(object item)\n    {\n        return false;\n    }\n}"
  },
  {
    "path": "Nodify/Compatibility/Controls/Primitives/IScrollInfo.cs",
    "content": "namespace System.Windows.Controls.Primitives;\n\npublic interface IScrollInfo : ILogicalScrollable\n{\n    //-------------------------------------------------------------------\n    //\n    //  Public Methods\n    //\n    //-------------------------------------------------------------------\n\n    #region Public Methods\n\n    /// <summary>\n    /// Scroll content by one line to the top.\n    /// </summary>\n    void LineUp();\n\n    /// <summary>\n    /// Scroll content by one line to the bottom.\n    /// </summary>\n    void LineDown();\n\n    /// <summary>\n    /// Scroll content by one line to the left.\n    /// </summary>\n    void LineLeft();\n\n    /// <summary>\n    /// Scroll content by one line to the right.\n    /// </summary>\n    void LineRight();\n\n\n    /// <summary>\n    /// Scroll content by one page to the top.\n    /// </summary>\n    void PageUp();\n\n    /// <summary>\n    /// Scroll content by one page to the bottom.\n    /// </summary>\n    void PageDown();\n\n    /// <summary>\n    /// Scroll content by one page to the left.\n    /// </summary>\n    void PageLeft();\n\n    /// <summary>\n    /// Scroll content by one page to the right.\n    /// </summary>\n    void PageRight();\n\n\n    /// <summary>\n    /// Scroll content by one page to the top.\n    /// </summary>\n    void MouseWheelUp();\n\n    /// <summary>\n    /// Scroll content by one page to the bottom.\n    /// </summary>\n    void MouseWheelDown();\n\n    /// <summary>\n    /// Scroll content by one page to the left.\n    /// </summary>\n    void MouseWheelLeft();\n\n    /// <summary>\n    /// Scroll content by one page to the right.\n    /// </summary>\n    void MouseWheelRight();\n\n    /// <summary>\n    /// Set the HorizontalOffset to the passed value.\n    /// An implementation may coerce this value into a valid range, typically inclusively between 0 and <see cref=\"ExtentWidth\" /> less <see cref=\"ViewportWidth\" />.\n    /// </summary>\n    void SetHorizontalOffset(double offset);\n\n    /// <summary>\n    /// Set the VerticalOffset to the passed value.\n    /// An implementation may coerce this value into a valid range, typically inclusively between 0 and <see cref=\"ExtentHeight\" /> less <see cref=\"ViewportHeight\" />.\n    /// </summary>\n    void SetVerticalOffset(double offset);\n\n    /// <summary>\n    /// This scrolls to make the rectangle in the Visual's coordinate space visible.\n    /// </summary>\n    /// <param name=\"visual\">The Visual that should become visible</param>\n    /// <param name=\"rectangle\">A rectangle representing in the visual's coordinate space to make visible.</param>\n    /// <returns>\n    /// A rectangle in the IScrollInfo's coordinate space that has been made visible.\n    /// Other ancestors to in turn make this new rectangle visible.\n    /// The rectangle should generally be a transformed version of the input rectangle.  In some cases, like\n    /// when the input rectangle cannot entirely fit in the viewport, the return value might be smaller.\n    /// </returns>\n    Rect MakeVisible(Visual visual, Rect rectangle);\n\n    #endregion\n\n    //-------------------------------------------------------------------\n    //\n    //  Public Properties\n    //\n    //-------------------------------------------------------------------\n\n    #region Public Properties\n\n    /// <summary>\n    /// ExtentWidth contains the full horizontal range of the scrolled content.\n    /// </summary>\n    double ExtentWidth { get; }\n\n    /// <summary>\n    /// ExtentHeight contains the full vertical range of the scrolled content.\n    /// </summary>\n    double ExtentHeight { get; }\n\n    /// <summary>\n    /// ViewportWidth contains the currently visible horizontal range of the scrolled content.\n    /// </summary>\n    double ViewportWidth { get; }\n\n    /// <summary>\n    /// ViewportHeight contains the currently visible vertical range of the scrolled content.\n    /// </summary>\n    double ViewportHeight { get; }\n\n    /// <summary>\n    /// HorizontalOffset is the horizontal offset into the scrolled content that represents the first unit visible.\n    /// </summary>\n    double HorizontalOffset { get; }\n\n    /// <summary>\n    /// VerticalOffset is the vertical offset into the scrolled content that represents the first unit visible.\n    /// </summary>\n    double VerticalOffset { get; }\n\n    /// <summary>\n    /// ScrollOwner is the container that controls any scrollbars, headers, etc... that are dependant\n    /// on this IScrollInfo's properties.  Implementers of IScrollInfo should call InvalidateScrollInfo()\n    /// on this object when properties change.\n    /// </summary>\n    //ScrollViewer ScrollOwner { get; set; }\n\n    #endregion\n}"
  },
  {
    "path": "Nodify/Compatibility/Controls/WpfControl.cs",
    "content": "using System;\nusing Avalonia.Controls.Primitives;\nusing Avalonia.Input;\nusing Avalonia.Interactivity;\n\nnamespace Nodify.Compatibility\n{\n    public class WpfControl : TemplatedControl\n    {\n        protected PointerEventArgs? currentPointerArgs;\n        \n        protected override void OnPointerEntered(PointerEventArgs e)\n        {\n            currentPointerArgs = e;\n            OnMouseEnter(e);\n            currentPointerArgs = null;\n            if (!e.Handled)\n                base.OnPointerEntered(e);\n        }\n\n        protected override  void OnPointerExited(PointerEventArgs e)\n        {\n            currentPointerArgs = e;\n            OnMouseLeave(e);\n            currentPointerArgs = null;\n            if (!e.Handled)\n                base.OnPointerExited(e);\n        }\n\n        protected override  void OnPointerMoved(PointerEventArgs e)\n        {\n            currentPointerArgs = e;\n            OnMouseMove(new MouseMoveEventArgs(e));\n            currentPointerArgs = null;\n            if (!e.Handled)\n                base.OnPointerMoved(e);\n        }\n\n        protected override void OnPointerPressed(PointerPressedEventArgs e)\n        {\n            currentPointerArgs = e;\n            OnMouseDown(new MouseButtonEventArgs(e));\n            currentPointerArgs = null;\n            if (!e.Handled)\n                base.OnPointerPressed(e);\n        }\n\n        protected override  void OnPointerReleased(PointerReleasedEventArgs e)\n        {\n            currentPointerArgs = e;\n            OnMouseUp(new MouseButtonEventArgs(e));\n            currentPointerArgs = null;\n            if (!e.Handled)\n                base.OnPointerReleased(e);\n        }\n        \n        protected virtual void OnMouseEnter(PointerEventArgs e)\n        {\n        }\n\n        protected virtual void OnMouseLeave(PointerEventArgs e)\n        {\n        }\n\n        protected virtual void OnMouseMove(MouseEventArgs e)\n        {\n        }\n\n        protected virtual void OnMouseDown(MouseButtonEventArgs e)\n        {\n        }\n\n        protected virtual void OnMouseUp(MouseButtonEventArgs e)\n        {\n        }\n\n        protected void CaptureMouseSafe()\n        {\n            if (currentPointerArgs == null)\n                throw new InvalidOperationException($\"You may only call {nameof(ReleaseMouseCapture)} from within a {nameof(OnMouseUp)} or {nameof(OnMouseDown)} event handler.\");\n            currentPointerArgs.Pointer.Capture(this);\n            this.PropagateMouseCapturedWithin(true);\n        }\n        \n        protected virtual void ReleaseMouseCapture()\n        {\n            if (currentPointerArgs == null)\n                throw new InvalidOperationException($\"You may only call {nameof(ReleaseMouseCapture)} from within a {nameof(OnMouseUp)} or {nameof(OnMouseDown)} event handler.\");\n            currentPointerArgs.Pointer.Capture(null);\n            this.PropagateMouseCapturedWithin(false);\n        }\n        \n        public bool IsMouseCaptured\n        {\n            get\n            {\n                if (currentPointerArgs == null)\n                    throw new InvalidOperationException($\"You may only call {nameof(ReleaseMouseCapture)} from within a {nameof(OnMouseUp)} or {nameof(OnMouseDown)} event handler.\");\n\n                return ReferenceEquals(currentPointerArgs?.Pointer.Captured, this);\n            }\n        }\n    }\n}"
  },
  {
    "path": "Nodify/Compatibility/Controls/WpfShape.cs",
    "content": "namespace Nodify.Compatibility;\n\n/// <summary>\n/// Nodify wants to override Render method in Shape, but this is not legal in Avalonia.\n/// This control is a workaround to allow overriding Render method in a Shape.\n/// It contains two inner controls: actual shape and actual renderer.\n/// </summary>\npublic class WpfShape : Panel\n{\n    private InnerShape? _shape;\n    private InnerRenderer? _renderer;\n\n    public static readonly StyledProperty<IBrush?> FillProperty = Shape.FillProperty.AddOwner<WpfShape>();\n\n    public static readonly StyledProperty<Stretch> StretchProperty = Shape.StretchProperty.AddOwner<WpfShape>();\n    \n    public static readonly StyledProperty<IBrush?> StrokeProperty = Shape.StrokeProperty.AddOwner<WpfShape>();\n    \n    public static readonly StyledProperty<AvaloniaList<double>?> StrokeDashArrayProperty = Shape.StrokeDashArrayProperty.AddOwner<WpfShape>();\n    \n    public static readonly StyledProperty<double> StrokeDashOffsetProperty = Shape.StrokeDashOffsetProperty.AddOwner<WpfShape>();\n    \n    public static readonly StyledProperty<double> StrokeThicknessProperty = Shape.StrokeThicknessProperty.AddOwner<WpfShape>();\n    \n    public static readonly StyledProperty<PenLineCap> StrokeLineCapProperty = Shape.StrokeLineCapProperty.AddOwner<WpfShape>();\n    \n    public static readonly StyledProperty<PenLineJoin> StrokeJoinProperty = Shape.StrokeJoinProperty.AddOwner<WpfShape>();\n    \n    protected static void AffectsGeometry<TShape>(params AvaloniaProperty[] properties) where TShape : WpfShape\n    {\n        foreach (AvaloniaProperty property in properties)\n            property.Changed.AddClassHandler<TShape>(AffectsGeometryInvalidate);\n    }\n    \n    protected new static void AffectsRender<T>(params AvaloniaProperty[] properties) where T : WpfShape\n    {\n        foreach (AvaloniaProperty property in properties)\n            property.Changed.AddClassHandler<T>((control, e) => control.InvalidateVisual());\n    }\n    \n    private static void AffectsGeometryInvalidate(WpfShape control, AvaloniaPropertyChangedEventArgs e)\n    {\n        if (e.Property == BoundsProperty && ((Rect) e.OldValue).Size == ((Rect) e.NewValue).Size)\n            return;\n        \n        control.InvalidateGeometry();\n    }\n\n    public WpfShape()\n    {\n        _shape = new InnerShape();\n        _shape.Bind(StretchProperty, this.GetObservable(StretchProperty), BindingPriority.Template);\n        _shape.Bind(FillProperty, this.GetObservable(FillProperty), BindingPriority.Template);\n        _shape.Bind(StrokeProperty, this.GetObservable(StrokeProperty), BindingPriority.Template);\n        _shape.Bind(StrokeThicknessProperty, this.GetObservable(StrokeThicknessProperty), BindingPriority.Template);\n        _shape.Bind(StrokeDashArrayProperty, this.GetObservable(StrokeDashArrayProperty), BindingPriority.Template);\n        _shape.Bind(StrokeDashOffsetProperty, this.GetObservable(StrokeDashOffsetProperty), BindingPriority.Template);\n        _shape.Bind(StrokeLineCapProperty, this.GetObservable(StrokeLineCapProperty), BindingPriority.Template);\n        _shape.Bind(StrokeJoinProperty, this.GetObservable(StrokeJoinProperty), BindingPriority.Template);\n        _renderer = new InnerRenderer();\n        Children.Add(_shape);\n        Children.Add(_renderer);\n        _renderer.OnRender += OnRender;\n        _shape.OnCreateDefiningGeometry += OnCreateDefiningGeometry;\n    }\n\n    private void InvalidateGeometry() => _shape?.InvalidateGeometry();\n    \n    private Geometry? OnCreateDefiningGeometry() => CreateDefiningGeometry();\n\n    private new void InvalidateVisual() => _renderer?.InvalidateVisual();\n    \n    private void OnRender(DrawingContext drawingContext) => Render(drawingContext);\n\n    protected virtual Geometry? CreateDefiningGeometry() => null;\n\n    protected new virtual void Render(DrawingContext drawingContext) { }\n    \n    /// <summary>\n    /// Gets or sets the <see cref=\"T:Avalonia.Media.IBrush\" /> that specifies how the shape's interior is painted.\n    /// </summary>\n    public IBrush? Fill\n    {\n      get => this.GetValue<IBrush?>(FillProperty);\n      set => this.SetValue<IBrush?>(FillProperty, value);\n    }\n\n    /// <summary>\n    /// Gets or sets a <see cref=\"P:Avalonia.Controls.Shapes.Shape.Stretch\" /> enumeration value that describes how the shape fills its allocated space.\n    /// </summary>\n    public Stretch Stretch\n    {\n      get => this.GetValue<Stretch>(StretchProperty);\n      set => this.SetValue<Stretch>(StretchProperty, value);\n    }\n\n    /// <summary>\n    /// Gets or sets the <see cref=\"T:Avalonia.Media.IBrush\" /> that specifies how the shape's outline is painted.\n    /// </summary>\n    public IBrush? Stroke\n    {\n      get => this.GetValue<IBrush?>(StrokeProperty);\n      set => this.SetValue<IBrush?>(StrokeProperty, value);\n    }\n\n    /// <summary>\n    /// Gets or sets a collection of <see cref=\"T:System.Double\" /> values that indicate the pattern of dashes and gaps that is used to outline shapes.\n    /// </summary>\n    public AvaloniaList<double>? StrokeDashArray\n    {\n      get => this.GetValue<AvaloniaList<double>?>(StrokeDashArrayProperty);\n      set => this.SetValue<AvaloniaList<double>?>(StrokeDashArrayProperty, value);\n    }\n\n    /// <summary>\n    /// Gets or sets a value that specifies the distance within the dash pattern where a dash begins.\n    /// </summary>\n    public double StrokeDashOffset\n    {\n      get => this.GetValue<double>(StrokeDashOffsetProperty);\n      set => this.SetValue<double>(StrokeDashOffsetProperty, value);\n    }\n\n    /// <summary>Gets or sets the width of the shape outline.</summary>\n    public double StrokeThickness\n    {\n      get => this.GetValue<double>(StrokeThicknessProperty);\n      set => this.SetValue<double>(StrokeThicknessProperty, value);\n    }\n\n    /// <summary>\n    /// Gets or sets a <see cref=\"T:Avalonia.Media.PenLineCap\" /> enumeration value that describes the shape at the ends of a line.\n    /// </summary>\n    public PenLineCap StrokeLineCap\n    {\n      get => this.GetValue<PenLineCap>(StrokeLineCapProperty);\n      set => this.SetValue<PenLineCap>(StrokeLineCapProperty, value);\n    }\n\n    /// <summary>\n    /// Gets or sets a <see cref=\"T:Avalonia.Media.PenLineJoin\" /> enumeration value that specifies the type of join that is used at the vertices of a Shape.\n    /// </summary>\n    public PenLineJoin StrokeJoin\n    {\n      get => this.GetValue<PenLineJoin>(StrokeJoinProperty);\n      set => this.SetValue<PenLineJoin>(StrokeJoinProperty, value);\n    }\n    \n    private class InnerShape : Shape\n    {\n        public event Func<Geometry?>? OnCreateDefiningGeometry;\n    \n        protected override Geometry? CreateDefiningGeometry()\n        {\n            return OnCreateDefiningGeometry?.Invoke();\n        }\n\n        public new void InvalidateGeometry()\n        {\n            base.InvalidateGeometry();\n        }\n    }\n\n    private class InnerRenderer : Control\n    {\n        public event Action<DrawingContext>? OnRender;\n    \n        public override void Render(DrawingContext context)\n        {\n            base.Render(context);\n            OnRender?.Invoke(context);\n        }\n    }\n}"
  },
  {
    "path": "Nodify/Compatibility/DefaultStyleKeyProperty.cs",
    "content": "using System;\n\nnamespace Nodify.Compatibility;\n\ninternal class DefaultStyleKeyProperty\n{\n    public static void OverrideMetadata(Type control, FrameworkPropertyMetadata metadata)\n    {\n        // just a placeholder so that the line doesn't have to be removed to minimize conflicts\n        // note: in Avalonia by default StyleOverrideKey is set to the type of the derived control (unlike WPF)\n        // so you need to add StyleOverrideKey only if you want to set the type to a different class\n    }\n}\n\ninternal class FrameworkPropertyMetadata\n{\n    public FrameworkPropertyMetadata(Type type)\n    {\n            \n    }\n}"
  },
  {
    "path": "Nodify/Compatibility/Dragging/DragCompletedEventArgs.cs",
    "content": "using System;\nusing Avalonia.Controls.Primitives;\nusing Avalonia.Interactivity;\n\nnamespace Nodify.Compatibility;\n\n/// <summary>\n/// This DragCompletedEventArgs class contains additional information about the\n/// DragCompleted event.\n/// </summary>\n/// <seealso cref=\"Thumb.DragCompletedEvent\" />\n/// <seealso cref=\"RoutedEventArgs\" />\npublic class DragCompletedEventArgs : RoutedEventArgs\n{\n    /// <summary>\n    /// This is an instance constructor for the DragCompletedEventArgs class.  It\n    /// is constructed with a reference to the event being raised.\n    /// </summary>\n    /// <returns>Nothing.</returns>\n    public DragCompletedEventArgs(double horizontalChange, double verticalChange, bool canceled) : base()\n    {\n        _horizontalChange = horizontalChange;\n        _verticalChange = verticalChange;\n        _wasCanceled = canceled;\n        RoutedEvent=Thumb.DragCompletedEvent;\n    }\n\n    /// <value>\n    /// Read-only access to the horizontal distance between the point where mouse's left-button\n    /// was pressed and the point where mouse's left-button was released\n    /// </value>\n    public double HorizontalChange\n    {\n        get { return _horizontalChange; }\n    }\n\n    /// <value>\n    /// Read-only access to the vertical distance between the point where mouse's left-button\n    /// was pressed and the point where mouse's left-button was released\n    /// </value>\n    public double VerticalChange\n    {\n        get { return _verticalChange; }\n    }\n\n    /// <summary>\n    /// Read-only access to boolean states whether the drag operation was canceled or not.\n    /// </summary>\n    /// <value></value>\n    public bool Canceled\n    {\n        get { return _wasCanceled; }\n    }\n        \n    private double _horizontalChange;\n    private double _verticalChange;\n    private bool _wasCanceled;\n}\n\n/// <summary>\n///     This delegate must used by handlers of the DragCompleted event.\n/// </summary>\n/// <param name=\"sender\">The current element along the event's route.</param>\n/// <param name=\"e\">The event arguments containing additional information about the event.</param>\n/// <returns>Nothing.</returns>\npublic delegate void DragCompletedEventHandler(object sender, DragCompletedEventArgs e);"
  },
  {
    "path": "Nodify/Compatibility/Dragging/DragDeltaEventArgs.cs",
    "content": "using System;\nusing Avalonia.Controls.Primitives;\nusing Avalonia.Interactivity;\n\nnamespace Nodify.Compatibility;\n\n/// <summary>\n/// This DragDeltaEventArgs class contains additional information about the\n/// DragDeltaEvent event.\n/// </summary>\n/// <seealso cref=\"Thumb.DragDeltaEvent\" />\n/// <seealso cref=\"RoutedEventArgs\" />\npublic class DragDeltaEventArgs : RoutedEventArgs\n{\n    /// <summary>\n    /// This is an instance constructor for the DragDeltaEventArgs class.  It\n    /// is constructed with a reference to the event being raised.\n    /// </summary>\n    /// <returns>Nothing.</returns>\n    public DragDeltaEventArgs(double horizontalChange, double verticalChange) : base()\n    {\n        _horizontalChange = horizontalChange;\n        _verticalChange = verticalChange;\n        RoutedEvent=Thumb.DragDeltaEvent;\n    }\n\n    /// <value>\n    /// Read-only access to the horizontal change.\n    /// </value>\n    public double HorizontalChange\n    {\n        get { return _horizontalChange; }\n    }\n\n    /// <value>\n    /// Read-only access to the vertical change.\n    /// </value>\n    public double VerticalChange\n    {\n        get { return _verticalChange; }\n    }\n\n    private double _horizontalChange;\n    private double _verticalChange;\n}\n\n/// <summary>\n///     This delegate must used by handlers of the DragDeltaEvent event.\n/// </summary>\n/// <param name=\"sender\">The current element along the event's route.</param>\n/// <param name=\"e\">The event arguments containing additional information about the event.</param>\n/// <returns>Nothing.</returns>\npublic delegate void DragDeltaEventHandler(object sender, DragDeltaEventArgs e);"
  },
  {
    "path": "Nodify/Compatibility/Dragging/DragStartedEventArgs.cs",
    "content": "using System;\nusing Avalonia.Controls.Primitives;\nusing Avalonia.Interactivity;\n\nnamespace Nodify.Compatibility;\n\n/// <summary>\n/// This DragStartedEventArgs class contains additional information about the\n/// DragStarted event.\n/// </summary>\n/// <seealso cref=\"Thumb.DragStartedEvent\" />\n/// <seealso cref=\"RoutedEventArgs\" />\npublic class DragStartedEventArgs : RoutedEventArgs\n{\n    /// <summary>\n    /// This is an instance constructor for the DragStartedEventArgs class.  It\n    /// is constructed with a reference to the event being raised.\n    /// </summary>\n    /// <returns>Nothing.</returns>\n    public DragStartedEventArgs(double horizontalOffset, double verticalOffset) : base()\n    {\n        _horizontalOffset = horizontalOffset;\n        _verticalOffset = verticalOffset;\n        RoutedEvent=Thumb.DragStartedEvent;\n    }\n\n    /// <value>\n    /// Read-only access to the horizontal offset (relative to Thumb's co-ordinate).\n    /// </value>\n    public double HorizontalOffset\n    {\n        get { return _horizontalOffset; }\n    }\n\n    /// <value>\n    /// Read-only access to the vertical offset (relative to Thumb's co-ordinate).\n    /// </value>\n    public double VerticalOffset\n    {\n        get { return _verticalOffset; }\n    }\n        \n    private double _horizontalOffset;\n    private double _verticalOffset;\n}\n\n/// <summary>\n///     This delegate must used by handlers of the DragStarted event.\n/// </summary>\n/// <param name=\"sender\">The current element along the event's route.</param>\n/// <param name=\"e\">The event arguments containing additional information about the event.</param>\n/// <returns>Nothing.</returns>\npublic delegate void DragStartedEventHandler(object sender, DragStartedEventArgs e);"
  },
  {
    "path": "Nodify/Compatibility/EmptyNamespaces.cs",
    "content": "// To minimize code changes, those empty namespaces serve as placeholders for the original namespaces in WPF.\n// So that original usings don't have to be removed and the code can be compiled without errors.\n// This will also help in the future when merging changes from the original WPF codebase.\n\nnamespace System.Windows.Data\n{\n    // Required or else the compiler will complain about empty namespace\n    internal class Empty { }\n}\n\nnamespace System.Windows.Documents\n{\n    internal class Empty { }\n}\n\nnamespace System.Windows.Markup\n{\n    internal class Empty { }\n}\n\nnamespace System.Windows.Media.Animation\n{\n    internal class Empty { }\n}\n\nnamespace System.Windows.Media.Imaging\n{\n    internal class Empty { }\n}\n\nnamespace System.Windows.Navigation\n{\n    internal class Empty { }\n}\n\nnamespace System.Windows.Controls\n{\n    internal class Empty { }\n}\n\nnamespace System.Windows.Shapes\n{\n    internal class Empty { }\n}\n\nnamespace System.Windows.Threading\n{\n    internal class Empty { }\n}\n\nnamespace System.Windows.Controls.Primitives\n{\n    internal class Empty { }\n}\n\nnamespace System.Windows\n{\n    internal class ThemeInfoAttribute : Attribute\n    {\n        public ThemeInfoAttribute(ResourceDictionaryLocation a, ResourceDictionaryLocation b)\n        {\n        \n        }\n    }\n\n    internal enum ResourceDictionaryLocation\n    {\n        None,\n        SourceAssembly,\n        ExternalAssembly\n    }\n}"
  },
  {
    "path": "Nodify/Compatibility/Extensions/ControlCaptureExtensions.cs",
    "content": "namespace Nodify.Compatibility;\n\ninternal static class ControlCaptureExtensions\n{\n    internal static void PropagateMouseCapturedWithin(this IInputElement start, bool isCaptured)\n    {\n        var control = start as Control;\n        NodifyEditor? editor = control.FindAncestorOfType<NodifyEditor>(true);\n        while (editor != null)\n        {\n            editor.IsMouseCaptureWithin = isCaptured;\n            editor = editor.FindAncestorOfType<NodifyEditor>(false);\n        }\n    }\n}"
  },
  {
    "path": "Nodify/Compatibility/Extensions/PointExtensions.cs",
    "content": "using System;\nusing Avalonia;\n\nnamespace Nodify.Compatibility;\n\ninternal static class PointExtensions\n{\n    public static double LengthSquared(this Point point)\n    {\n        return point.X * point.X + point.Y * point.Y;\n    }\n        \n    public static double Length(this Point point)\n    {\n        return Math.Sqrt(point.LengthSquared());\n    }\n}"
  },
  {
    "path": "Nodify/Compatibility/Extensions/SizeExtensions.cs",
    "content": "using Avalonia;\n\nnamespace Nodify.Compatibility;\n\ninternal static class SizeExtensions\n{\n    public static Vector ToVector(this Size size) => new Vector(size.Width, size.Height);\n}"
  },
  {
    "path": "Nodify/Compatibility/Extensions/StreamGeometryContextExtensions.cs",
    "content": "namespace Nodify.Compatibility;\n\ninternal static class StreamGeometryContextExtensions\n{\n    public static void LineTo(this StreamGeometryContext context, Point point, bool isStroked, bool isSmoothJoin)\n    {\n        context.LineTo(point);\n    }\n\n    public static void BezierTo(this StreamGeometryContext context,\n        Point point1, Point point2,\n        Point point3, bool isStroked, bool isSmoothJoin)\n    {\n        context.CubicBezierTo(point1, point2, point3);\n    }\n\n    public static void QuadraticBezierTo(this StreamGeometryContext context,\n        Point point1, Point point2, bool isStroked, bool isSmoothJoin)\n    {\n        context.QuadraticBezierTo(point1, point2);\n    }\n\n    public static System.IDisposable BeginFigure(this StreamGeometryContext context, Point startPoint, bool isFilled, bool isClosed)\n    {\n        context.BeginFigure(startPoint, isFilled);\n        return new EndFigureOnDispose(context, isClosed);\n    }\n        \n    private struct EndFigureOnDispose : System.IDisposable\n    {\n        private readonly StreamGeometryContext context;\n        private readonly bool isClosed;\n\n        public EndFigureOnDispose(StreamGeometryContext context, bool isClosed)\n        {\n            this.context = context;\n            this.isClosed = isClosed;\n        }\n\n        public void Dispose()\n        {\n            context.EndFigure(isClosed);\n        }\n    }\n}"
  },
  {
    "path": "Nodify/Compatibility/Extensions/VisualExtensions.cs",
    "content": "namespace Nodify.Compatibility;\n\ninternal static class VisualExtensions\n{\n    public static bool IsAncestorOf(this Visual visual, Visual target)\n    {\n        return ReferenceEquals(visual, target) || visual.IsVisualAncestorOf(target);\n    }\n}"
  },
  {
    "path": "Nodify/Compatibility/Input/InputGesture.cs",
    "content": "using System;\n\nnamespace Nodify.Compatibility;\n\n/// <summary>\n/// InputGesture - abstract base class for individual input device gestures.\n///                For Ex: KeyGesture (Keyboard), MouseGesture (Mouse) derived from this.\n///                \n/// </summary>\npublic abstract class InputGesture\n{\n    //------------------------------------------------------\n    //\n    //  Constructors\n    //\n    //------------------------------------------------------\n\n    //------------------------------------------------------\n    //\n    //  Public Methods\n    //\n    //------------------------------------------------------\n    #region Public Methods\n    /// <summary>\n    /// Sees if the InputGesture matches the input associated with the inputEventArgs\n    /// </summary>\n    /// <remarks>\n    /// Compares an InputEventArgs value to Gesture inside.\n    /// This method when overriden by derived classes, will match\n    /// InputEventArgs with its internal values and return a true/false.\n    /// </remarks>\n    /// <param name=\"targetElement\">the element to receive the command</param>\n    /// <param name=\"inputEventArgs\">inputEventArgs to compare to</param>\n    /// <returns>True if matched, false otherwise.\n    /// </returns>\n    public abstract bool Matches(object targetElement, EventArgs inputEventArgs);\n\n    #endregion Public Methods\n}"
  },
  {
    "path": "Nodify/Compatibility/Input/InputGestureCollection.cs",
    "content": "namespace Nodify.Compatibility;\n\npublic class InputGestureCollection : AvaloniaList<InputGesture>\n{\n        \n}"
  },
  {
    "path": "Nodify/Compatibility/Input/InputKeyGesture.cs",
    "content": "using System;\nusing Avalonia.Input;\n\nnamespace Nodify.Compatibility;\n\npublic class InputKeyGesture : InputGesture\n{\n    private KeyGesture keyGesture;\n        \n    public InputKeyGesture(Key key, KeyModifiers modifiers = KeyModifiers.None)\n    {\n        keyGesture = new KeyGesture(key, modifiers);\n    }\n\n    public InputKeyGesture(KeyGesture gesture)\n    {\n        keyGesture = gesture;\n    }\n\n    public override bool Matches(object targetElement, EventArgs inputEventArgs)\n    {\n        return keyGesture.Matches(inputEventArgs as KeyEventArgs);\n    }\n}"
  },
  {
    "path": "Nodify/Compatibility/Input/MouseAction.cs",
    "content": "namespace Nodify.Compatibility;\n\npublic enum MouseAction : byte\n{\n    /// <summary>\n    /// None\n    /// </summary>\n    None, \n    /// <summary>\n    /// LeftClick\n    /// </summary>\n    LeftClick, \n    /// <summary>\n    /// RightClick\n    /// </summary>\n    RightClick, \n    /// <summary>\n    /// MiddleClick\n    /// </summary>\n    MiddleClick, \n    /// <summary>\n    /// WheelClick\n    /// </summary>\n    WheelClick,\n    /// <summary>\n    /// LeftDoubleClick\n    /// </summary>\n    LeftDoubleClick, \n    /// <summary>\n    /// RightDoubleClick\n    /// </summary>\n    RightDoubleClick, \n    /// <summary>\n    /// MiddleDoubleClick\n    /// </summary>\n    MiddleDoubleClick\n}"
  },
  {
    "path": "Nodify/Compatibility/Input/MouseButtonEventArgs.cs",
    "content": "namespace Nodify.Compatibility;\n\npublic class MouseButtonEventArgs : MouseEventArgs\n{\n    private readonly RoutedEventArgs args;\n\n    internal MouseButtonEventArgs(PointerEventArgs pointerArgs)\n    {\n        this.args = pointerArgs;\n        var point = pointerArgs.GetCurrentPoint(null);\n        ChangedButton = point.Properties.PointerUpdateKind switch\n        {\n            PointerUpdateKind.LeftButtonPressed => MouseButton.Left,\n            PointerUpdateKind.LeftButtonReleased => MouseButton.Left,\n            PointerUpdateKind.RightButtonPressed => MouseButton.Right,\n            PointerUpdateKind.RightButtonReleased => MouseButton.Right,\n            PointerUpdateKind.MiddleButtonPressed => MouseButton.Middle,\n            PointerUpdateKind.MiddleButtonReleased => MouseButton.Middle,\n            _ => MouseButton.None\n        };\n        ClickCount = pointerArgs is PointerPressedEventArgs pressedArgs ? pressedArgs.ClickCount : 1;\n        Source = pointerArgs.Source;\n        KeyModifiers = pointerArgs.KeyModifiers;\n        ButtonState = MouseButtonState.Pressed;\n        RightButton = point.Properties.IsRightButtonPressed ? MouseButtonState.Pressed : MouseButtonState.Released;\n    }\n\n    public MouseButtonEventArgs(PointerReleasedEventArgs releasedEventArgs)\n    {\n        this.args = releasedEventArgs;\n        var point = releasedEventArgs.GetCurrentPoint(null);\n        ChangedButton = point.Properties.PointerUpdateKind switch\n        {\n            PointerUpdateKind.LeftButtonReleased => MouseButton.Left,\n            PointerUpdateKind.RightButtonReleased => MouseButton.Right,\n            PointerUpdateKind.MiddleButtonReleased => MouseButton.Middle,\n            _ => MouseButton.None\n        };\n        ClickCount = 1;\n        Source = releasedEventArgs.Source;\n        KeyModifiers = releasedEventArgs.KeyModifiers;\n        ButtonState = MouseButtonState.Released;\n        RightButton = point.Properties.IsRightButtonPressed ? MouseButtonState.Pressed : MouseButtonState.Released;\n    }\n\n    public override bool Handled\n    {\n        get => args.Handled;\n        set => args.Handled = value;\n    }\n        \n    public MouseButton ChangedButton { get; }\n        \n    public int ClickCount { get; }\n        \n    public override object? Source { get; }\n        \n    public MouseButtonState ButtonState { get; }\n        \n    public override KeyModifiers KeyModifiers { get; }\n\n    public IInputElement? Captured\n    {\n        get\n        {\n            if (args is PointerPressedEventArgs e)\n            {\n                return e.Pointer.Captured;\n            }\n            else if (args is PointerReleasedEventArgs r)\n            {\n                return r.Pointer.Captured;\n            }\n\n            return null;\n        }\n    }\n\n    public MouseButtonState RightButton { get; }\n\n    public IDisposable Capture(IInputElement element)\n    {\n        if (args is PointerPressedEventArgs e)\n        {\n            e.Pointer.Capture(element);\n            element.PropagateMouseCapturedWithin(true);\n            return new ReleaseMouseCaptureOperation(e.Pointer, element);\n        }\n        else if (args is PointerReleasedEventArgs r)\n        {\n            r.Pointer.Capture(element);\n            element.PropagateMouseCapturedWithin(true);\n            return new ReleaseMouseCaptureOperation(r.Pointer, element);\n        }\n\n        return EmptyDisposable.Instance;\n    }\n        \n    private struct ReleaseMouseCaptureOperation : IDisposable\n    {\n        private readonly IPointer pointer;\n        private readonly IInputElement element;\n\n        public ReleaseMouseCaptureOperation(IPointer pointer, IInputElement element)\n        {\n            this.pointer = pointer;\n            this.element = element;\n        }\n\n        public void Dispose()\n        {\n            if (pointer.Captured == element)\n            {\n                pointer.Capture(null);\n                element.PropagateMouseCapturedWithin(false);\n            }\n        }\n    }\n        \n    private struct EmptyDisposable : IDisposable\n    {\n        public static IDisposable Instance { get; } = new EmptyDisposable();\n            \n        public void Dispose() { }\n    }\n\n    public override Point GetPosition(Visual? relativeTo)\n    {\n        if (args is PointerEventArgs pointerArgs)\n        {\n            return pointerArgs.GetPosition(relativeTo);\n        }\n        return default;\n    }\n}"
  },
  {
    "path": "Nodify/Compatibility/Input/MouseButtonState.cs",
    "content": "namespace Nodify.Compatibility;\n\npublic enum MouseButtonState\n{\n    Released = 0,\n    Pressed = 1\n}"
  },
  {
    "path": "Nodify/Compatibility/Input/MouseEventArgs.cs",
    "content": "namespace Nodify.Compatibility;\n\npublic abstract class MouseEventArgs : EventArgs\n{\n    public abstract bool Handled { get; set; }\n        \n    public abstract KeyModifiers KeyModifiers { get; }\n        \n    public abstract object? Source { get; }\n\n    public abstract Point GetPosition(Visual? relativeTo);\n}"
  },
  {
    "path": "Nodify/Compatibility/Input/MouseGesture.cs",
    "content": "using System;\nusing System.ComponentModel;\nusing Avalonia.Input;\n\nnamespace Nodify.Compatibility;\n\npublic class MouseGesture : InputGesture\n{\n    //------------------------------------------------------\n    //\n    //  Constructors\n    //\n    //------------------------------------------------------\n\n    #region Constructors\n    /// <summary>\n    ///  Constructor\n    /// </summary>\n    public MouseGesture()   // Mouse action\n    {\n    }\n\n    /// <summary>\n    ///  constructor\n    /// </summary>\n    /// <param name=\"mouseAction\">Mouse Action</param>\n    public MouseGesture(MouseAction mouseAction): this(mouseAction, KeyModifiers.None)\n    {\n    }\n\n    /// <summary>\n    ///  Constructor\n    /// </summary>\n    /// <param name=\"mouseAction\">Mouse Action</param>\n    /// <param name=\"modifiers\">Modifiers</param>\n    public MouseGesture( MouseAction mouseAction,KeyModifiers modifiers)   // acclerator action\n    {\n        if (!MouseGesture.IsDefinedMouseAction(mouseAction))\n            throw new InvalidEnumArgumentException(\"mouseAction\", (int)mouseAction, typeof(MouseAction));\n\n        _modifiers = modifiers;\n        _mouseAction = mouseAction;\n\n        //AttachClassListeners();\n    }\n    #endregion Constructors\n        \n    //------------------------------------------------------\n    //\n    //  Public Methods\n    //\n    //------------------------------------------------------\n\n    #region Public Methods\n    /// <summary>\n    /// Action \n    /// </summary>\n    public MouseAction MouseAction\n    {\n        get \n        { \n            return _mouseAction; \n        }\n        set \n        {\n            if (!MouseGesture.IsDefinedMouseAction((MouseAction)value))\n                throw new InvalidEnumArgumentException(\"value\", (int)value, typeof(MouseAction));\n            if (_mouseAction != value)\n            {\n                _mouseAction = (MouseAction)value;\n                OnPropertyChanged(\"MouseAction\");\n            }\n        }\n    }\n\n    /// <summary>\n    /// Modifiers \n    /// </summary>\n    public KeyModifiers Modifiers\n    {\n        get \n        { \n            return _modifiers; \n        }\n        set \n        {\n            if (_modifiers != value)\n            {\n                _modifiers = (KeyModifiers)value;\n                OnPropertyChanged(\"Modifiers\");\n            }\n        }\n    }\n\n    /// <summary>\n    ///     Compares InputEventArgs with current Input\n    /// </summary>\n    /// <param name=\"targetElement\">the element to receive the command</param>\n    /// <param name=\"inputEventArgs\">inputEventArgs to compare to</param>\n    /// <returns>True - if matches, false otherwise.\n    /// </returns>\n    public override bool Matches(object targetElement, EventArgs inputEventArgs)\n    {\n        MouseAction mouseAction = GetMouseAction(inputEventArgs);\n        if(mouseAction != MouseAction.None && inputEventArgs is PointerEventArgs e)\n        {\n            return ( ( (int)this.MouseAction == (int)mouseAction ) && ( this.Modifiers == e.KeyModifiers ) );\n        }\n        if(mouseAction != MouseAction.None && inputEventArgs is MouseButtonEventArgs mouseE)\n        {\n            return ( ( (int)this.MouseAction == (int)mouseAction ) && ( this.Modifiers == mouseE.KeyModifiers ) );\n        }\n        return false;\n    }\n\n\n    // Helper like Enum.IsDefined,  for MouseAction.\n    internal static bool IsDefinedMouseAction(MouseAction mouseAction)\n    {\n        return (mouseAction >= MouseAction.None && mouseAction <= MouseAction.MiddleDoubleClick);\n    }\n    #endregion Public Methods\n\n    #region Internal NotifyProperty changed\n\n    /// <summary>\n    ///     PropertyChanged event Handler\n    /// </summary>\n    internal event PropertyChangedEventHandler PropertyChanged;\n\n    /// <summary>\n    ///     PropertyChanged virtual\n    /// </summary>\n    internal virtual void OnPropertyChanged(string propertyName)\n    {\n        if (PropertyChanged != null)\n        {\n            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));\n        }\n    }\n\n    #endregion\n\n    //------------------------------------------------------\n    //\n    //  Internal Methods\n    //\n    //------------------------------------------------------\n    #region Internal Methods\n\n    internal static MouseAction GetMouseAction(EventArgs inputArgs)\n    {\n        MouseAction MouseAction = MouseAction.None;\n\n        MouseButtonEventArgs args = inputArgs as MouseButtonEventArgs;\n        if(args != null)\n        {\n            // if(inputArgs is MouseWheelEventArgs)\n            // {\n            //     MouseAction = MouseAction.WheelClick;\n            // }\n            // else\n            {\n                switch(args.ChangedButton)\n                {\n                    case MouseButton.Left:\n                    {\n                        if(args.ClickCount == 2)\n                            MouseAction = MouseAction.LeftDoubleClick;\n                        else if(args.ClickCount == 1)\n                            MouseAction = MouseAction.LeftClick;\n                    }\n                        break;\n\n                    case MouseButton.Right:\n                    {\n                        if(args.ClickCount == 2)\n                            MouseAction = MouseAction.RightDoubleClick;\n                        else if(args.ClickCount == 1)\n                            MouseAction = MouseAction.RightClick;\n                    }\n                        break;\n\n                    case MouseButton.Middle:\n                    {\n                        if(args.ClickCount == 2)\n                            MouseAction = MouseAction.MiddleDoubleClick;\n                        else if(args.ClickCount == 1)\n                            MouseAction = MouseAction.MiddleClick;\n                    }\n                        break;\n                }\n            }\n        }\n            \n        if(inputArgs is PointerWheelEventArgs)\n        {\n            MouseAction = MouseAction.WheelClick;\n        }\n        else if (inputArgs is PointerPressedEventArgs pointerPressed)\n        {\n            switch(pointerPressed.GetCurrentPoint(null).Properties.PointerUpdateKind)\n            {\n                case PointerUpdateKind.LeftButtonPressed:\n                {\n                    if(pointerPressed.ClickCount == 2)\n                        MouseAction = MouseAction.LeftDoubleClick;\n                    else if(pointerPressed.ClickCount == 1)\n                        MouseAction = MouseAction.LeftClick;\n                }\n                    break;\n\n                case PointerUpdateKind.RightButtonPressed:\n                {\n                    if(pointerPressed.ClickCount == 2)\n                        MouseAction = MouseAction.RightDoubleClick;\n                    else if(pointerPressed.ClickCount == 1)\n                        MouseAction = MouseAction.RightClick;\n                }\n                    break;\n\n                case PointerUpdateKind.MiddleButtonPressed:\n                {\n                    if(pointerPressed.ClickCount == 2)\n                        MouseAction = MouseAction.MiddleDoubleClick;\n                    else if(pointerPressed.ClickCount == 1)\n                        MouseAction = MouseAction.MiddleClick;\n                }\n                    break;\n            }\n        }\n        else if (inputArgs is PointerReleasedEventArgs pointerReleased)\n        {\n            switch(pointerReleased.GetCurrentPoint(null).Properties.PointerUpdateKind)\n            {\n                case PointerUpdateKind.LeftButtonReleased:\n                {\n                    MouseAction = MouseAction.LeftClick;\n                }\n                    break;\n\n                case PointerUpdateKind.RightButtonReleased:\n                {\n                    MouseAction = MouseAction.RightClick;\n                }\n                    break;\n\n                case PointerUpdateKind.MiddleButtonReleased:\n                {\n                    MouseAction = MouseAction.MiddleClick;\n                }\n                    break;\n            }   \n        }\n        return MouseAction;\n    }\n\n    #endregion Internal Methods\n\n    //------------------------------------------------------\n    //\n    //  Private Fields\n    //\n    //------------------------------------------------------\n    #region Private Fields\n    private MouseAction  _mouseAction = MouseAction.None;\n    private KeyModifiers  _modifiers = KeyModifiers.None;\n    //       private static bool _classRegistered = false;\n    #endregion Private Fields\n}"
  },
  {
    "path": "Nodify/Compatibility/Input/MouseMoveEventArgs.cs",
    "content": "namespace Nodify.Compatibility;\n\npublic class MouseMoveEventArgs : MouseEventArgs\n{\n    private readonly PointerEventArgs args;\n        \n    internal MouseMoveEventArgs(PointerEventArgs e)\n    {\n        args = e;\n    }\n        \n    public override bool Handled\n    {\n        get => args.Handled;\n        set => args.Handled = value;\n    }\n        \n    public override KeyModifiers KeyModifiers => args.KeyModifiers;\n        \n    public override object? Source => args.Source;\n\n    public override Point GetPosition(Visual? relativeTo)\n    {\n        return args.GetPosition(relativeTo);\n    }\n}"
  },
  {
    "path": "Nodify/Compatibility/Input/MouseWheelEventArgs.cs",
    "content": "namespace Nodify.Compatibility;\n\npublic class MouseWheelEventArgs : MouseEventArgs\n{\n    private readonly PointerWheelEventArgs? _args;\n\n    public MouseWheelEventArgs()\n    {\n    }\n\n    public MouseWheelEventArgs(PointerWheelEventArgs args)\n    {\n        _args = args;\n    }\n\n    /// <summary>\n    /// Gets the delta in WPF-equivalent units (120 per notch).\n    /// </summary>\n    public int Delta => _args != null ? (int)(_args.Delta.Y * WpfDeltaScale) : 0;\n\n    public override bool Handled\n    {\n        get => _args?.Handled ?? false;\n        set\n        {\n            if (_args != null)\n                _args.Handled = value;\n        }\n    }\n\n    public override KeyModifiers KeyModifiers => _args?.KeyModifiers ?? default;\n\n    public override object? Source => _args?.Source;\n\n    public override Point GetPosition(Visual? relativeTo)\n    {\n        return _args?.GetPosition(relativeTo) ?? default;\n    }\n\n    // Avalonia delta is ~0.6 per notch, WPF is 120 per notch\n    private const double WpfDeltaScale = 120.0 / 0.6;\n}\n"
  },
  {
    "path": "Nodify/Compatibility/PanelUtilities.cs",
    "content": "using System;\nusing Avalonia;\nusing Avalonia.Controls;\nusing Avalonia.Layout;\nusing Avalonia.VisualTree;\n\nnamespace Nodify.Compatibility\n{\n    internal static class PanelUtilities\n    {\n        private static void AffectsParentArrangeInvalidate<TPanel>(AvaloniaPropertyChangedEventArgs e)\n            where TPanel : Layoutable\n        {\n            var control = e.Sender as Control;\n            var panel = control?.GetVisualParent() as TPanel;\n            panel?.InvalidateArrange();\n        }\n        \n        public static void AffectsParentArrange<TPanel>(params AvaloniaProperty[] properties)\n            where TPanel : Layoutable\n        {\n            foreach (var property in properties)\n            {\n                property.Changed.Subscribe(new AnonuymousObserver<AvaloniaPropertyChangedEventArgs>(AffectsParentArrangeInvalidate<TPanel>));\n            }\n        }\n        \n        private class AnonuymousObserver<T> : IObserver<T>\n        {\n            private readonly Action<T> _onNext;\n            private readonly Action<Exception>? _onError;\n            private readonly Action? _onCompleted;\n\n            public AnonuymousObserver(Action<T> onNext, Action<Exception>? onError = null, Action? onCompleted = null)\n            {\n                _onNext = onNext;\n                _onError = onError;\n                _onCompleted = onCompleted;\n            }\n\n            public void OnCompleted() => _onCompleted?.Invoke();\n            public void OnError(Exception error) => _onError?.Invoke(error);\n            public void OnNext(T value) => _onNext?.Invoke(value);\n        }\n    }\n}"
  },
  {
    "path": "Nodify/Compatibility/VisualTreeHelper.cs",
    "content": "namespace Nodify.Compatibility;\n\ninternal static class VisualTreeHelper\n{\n    public static DependencyObject? GetParent(DependencyObject? x)\n    {\n        return (x as Visual)?.Parent;\n    }\n\n    public static void HitTest(UIElement element,\n        Func<DependencyObject, HitTestFilterBehavior> filter, \n        Func<HitTestResult, HitTestResultBehavior> test,\n        PointHitTestParameters parameters)\n    {\n        DependencyObject? topMost = element.InputHitTest(parameters.Position) as DependencyObject;\n        while (topMost != null)\n        {\n            if (filter(topMost) == HitTestFilterBehavior.Stop)\n            {\n                break;\n            }\n            topMost = GetParent(topMost);\n        }\n    }\n\n    public static void HitTest(UIElement element,\n        Func<DependencyObject, HitTestFilterBehavior> filter,\n        Func<HitTestResult, HitTestResultBehavior> test,\n        GeometryHitTestParameters parameters)\n    {\n        // unfortunately this is not supported in Avalonia\n    }\n}\n\ninternal class PointHitTestParameters\n{\n    public PointHitTestParameters(Point position)\n    {\n        Position = position;\n    }\n\n    public Point Position { get; }\n}\n\ninternal enum HitTestFilterBehavior\n{\n    Continue,\n    ContinueSkipSelfAndChildren,\n    Stop,\n    ContinueSkipChildren,\n    ContinueSkipSelf\n}\n\ninternal class GeometryHitTestParameters\n{\n    public GeometryHitTestParameters(Geometry geometry)\n    {\n        Geometry = geometry;\n    }\n\n    public Geometry Geometry { get; }\n}\n\ninternal enum HitTestResultBehavior\n{\n    Continue,\n    Stop\n}\n\ninternal class HitTestResult\n{\n    public HitTestResult(Visual visualHit)\n    {\n        VisualHit = visualHit;\n    }\n\n    public Visual VisualHit { get; }\n}"
  },
  {
    "path": "Nodify/Connections/BaseConnection.Avalonia.cs",
    "content": "using System.Threading;\n\nnamespace Nodify;\n\npublic partial class BaseConnection\n{\n    private CancellationTokenSource? animationTokenSource;\n\n    static BaseConnection()\n    {\n        AffectsRender<BaseConnection>(SourceProperty, TargetProperty, SourceOffsetProperty, TargetOffsetProperty,\n            SourceOffsetModeProperty, TargetOffsetModeProperty, DirectionProperty, SpacingProperty, ArrowSizeProperty, ArrowEndsProperty, ArrowShapeProperty, TextProperty, DirectionalArrowsCountProperty, DirectionalArrowsOffsetProperty, OutlineThicknessProperty, OutlineBrushProperty,\n            IsAnimatingDirectionalArrowsProperty, DirectionalArrowsAnimationDurationProperty);\n        AffectsGeometry<BaseConnection>(SourceProperty, TargetProperty, SourceOffsetProperty, TargetOffsetProperty,\n            SourceOffsetModeProperty, TargetOffsetModeProperty, DirectionProperty, SpacingProperty, ArrowSizeProperty, ArrowEndsProperty, ArrowShapeProperty, SourceOrientationProperty, TargetOrientationProperty, DirectionalArrowsCountProperty, DirectionalArrowsOffsetProperty, OutlineThicknessProperty, OutlineBrushProperty);\n        OutlineBrushProperty.Changed.AddClassHandler<BaseConnection>(OnOutlinePenChanged);\n        OutlineThicknessProperty.Changed.AddClassHandler<BaseConnection>(OnOutlinePenChanged);\n        IsAnimatingDirectionalArrowsProperty.Changed.AddClassHandler<BaseConnection>(OnIsAnimatingDirectionalArrowsChanged);\n        DirectionalArrowsAnimationDurationProperty.Changed.AddClassHandler<BaseConnection>(OnDirectionalArrowsAnimationDurationChanged);\n        IsSelectedProperty.Changed.AddClassHandler<Control>(OnIsSelectedChanged);\n    }\n}"
  },
  {
    "path": "Nodify/Connections/BaseConnection.cs",
    "content": "using System;\nusing System.ComponentModel;\nusing System.Globalization;\nusing System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Documents;\nusing System.Windows.Input;\nusing System.Windows.Media;\nusing System.Windows.Shapes;\n\nnamespace Nodify\n{\n    /// <summary>\n    /// Specifies the offset type that can be applied to a <see cref=\"BaseConnection\"/> using the <see cref=\"BaseConnection.SourceOffset\"/> and the <see cref=\"BaseConnection.TargetOffset\"/> values.\n    /// </summary>\n    public enum ConnectionOffsetMode\n    {\n        /// <summary>\n        /// No offset applied.\n        /// </summary>\n        None,\n\n        /// <summary>\n        /// The offset is applied in a circle around the point.\n        /// </summary>\n        Circle,\n\n        /// <summary>\n        /// The offset is applied in a rectangle shape around the point.\n        /// </summary>\n        Rectangle,\n\n        /// <summary>\n        /// The offset is applied in a rectangle shape around the point, perpendicular to the edges.\n        /// </summary>\n        Edge,\n\n        /// <summary>\n        /// The offset is applied as a fixed margin.\n        /// </summary>\n        Static\n    }\n\n    /// <summary>\n    /// The direction in which a connection is oriented.\n    /// </summary>\n    public enum ConnectionDirection\n    {\n        /// <summary>\n        /// From <see cref=\"BaseConnection.Source\"/> to <see cref=\"BaseConnection.Target\"/>.\n        /// </summary>\n        Forward,\n\n        /// <summary>\n        /// From <see cref=\"BaseConnection.Target\"/> to <see cref=\"BaseConnection.Source\"/>.\n        /// </summary>\n        Backward\n    }\n\n    /// <summary>\n    /// The end at which the arrow head is drawn.\n    /// </summary>\n    public enum ArrowHeadEnds\n    {\n        /// <summary>\n        /// Arrow head at start.\n        /// </summary>\n        Start,\n\n        /// <summary>\n        /// Arrow head at end.\n        /// </summary>\n        End,\n\n        /// <summary>\n        /// Arrow heads at both ends.\n        /// </summary>\n        Both,\n\n        /// <summary>\n        /// No arrow head.\n        /// </summary>\n        None\n    }\n\n    /// <summary>\n    /// The shape of the arrowhead.\n    /// </summary>\n    public enum ArrowHeadShape\n    {\n        /// <summary>\n        /// The default arrowhead.\n        /// </summary>\n        Arrowhead,\n\n        /// <summary>\n        /// An ellipse.\n        /// </summary>\n        Ellipse,\n\n        /// <summary>\n        /// A rectangle.\n        /// </summary>\n        Rectangle\n    }\n\n    /// <summary>\n    /// Represents the base class for shapes that are drawn from a <see cref=\"Source\"/> point to a <see cref=\"Target\"/> point.\n    /// </summary>\n    public abstract partial class BaseConnection : WpfShape\n    {\n        #region Dependency Properties\n\n        public static readonly StyledProperty<Point> SourceProperty = AvaloniaProperty.Register<BaseConnection, Point>(nameof(Source), BoxValue.Point);\n        public static readonly StyledProperty<Point> TargetProperty = AvaloniaProperty.Register<BaseConnection, Point>(nameof(Target), BoxValue.Point);\n        public static readonly StyledProperty<Size> SourceOffsetProperty = AvaloniaProperty.Register<BaseConnection, Size>(nameof(SourceOffset), BoxValue.ConnectionOffset);\n        public static readonly StyledProperty<Size> TargetOffsetProperty = AvaloniaProperty.Register<BaseConnection, Size>(nameof(TargetOffset), BoxValue.ConnectionOffset);\n        public static readonly StyledProperty<ConnectionOffsetMode> SourceOffsetModeProperty = AvaloniaProperty.Register<BaseConnection, ConnectionOffsetMode>(nameof(SourceOffsetMode), ConnectionOffsetMode.Static);\n        public static readonly StyledProperty<ConnectionOffsetMode> TargetOffsetModeProperty = AvaloniaProperty.Register<BaseConnection, ConnectionOffsetMode>(nameof(TargetOffsetMode), ConnectionOffsetMode.Static);\n        public static readonly StyledProperty<Orientation> SourceOrientationProperty = AvaloniaProperty.Register<BaseConnection, Orientation>(nameof(SourceOrientation), Orientation.Horizontal);\n        public static readonly StyledProperty<Orientation> TargetOrientationProperty = AvaloniaProperty.Register<BaseConnection, Orientation>(nameof(TargetOrientation), Orientation.Horizontal);\n        public static readonly StyledProperty<ConnectionDirection> DirectionProperty = AvaloniaProperty.Register<BaseConnection, ConnectionDirection>(nameof(Direction));\n        public static readonly StyledProperty<uint> DirectionalArrowsCountProperty = AvaloniaProperty.Register<BaseConnection, uint>(nameof(DirectionalArrowsCount), BoxValue.UInt0);\n        public static readonly StyledProperty<double> DirectionalArrowsOffsetProperty = AvaloniaProperty.Register<BaseConnection, double>(nameof(DirectionalArrowsOffset), BoxValue.Double0);\n        public static readonly StyledProperty<bool> IsAnimatingDirectionalArrowsProperty = AvaloniaProperty.Register<BaseConnection, bool>(nameof(IsAnimatingDirectionalArrows), BoxValue.False);\n        public static readonly StyledProperty<double> DirectionalArrowsAnimationDurationProperty = AvaloniaProperty.Register<BaseConnection, double>(nameof(DirectionalArrowsAnimationDuration), BoxValue.Double2);\n        public static readonly StyledProperty<double> SpacingProperty = AvaloniaProperty.Register<BaseConnection, double>(nameof(Spacing), BoxValue.Double0);\n        public static readonly StyledProperty<Size> ArrowSizeProperty = AvaloniaProperty.Register<BaseConnection, Size>(nameof(ArrowSize), BoxValue.ArrowSize);\n        public static readonly StyledProperty<ArrowHeadEnds> ArrowEndsProperty = AvaloniaProperty.Register<BaseConnection, ArrowHeadEnds>(nameof(ArrowEnds), ArrowHeadEnds.End);\n        public static readonly StyledProperty<ArrowHeadShape> ArrowShapeProperty = AvaloniaProperty.Register<BaseConnection, ArrowHeadShape>(nameof(ArrowShape), ArrowHeadShape.Arrowhead);\n        public static readonly StyledProperty<ICommand> SplitCommandProperty = AvaloniaProperty.Register<BaseConnection, ICommand>(nameof(SplitCommand));\n        public static readonly StyledProperty<ICommand> DisconnectCommandProperty = Connector.DisconnectCommandProperty.AddOwner<BaseConnection>();\n        public static readonly StyledProperty<double> OutlineThicknessProperty = AvaloniaProperty.Register<BaseConnection, double>(nameof(OutlineThickness), BoxValue.Double5);\n        public static readonly StyledProperty<IBrush?> OutlineBrushProperty = AvaloniaProperty.Register<BaseConnection, IBrush?>(nameof(OutlineBrush));\n        public static readonly StyledProperty<IBrush?> ForegroundProperty = TextBlock.ForegroundProperty.AddOwner<BaseConnection>();\n        public static readonly StyledProperty<string?> TextProperty = TextBlock.TextProperty.AddOwner<BaseConnection>();\n        public static readonly StyledProperty<double> FontSizeProperty = TextElement.FontSizeProperty.AddOwner<BaseConnection>();\n        public static readonly StyledProperty<FontFamily> FontFamilyProperty = TextElement.FontFamilyProperty.AddOwner<BaseConnection>();\n        public static readonly StyledProperty<FontWeight> FontWeightProperty = TextElement.FontWeightProperty.AddOwner<BaseConnection>();\n        public static readonly StyledProperty<FontStyle> FontStyleProperty = TextElement.FontStyleProperty.AddOwner<BaseConnection>();\n        public static readonly StyledProperty<FontStretch> FontStretchProperty = TextElement.FontStretchProperty.AddOwner<BaseConnection>();\n\n        public static readonly AttachedProperty<bool> IsSelectableProperty = AvaloniaProperty.RegisterAttached<BaseConnection, Control, bool>(\"IsSelectable\");\n        public static readonly AttachedProperty<bool> IsSelectedProperty = AvaloniaProperty.RegisterAttached<BaseConnection, Control, bool>(\"IsSelected\", defaultBindingMode: BindingMode.TwoWay);\n\n        private static void OnIsSelectedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)\n        {\n            var container = d is BaseConnection conn ? conn.Container : ((UIElement)d).GetParentOfType<ConnectionContainer>();\n            if (container != null)\n            {\n                container.IsSelected = (bool)e.NewValue;\n            }\n        }\n\n        public static bool GetIsSelectable(UIElement elem)\n            => (bool)elem.GetValue(IsSelectableProperty);\n\n        public static void SetIsSelectable(UIElement elem, bool value)\n            => elem.SetValue(IsSelectableProperty, value);\n\n        public static bool GetIsSelected(UIElement elem)\n            => (bool)elem.GetValue(IsSelectedProperty);\n\n        public static void SetIsSelected(UIElement? elem, bool value)\n            => elem?.SetValue(IsSelectedProperty, value);\n\n        private static void OnIsAnimatingDirectionalArrowsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)\n        {\n            var con = (BaseConnection)d;\n            if (e.NewValue is true)\n            {\n                con.StartAnimation(con.DirectionalArrowsAnimationDuration);\n            }\n            else\n            {\n                con.StopAnimation();\n            }\n        }\n\n        private static void OnDirectionalArrowsAnimationDurationChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)\n        {\n            var con = (BaseConnection)d;\n            if (con.IsAnimatingDirectionalArrows)\n            {\n                con.StartAnimation((double)e.NewValue);\n            }\n        }\n\n        private static void OnOutlinePenChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)\n        {\n            ((BaseConnection)d)._outlinePen = null;\n        }\n\n        /// <summary>\n        /// Gets or sets the start point of this connection.\n        /// </summary>\n        public Point Source\n        {\n            get => (Point)GetValue(SourceProperty);\n            set => SetValue(SourceProperty, value);\n        }\n\n        /// <summary>\n        /// Gets or sets the end point of this connection.\n        /// </summary>\n        public Point Target\n        {\n            get => (Point)GetValue(TargetProperty);\n            set => SetValue(TargetProperty, value);\n        }\n\n        /// <summary>\n        /// Gets or sets the offset from the <see cref=\"Source\"/> point.\n        /// </summary>\n        public Size SourceOffset\n        {\n            get => (Size)GetValue(SourceOffsetProperty);\n            set => SetValue(SourceOffsetProperty, value);\n        }\n\n        /// <summary>\n        /// Gets or sets the offset from the <see cref=\"Target\"/> point.\n        /// </summary>\n        public Size TargetOffset\n        {\n            get => (Size)GetValue(TargetOffsetProperty);\n            set => SetValue(TargetOffsetProperty, value);\n        }\n\n        /// <summary>\n        /// Gets or sets the <see cref=\"ConnectionOffsetMode\"/> to apply to the <see cref=\"Source\"/> when drawing the connection.\n        /// </summary>\n        public ConnectionOffsetMode SourceOffsetMode\n        {\n            get => (ConnectionOffsetMode)GetValue(SourceOffsetModeProperty);\n            set => SetValue(SourceOffsetModeProperty, value);\n        }\n\n        /// <summary>\n        /// Gets or sets the <see cref=\"ConnectionOffsetMode\"/> to apply to the <see cref=\"Target\"/> when drawing the connection.\n        /// </summary>\n        public ConnectionOffsetMode TargetOffsetMode\n        {\n            get => (ConnectionOffsetMode)GetValue(TargetOffsetModeProperty);\n            set => SetValue(TargetOffsetModeProperty, value);\n        }\n\n        /// <summary>\n        /// Gets or sets the orientation in which this connection is flowing.\n        /// </summary>\n        public Orientation SourceOrientation\n        {\n            get => (Orientation)GetValue(SourceOrientationProperty);\n            set => SetValue(SourceOrientationProperty, value);\n        }\n\n        /// <summary>\n        /// Gets or sets the orientation in which this connection is flowing.\n        /// </summary>\n        public Orientation TargetOrientation\n        {\n            get => (Orientation)GetValue(TargetOrientationProperty);\n            set => SetValue(TargetOrientationProperty, value);\n        }\n\n        /// <summary>\n        /// Gets or sets the direction in which this connection is flowing.\n        /// </summary>\n        public ConnectionDirection Direction\n        {\n            get => (ConnectionDirection)GetValue(DirectionProperty);\n            set => SetValue(DirectionProperty, value);\n        }\n\n        /// <summary>\n        /// Gets or sets the number of arrows to be drawn on the line in the direction of the connection (see <see cref=\"Direction\"/>).\n        /// </summary>\n        public uint DirectionalArrowsCount\n        {\n            get => (uint)GetValue(DirectionalArrowsCountProperty);\n            set => SetValue(DirectionalArrowsCountProperty, value);\n        }\n\n        /// <summary>\n        /// Gets or sets the offset of the arrows drawn by the <see cref=\"DirectionalArrowsCount\"/> (value is clamped between 0 and 1).\n        /// </summary>\n        public double DirectionalArrowsOffset\n        {\n            get => (double)GetValue(DirectionalArrowsOffsetProperty);\n            set => SetValue(DirectionalArrowsOffsetProperty, value);\n        }\n\n        /// <summary>\n        /// Gets or sets whether the directional arrows should be flowing through the connection wire.\n        /// </summary>\n        public bool IsAnimatingDirectionalArrows\n        {\n            get => (bool)GetValue(IsAnimatingDirectionalArrowsProperty);\n            set => SetValue(IsAnimatingDirectionalArrowsProperty, value);\n        }\n\n        /// <summary>\n        /// Gets or sets the duration in seconds of a directional arrow flowing from <see cref=\"Source\"/> to <see cref=\"Target\"/>.\n        /// </summary>\n        public double DirectionalArrowsAnimationDuration\n        {\n            get => (double)GetValue(DirectionalArrowsAnimationDurationProperty);\n            set => SetValue(DirectionalArrowsAnimationDurationProperty, value);\n        }\n\n        /// <summary>\n        /// Gets or sets the arrowhead ends.\n        /// </summary>\n        public ArrowHeadEnds ArrowEnds\n        {\n            get => (ArrowHeadEnds)GetValue(ArrowEndsProperty);\n            set => SetValue(ArrowEndsProperty, value);\n        }\n\n        /// <summary>\n        /// Gets or sets the arrowhead ends.\n        /// </summary>\n        public ArrowHeadShape ArrowShape\n        {\n            get => (ArrowHeadShape)GetValue(ArrowShapeProperty);\n            set => SetValue(ArrowShapeProperty, value);\n        }\n\n        /// <summary>\n        /// The distance between the start point and the where the angle breaks.\n        /// </summary>\n        public double Spacing\n        {\n            get => (double)GetValue(SpacingProperty);\n            set => SetValue(SpacingProperty, value);\n        }\n\n        /// <summary>\n        /// Gets or sets the size of the arrow head.\n        /// </summary>\n        public Size ArrowSize\n        {\n            get => (Size)GetValue(ArrowSizeProperty);\n            set => SetValue(ArrowSizeProperty, value);\n        }\n\n        /// <summary>\n        /// Splits the connection. Triggered by <see cref=\"EditorGestures.ConnectionGestures.Split\"/> gesture.\n        /// Parameter is the location where the splitting ocurred.\n        /// </summary>\n        public ICommand? SplitCommand\n        {\n            get => (ICommand)GetValue(SplitCommandProperty);\n            set => SetValue(SplitCommandProperty, value);\n        }\n\n        /// <summary>\n        /// Removes this connection. Triggered by <see cref=\"EditorGestures.ConnectionGestures.Disconnect\"/> gesture.\n        /// Parameter is the location where the disconnect ocurred.\n        /// </summary>\n        public ICommand? DisconnectCommand\n        {\n            get => (ICommand?)GetValue(DisconnectCommandProperty);\n            set => SetValue(DisconnectCommandProperty, value);\n        }\n\n        /// <summary>\n        /// The thickness of the outline.\n        /// </summary>\n        public double OutlineThickness\n        {\n            get => (double)GetValue(OutlineThicknessProperty);\n            set => SetValue(OutlineThicknessProperty, value);\n        }\n\n        /// <summary>\n        /// The brush used to render the outline.\n        /// </summary>\n        public IBrush? OutlineBrush\n        {\n            get => (IBrush?)GetValue(OutlineBrushProperty);\n            set => SetValue(OutlineBrushProperty, value);\n        }\n\n        /// <summary>\n        /// The brush used to render the <see cref=\"Text\"/>.\n        /// </summary>\n        public IBrush? Foreground\n        {\n            get => (IBrush?)GetValue(ForegroundProperty);\n            set => SetValue(ForegroundProperty, value);\n        }\n\n        /// <summary>\n        /// Gets or sets the text contents of the <see cref=\"BaseConnection\"/>.\n        /// </summary>\n        public string? Text\n        {\n            get => (string?)GetValue(TextProperty);\n            set => SetValue(TextProperty, value);\n        }\n\n        /// <inheritdoc cref=\"TextElement.FontSize\" />\n        //[TypeConverter(typeof(FontSizeConverter))]\n        public double FontSize\n        {\n            get => (double)GetValue(FontSizeProperty);\n            set => SetValue(FontSizeProperty, value);\n        }\n\n        /// <inheritdoc cref=\"TextElement.FontFamily\" />\n        public FontFamily FontFamily\n        {\n            get => (FontFamily)GetValue(FontFamilyProperty);\n            set => SetValue(FontFamilyProperty, value);\n        }\n\n        /// <inheritdoc cref=\"TextElement.FontStyle\" />\n        public FontStyle FontStyle\n        {\n            get => (FontStyle)GetValue(FontStyleProperty);\n            set => SetValue(FontStyleProperty, value);\n        }\n\n        /// <inheritdoc cref=\"TextElement.FontWeight\" />\n        public FontWeight FontWeight\n        {\n            get => (FontWeight)GetValue(FontWeightProperty);\n            set => SetValue(FontWeightProperty, value);\n        }\n\n        /// <inheritdoc cref=\"TextElement.FontStretch\" />\n        public FontStretch FontStretch\n        {\n            get => (FontStretch)GetValue(FontStretchProperty);\n            set => SetValue(FontStretchProperty, value);\n        }\n\n        #endregion\n\n        #region Routed Events\n\n        public static readonly RoutedEvent DisconnectEvent = RoutedEvent.Register<ConnectionEventArgs>(nameof(Disconnect), RoutingStrategies.Bubble, typeof(BaseConnection));\n        public static readonly RoutedEvent SplitEvent = RoutedEvent.Register<ConnectionEventArgs>(nameof(Split), RoutingStrategies.Bubble, typeof(BaseConnection));\n\n        /// <summary>Triggered by the <see cref=\"EditorGestures.ConnectionGestures.Disconnect\"/> gesture.</summary>\n        public event ConnectionEventHandler Disconnect\n        {\n            add => AddHandler(DisconnectEvent, value);\n            remove => RemoveHandler(DisconnectEvent, value);\n        }\n\n        /// <summary>Triggered by the <see cref=\"EditorGestures.ConnectionGestures.Split\"/> gesture.</summary>\n        public event ConnectionEventHandler Split\n        {\n            add => AddHandler(SplitEvent, value);\n            remove => RemoveHandler(SplitEvent, value);\n        }\n\n        #endregion\n\n        /// <summary>\n        /// Whether to prioritize controls of type <see cref=\"BaseConnection\"/> inside custom connections (connection wrappers) \n        /// when setting the <see cref=\"IsSelectableProperty\"/> and <see cref=\"IsSelectedProperty\"/> attached properties.\n        /// </summary>\n        /// <remarks>\n        /// Will fallback to the first <see cref=\"UIElement\"/> if no <see cref=\"BaseConnection\"/> is found or the value is false.\n        /// </remarks>\n        public static bool PrioritizeBaseConnectionForSelection { get; set; } = true;\n\n        /// <summary>\n        /// Gets a vector that has its coordinates set to 0.\n        /// </summary>\n        protected static readonly Vector ZeroVector = new Vector(0d, 0d);\n        \n        private Pen? _outlinePen;\n\n        private ConnectionContainer? _container;\n        private ConnectionContainer? Container => _container ??= this.GetParentOfType<ConnectionContainer>();\n\n        protected override Geometry CreateDefiningGeometry()\n        {\n            var _geometry = new StreamGeometry\n            {\n                // FillRule = FillRule.EvenOdd\n            };\n            using (StreamGeometryContext context = _geometry.Open())\n            {\n                (Vector sourceOffset, Vector targetOffset) = GetOffset();\n                var (arrowStart, arrowEnd) = DrawLineGeometry(context, Source + sourceOffset, Target + targetOffset);\n\n                if (ArrowSize.Width != 0d && ArrowSize.Height != 0d)\n                {\n                    var reverseDirection = Direction == ConnectionDirection.Forward ? ConnectionDirection.Backward : ConnectionDirection.Forward;\n                    switch (ArrowEnds)\n                    {\n                        case ArrowHeadEnds.Start:\n                            DrawArrowGeometry(context, arrowStart.ArrowStartSource, arrowStart.ArrowStartTarget, reverseDirection, ArrowShape, SourceOrientation);\n                            break;\n                        case ArrowHeadEnds.End:\n                            DrawArrowGeometry(context, arrowEnd.ArrowEndSource, arrowEnd.ArrowEndTarget, Direction, ArrowShape, TargetOrientation);\n                            break;\n                        case ArrowHeadEnds.Both:\n                            DrawArrowGeometry(context, arrowEnd.ArrowEndSource, arrowEnd.ArrowEndTarget, Direction, ArrowShape, TargetOrientation);\n                            DrawArrowGeometry(context, arrowStart.ArrowStartSource, arrowStart.ArrowStartTarget, reverseDirection, ArrowShape, SourceOrientation);\n                            break;\n                        case ArrowHeadEnds.None:\n                        default:\n                            break;\n                    }\n\n                    if (DirectionalArrowsCount > 0)\n                    {\n                        DrawDirectionalArrowsGeometry(context, Source + sourceOffset, Target + targetOffset);\n                    }\n                }\n            }\n\n            return _geometry;\n        }\n\n        protected abstract ((Point ArrowStartSource, Point ArrowStartTarget), (Point ArrowEndSource, Point ArrowEndTarget)) DrawLineGeometry(StreamGeometryContext context, Point source, Point target);\n\n        protected virtual void DrawDirectionalArrowsGeometry(StreamGeometryContext context, Point source, Point target) { }\n\n        protected virtual void DrawDirectionalArrowheadGeometry(StreamGeometryContext context, Vector direction, Point location)\n        {\n            double headWidth = ArrowSize.Width;\n            double headHeight = ArrowSize.Height / 2;\n\n            double angle = Math.Atan2(direction.Y, direction.X);\n            double sinT = Math.Sin(angle);\n            double cosT = Math.Cos(angle);\n\n            var from = new Point(location.X + (headWidth * cosT - headHeight * sinT), location.Y + (headWidth * sinT + headHeight * cosT));\n            var to = new Point(location.X + (headWidth * cosT + headHeight * sinT), location.Y - (headHeight * cosT - headWidth * sinT));\n\n            context.BeginFigure(location, true, true);\n            context.LineTo(from, true, true);\n            context.LineTo(to, true, true);\n        }\n\n        protected virtual void DrawArrowGeometry(StreamGeometryContext context, Point source, Point target, ConnectionDirection arrowDirection = ConnectionDirection.Forward, ArrowHeadShape shape = ArrowHeadShape.Arrowhead, Orientation orientation = Orientation.Horizontal)\n        {\n            switch (shape)\n            {\n                case ArrowHeadShape.Ellipse:\n                    DrawEllipseArrowhead(context, source, target, arrowDirection, orientation);\n                    break;\n                case ArrowHeadShape.Rectangle:\n                    DrawRectangleArrowhead(context, source, target, arrowDirection, orientation);\n                    break;\n                case ArrowHeadShape.Arrowhead:\n                default:\n                    DrawDefaultArrowhead(context, source, target, arrowDirection, orientation);\n                    break;\n            }\n        }\n\n        protected virtual void DrawDefaultArrowhead(StreamGeometryContext context, Point source, Point target, ConnectionDirection arrowDirection = ConnectionDirection.Forward, Orientation orientation = Orientation.Horizontal)\n        {\n            double direction = arrowDirection == ConnectionDirection.Forward ? 1d : -1d;\n\n            if (orientation == Orientation.Horizontal)\n            {\n                double headWidth = ArrowSize.Width;\n                double headHeight = ArrowSize.Height / 2;\n\n                var from = new Point(target.X - headWidth * direction, target.Y + headHeight);\n                var to = new Point(target.X - headWidth * direction, target.Y - headHeight);\n\n                using var _ = context.BeginFigure(target, true, true);\n                context.LineTo(from, true, true);\n                context.LineTo(to, true, true);\n            }\n            else\n            {\n                double headWidth = ArrowSize.Width / 2;\n                double headHeight = ArrowSize.Height;\n\n                var from = new Point(target.X - headWidth, target.Y - headHeight * direction);\n                var to = new Point(target.X + headWidth, target.Y - headHeight * direction);\n\n                using var _ = context.BeginFigure(target, true, true);\n                context.LineTo(from, true, true);\n                context.LineTo(to, true, true);\n            }\n        }\n\n        protected virtual void DrawRectangleArrowhead(StreamGeometryContext context, Point source, Point target, ConnectionDirection arrowDirection = ConnectionDirection.Forward, Orientation orientation = Orientation.Horizontal)\n        {\n            double direction = arrowDirection == ConnectionDirection.Forward ? 1d : -1d;\n\n            if (orientation == Orientation.Horizontal)\n            {\n                double headWidth = ArrowSize.Width;\n                double headHeight = ArrowSize.Height / 2;\n                var bottomRight = new Point(target.X, target.Y + headHeight);\n                var bottomLeft = new Point(target.X - headWidth * direction, target.Y + headHeight);\n                var topLeft = new Point(target.X - headWidth * direction, target.Y - headHeight);\n                var topRight = new Point(target.X, target.Y - headHeight);\n\n                using var _ = context.BeginFigure(target, true, true);\n                context.LineTo(bottomRight, true, true);\n                context.LineTo(bottomLeft, true, true);\n                context.LineTo(topLeft, true, true);\n                context.LineTo(topRight, true, true);\n            }\n            else\n            {\n                double headWidth = ArrowSize.Width / 2;\n                double headHeight = ArrowSize.Height;\n                var bottomLeft = new Point(target.X - headWidth, target.Y);\n                var topLeft = new Point(target.X - headWidth, target.Y - headHeight * direction);\n                var topRight = new Point(target.X + headWidth, target.Y - headHeight * direction);\n                var bottomRight = new Point(target.X + headWidth, target.Y);\n\n                using var _ = context.BeginFigure(target, true, true);\n                context.LineTo(bottomLeft, true, true);\n                context.LineTo(topLeft, true, true);\n                context.LineTo(topRight, true, true);\n                context.LineTo(bottomRight, true, true);\n            }\n        }\n\n        protected virtual void DrawEllipseArrowhead(StreamGeometryContext context, Point source, Point target, ConnectionDirection arrowDirection = ConnectionDirection.Forward, Orientation orientation = Orientation.Horizontal)\n        {\n            const double ControlPointRatio = 0.55228474983079356; // (Math.Sqrt(2) - 1) * 4 / 3;\n\n            double direction = arrowDirection == ConnectionDirection.Forward ? 1d : -1d;\n            var targetLocation = orientation == Orientation.Horizontal\n                ? new Point(target.X - ArrowSize.Width / 2 * direction, target.Y)\n                : new Point(target.X, target.Y - ArrowSize.Height / 2 * direction);\n\n            double headWidth = ArrowSize.Width / 2;\n            double headHeight = ArrowSize.Height / 2;\n\n            double x0 = targetLocation.X - headWidth;\n            double x1 = targetLocation.X - headWidth * ControlPointRatio;\n            double x2 = targetLocation.X;\n            double x3 = targetLocation.X + headWidth * ControlPointRatio;\n            double x4 = targetLocation.X + headWidth;\n\n            double y0 = targetLocation.Y - headHeight;\n            double y1 = targetLocation.Y - headHeight * ControlPointRatio;\n            double y2 = targetLocation.Y;\n            double y3 = targetLocation.Y + headHeight * ControlPointRatio;\n            double y4 = targetLocation.Y + headHeight;\n\n            using var _ = context.BeginFigure(new Point(x2, y0), true, true);\n            context.BezierTo(new Point(x3, y0), new Point(x4, y1), new Point(x4, y2), true, true);\n            context.BezierTo(new Point(x4, y3), new Point(x3, y4), new Point(x2, y4), true, true);\n            context.BezierTo(new Point(x1, y4), new Point(x0, y3), new Point(x0, y2), true, true);\n            context.BezierTo(new Point(x0, y1), new Point(x1, y0), new Point(x2, y0), true, true);\n        }\n\n        /// <summary>\n        /// Gets the resulting offset after applying the <see cref=\"SourceOffsetMode\"/>.\n        /// </summary>\n        /// <returns></returns>\n        protected virtual (Vector SourceOffset, Vector TargetOffset) GetOffset()\n        {\n            Vector sourceDelta = Target - Source;\n            Vector targetDelta = Source - Target;\n            double arrowDirection = Direction == ConnectionDirection.Forward ? 1d : -1d;\n\n            var sourceOffset = GetOffset(SourceOffsetMode, sourceDelta, SourceOffset, arrowDirection);\n            var targetOffset = GetOffset(TargetOffsetMode, targetDelta, TargetOffset, -arrowDirection);\n\n            if (SourceOrientation == Orientation.Vertical)\n            {\n                sourceOffset = new Vector(sourceOffset.Y, sourceOffset.X);\n            }\n\n            if (TargetOrientation == Orientation.Vertical)\n            {\n                targetOffset = new Vector(targetOffset.Y, targetOffset.X);\n            }\n\n            return (sourceOffset, targetOffset);\n\n            static Vector GetOffset(ConnectionOffsetMode mode, Vector delta, Size currentOffset, double arrowDirection) => mode switch\n            {\n                ConnectionOffsetMode.Rectangle => GetRectangleModeOffset(delta, currentOffset),\n                ConnectionOffsetMode.Circle => GetCircleModeOffset(delta, currentOffset),\n                ConnectionOffsetMode.Edge => GetEdgeModeOffset(delta, currentOffset),\n                ConnectionOffsetMode.Static => GetStaticModeOffset(arrowDirection, currentOffset),\n                ConnectionOffsetMode.None => ZeroVector,\n                _ => throw new NotImplementedException()\n            };\n\n            static Vector GetStaticModeOffset(double direction, Size offset)\n            {\n                double xOffset = offset.Width * direction;\n                double yOffset = offset.Height * direction;\n\n                return new Vector(xOffset, yOffset);\n            }\n\n            static Vector GetEdgeModeOffset(Vector delta, Size offset)\n            {\n                double xOffset = Math.Min(Math.Abs(delta.X) / 2d, offset.Width) * Math.Sign(delta.X);\n                double yOffset = Math.Min(Math.Abs(delta.Y) / 2d, offset.Height) * Math.Sign(delta.Y);\n\n                return new Vector(xOffset, yOffset);\n            }\n\n            static Vector GetCircleModeOffset(Vector delta, Size offset)\n            {\n                if (delta.SquaredLength > 0d)\n                {\n                    delta.Normalize();\n                }\n\n                return new Vector(delta.X * offset.Width, delta.Y * offset.Height);\n            }\n\n            static Vector GetRectangleModeOffset(Vector delta, Size offset)\n            {\n                if (delta.SquaredLength > 0d)\n                {\n                    delta.Normalize();\n                }\n\n                double angle = Math.Atan2(delta.Y, delta.X);\n                double x, y;\n\n                if (offset.Width * 2d * Math.Abs(delta.Y) < offset.Height * 2d * Math.Abs(delta.X))\n                {\n                    x = Math.Sign(delta.X) * offset.Width;\n                    y = Math.Tan(angle) * x;\n                }\n                else\n                {\n                    y = Math.Sign(delta.Y) * offset.Height;\n                    x = 1.0d / Math.Tan(angle) * y;\n                }\n\n                return new Vector(x, y);\n            }\n        }\n\n        protected virtual Point GetTextPosition(FormattedText text, Point source, Point target)\n        {\n            double direction = Direction == ConnectionDirection.Forward ? 1d : -1d;\n            var spacing = new Vector(Spacing * direction, 0d);\n            var spacingVertical = new Vector(spacing.Y, spacing.X);\n\n            var p0 = source + (SourceOrientation == Orientation.Vertical ? spacingVertical : spacing);\n            var p1 = target - (TargetOrientation == Orientation.Vertical ? spacingVertical : spacing);\n\n            return new Point((p0.X + p1.X - text.Width) / 2, (p0.Y + p1.Y - text.Height) / 2);\n        }\n\n        /// <summary>Starts animating the directional arrows.</summary>\n        /// <param name=\"duration\">The duration for moving an arrowhead from <see cref=\"Source\"/> to <see cref=\"Target\"/>.</param>\n        public void StartAnimation(double duration = 1.5d)\n        {\n            StopAnimation();\n            animationTokenSource = new();\n            this.StartLoopingAnimation(DirectionalArrowsOffsetProperty, DirectionalArrowsOffset + 1d, duration, animationTokenSource.Token);\n        }\n\n        /// <summary>Stops the animation started by <see cref=\"StartAnimation(double)\"/></summary>\n        public void StopAnimation()\n        {\n            this.CancelAnimation(DirectionalArrowsOffsetProperty, animationTokenSource);\n        }\n\n        protected override void OnPointerPressed(PointerPressedEventArgs e)\n        {\n            Focus();\n\n            e.Pointer.Capture(this);\n            this.PropagateMouseCapturedWithin(true);\n\n            EditorGestures.ConnectionGestures gestures = EditorGestures.Mappings.Connection;\n            if (gestures.Split.Matches(e.Source, e))\n            {\n                Point splitLocation = e.GetPosition(this);\n                OnSplit(splitLocation);\n\n                e.Handled = true;\n            }\n            else if (gestures.Disconnect.Matches(e.Source, e))\n            {\n                OnDisconnect();\n\n                e.Handled = true;\n            }\n        }\n\n        protected internal void OnSplit(Point splitLocation)\n        {\n            var args = new ConnectionEventArgs(DataContext)\n            {\n                RoutedEvent = SplitEvent,\n                SplitLocation = splitLocation,\n                Source = this\n            };\n\n            RaiseEvent(args);\n\n            // Raise SplitCommand if SplitEvent is not handled\n            if (!args.Handled && (SplitCommand?.CanExecute(splitLocation) ?? false))\n            {\n                SplitCommand.Execute(splitLocation);\n            }\n        }\n\n        protected internal void OnDisconnect()\n        {\n            var args = new ConnectionEventArgs(DataContext)\n            {\n                RoutedEvent = DisconnectEvent,\n                Source = this\n            };\n\n            RaiseEvent(args);\n\n            // Raise DisconnectCommand if DisconnectEvent is not handled\n            if (!args.Handled && (DisconnectCommand?.CanExecute(null) ?? false))\n            {\n                DisconnectCommand.Execute(null);\n            }\n        }\n\n        protected override void OnPointerReleased(PointerReleasedEventArgs e)\n        {\n            if (ReferenceEquals(e.Pointer.Captured, this))\n            {\n                e.Pointer.Capture(null);\n                this.PropagateMouseCapturedWithin(false);\n            }\n        }\n        \n        private Pen GetOutlinePen()\n        {\n            return _outlinePen ??= new Pen(OutlineBrush, StrokeThickness + OutlineThickness * 2d);\n        }\n\n        protected override void Render(DrawingContext drawingContext)\n        {\n            if (OutlineBrush != null)\n            {\n                drawingContext.DrawGeometry(OutlineBrush, GetOutlinePen(), CreateDefiningGeometry());\n            }\n\n            base.Render(drawingContext);\n        \n            if (!string.IsNullOrEmpty(Text))\n            {\n                var typeface = new Typeface(FontFamily, FontStyle, FontWeight, FontStretch);\n                var text = new FormattedText(Text, CultureInfo.CurrentUICulture, FlowDirection, typeface, FontSize, Foreground ?? Stroke);\n\n                (Vector sourceOffset, Vector targetOffset) = GetOffset();\n                drawingContext.DrawText(text, GetTextPosition(text, Source + sourceOffset, Target + targetOffset));\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "Nodify/Connections/CircuitConnection.cs",
    "content": "﻿using System;\nusing System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Media;\n\nnamespace Nodify\n{\n    /// <summary>\n    /// Represents a line that is controlled by an angle.\n    /// </summary>\n    public class CircuitConnection : LineConnection\n    {\n        protected const double Degrees = Math.PI / 180.0d;\n\n        public static readonly StyledProperty<double> AngleProperty = AvaloniaProperty.Register<CircuitConnection, double>(nameof(Angle), BoxValue.Double45);\n\n        /// <summary>\n        /// The angle of the connection in degrees.\n        /// </summary>\n        public double Angle\n        {\n            get => (double)GetValue(AngleProperty);\n            set => SetValue(AngleProperty, value);\n        }\n\n        static CircuitConnection()\n        {\n            DefaultStyleKeyProperty.OverrideMetadata(typeof(CircuitConnection), new FrameworkPropertyMetadata(typeof(CircuitConnection)));\n            NodifyEditor.CuttingConnectionTypes.Add(typeof(CircuitConnection));\n            AffectsRender<CircuitConnection>(AngleProperty);\n        }\n\n        protected override ((Point ArrowStartSource, Point ArrowStartTarget), (Point ArrowEndSource, Point ArrowEndTarget)) DrawLineGeometry(StreamGeometryContext context, Point source, Point target)\n        {\n            var (p0, p1, p2) = GetLinePoints(source, target);\n\n            using var _ = context.BeginFigure(source, false, false);\n            if (CornerRadius > 0)\n            {\n                AddSmoothCorner(context, source, p0, p1, CornerRadius);\n                AddSmoothCorner(context, p0, p1, p2, CornerRadius);\n                AddSmoothCorner(context, p1, p2, target, CornerRadius);\n            }\n            else\n            {\n                context.LineTo(p0, true, true);\n                context.LineTo(p1, true, true);\n                context.LineTo(p2, true, true);\n            }\n            context.LineTo(target, true, true);\n\n            if (Spacing < 1d)\n            {\n                return ((p1, source), (p1, target));\n            }\n\n            return ((p0, source), (p1, target));\n        }\n\n        protected override Point GetTextPosition(FormattedText text, Point source, Point target)\n        {\n            var (p0, p1, p2) = GetLinePoints(source, target);\n\n            Vector deltaSource = p1 - p0;\n            Vector deltaTarget = p2 - p1;\n\n            if (deltaSource.SquaredLength > deltaTarget.SquaredLength)\n            {\n                return new Point((p0.X + p1.X - text.Width) / 2, (p0.Y + p1.Y - text.Height) / 2);\n            }\n\n            return new Point((p2.X + p1.X - text.Width) / 2, (p2.Y + p1.Y - text.Height) / 2);\n        }\n\n        protected override void DrawDirectionalArrowsGeometry(StreamGeometryContext context, Point source, Point target)\n        {\n            var (p0, p1, p2) = GetLinePoints(source, target);\n\n            double spacing = 1d / (DirectionalArrowsCount + 1);\n            for (int i = 1; i <= DirectionalArrowsCount; i++)\n            {\n                double t = (spacing * i + DirectionalArrowsOffset).WrapToRange(0d, 1d);\n                var (segment, to) = InterpolateLine(p0, p1, p2, t);\n\n                var direction = segment.SegmentStart - segment.SegmentEnd;\n                base.DrawDirectionalArrowheadGeometry(context, direction, to);\n            }\n        }\n\n        private (Point P0, Point P1, Point P2) GetLinePoints(in Point source, in Point target)\n        {\n            double direction = Direction == ConnectionDirection.Forward ? 1d : -1d;\n            var spacing = new Vector(Spacing * direction, 0d);\n            var spacingVertical = new Vector(spacing.Y, spacing.X);\n            var arrowOffset = new Vector(ArrowSize.Width * direction, 0d);\n\n            if (TargetOrientation == Orientation.Vertical)\n            {\n                arrowOffset = new Vector(arrowOffset.Y, arrowOffset.X);\n            }\n\n            Point endPoint = Spacing > 0 ? target - arrowOffset : target;\n\n            Point p1 = source + (SourceOrientation == Orientation.Vertical ? spacingVertical : spacing);\n            Point p3 = endPoint - (TargetOrientation == Orientation.Vertical ? spacingVertical : spacing);\n            Point p2 = GetControlPoint(p1, p3);\n\n            return (p1, p2, p3);\n        }\n\n        private Point GetControlPoint(in Point source, in Point target)\n        {\n            Vector delta = target - source;\n            double tangent = Math.Tan(Angle * Degrees);\n\n            double dx = Math.Abs(delta.X);\n            double dy = Math.Abs(delta.Y);\n\n            double slopeWidth = dy / tangent;\n            if (dx > slopeWidth)\n            {\n                return delta.X > 0d ? new Point(target.X - slopeWidth, source.Y) : new Point(source.X - slopeWidth, target.Y);\n            }\n\n            double slopeHeight = dx * tangent;\n            if (dy > slopeHeight)\n            {\n                if (delta.Y > 0d)\n                {\n                    // handle top left\n                    return delta.X < 0d ? new Point(source.X, target.Y - slopeHeight) : new Point(target.X, source.Y + slopeHeight);\n                }\n\n                // handle bottom left\n                if (delta.X < 0d)\n                {\n                    return new Point(source.X, target.Y + slopeHeight);\n                }\n            }\n\n            return new Point(target.X, source.Y - slopeHeight);\n        }\n    }\n}\n"
  },
  {
    "path": "Nodify/Connections/Connection.cs",
    "content": "﻿using System;\nusing System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Media;\n\nnamespace Nodify\n{\n    /// <summary>\n    /// Represents a cubic bezier curve.\n    /// </summary>\n    public class Connection : BaseConnection\n    {\n        static Connection()\n        {\n            DefaultStyleKeyProperty.OverrideMetadata(typeof(Connection), new FrameworkPropertyMetadata(typeof(Connection)));\n            NodifyEditor.CuttingConnectionTypes.Add(typeof(Connection));\n        }\n\n        private const double _baseOffset = 100d;\n        private const double _offsetGrowthRate = 25d;\n\n        protected override ((Point ArrowStartSource, Point ArrowStartTarget), (Point ArrowEndSource, Point ArrowEndTarget)) DrawLineGeometry(StreamGeometryContext context, Point source, Point target)\n        {\n            var (p0, p1, p2, p3) = GetBezierControlPoints(source, target);\n\n            using var _ = context.BeginFigure(source, false, false);\n            context.LineTo(p0, true, true);\n            context.BezierTo(p1, p2, p3, true, true);\n            context.LineTo(target, true, true);\n\n            return ((target, source), (source, target));\n        }\n\n        protected override void DrawDirectionalArrowsGeometry(StreamGeometryContext context, Point source, Point target)\n        {\n            var (p0, p1, p2, p3) = GetBezierControlPoints(source, target);\n\n            double spacing = 1d / (DirectionalArrowsCount + 1);\n            for (int i = 1; i <= DirectionalArrowsCount; i++)\n            {\n                double t = (spacing * i + DirectionalArrowsOffset).WrapToRange(0d, 1d);\n                var to = InterpolateCubicBezier(p0, p1, p2, p3, t);\n                var direction = GetBezierTangent(p0, p1, p2, p3, t);\n\n                base.DrawDirectionalArrowheadGeometry(context, direction, to);\n            }\n        }\n\n        protected override Point GetTextPosition(FormattedText text, Point source, Point target)\n        {\n            var (p0, p1, p2, p3) = GetBezierControlPoints(source, target);\n            var textCenter = new Vector(text.Width / 2, text.Height / 2);\n            return InterpolateCubicBezier(p0, p1, p2, p3, 0.5) - textCenter;\n        }\n\n        private (Point P0, Point P1, Point P2, Point P3) GetBezierControlPoints(Point source, Point target)\n        {\n            double direction = Direction == ConnectionDirection.Forward ? 1d : -1d;\n            var spacing = new Vector(Spacing * direction, 0d);\n            var spacingVertical = new Vector(spacing.Y, spacing.X);\n\n            Point startPoint = source + (SourceOrientation == Orientation.Vertical ? spacingVertical : spacing);\n            Point endPoint = target - (TargetOrientation == Orientation.Vertical ? spacingVertical : spacing);\n\n            Vector delta = target - source;\n            double height = Math.Abs(delta.Y);\n            double width = Math.Abs(delta.X);\n\n            // Smooth curve when distance is lower than base offset\n            double smooth = Math.Min(_baseOffset, height);\n            // Calculate offset based on distance\n            double offset = Math.Max(smooth, width / 2d);\n            // Grow slowly with distance\n            offset = Math.Min(_baseOffset + Math.Sqrt(width * _offsetGrowthRate), offset);\n\n            var controlPoint = new Vector(offset * direction, 0d);\n            var controlPointVertical = new Vector(controlPoint.Y, controlPoint.X);\n\n            // Avoid sharp bend if orientation different (when close to each other)\n            if (TargetOrientation != SourceOrientation)\n            {\n                controlPoint *= 0.5;\n            }\n\n            Point p0 = startPoint;\n            Point p1 = startPoint + (SourceOrientation == Orientation.Vertical ? controlPointVertical : controlPoint);\n            Point p2 = endPoint - (TargetOrientation == Orientation.Vertical ? controlPointVertical : controlPoint);\n            Point p3 = endPoint;\n\n            return (p0, p1, p2, p3);\n        }\n\n        private static Vector GetBezierTangent(Point P0, Point P1, Point P2, Point P3, double t)\n        {\n            // Calculate the derivatives of the Bezier curve equation and negate the result\n            return -(-3 * (1 - t) * (1 - t) * (Vector)P0 +\n                    (3 * (1 - t) * (1 - t) * (Vector)P1 - 6 * t * (1 - t) * (Vector)P1) +\n                    (6 * t * (1 - t) * (Vector)P2 - 3 * t * t * (Vector)P2) +\n                    3 * t * t * (Vector)P3);\n        }\n\n        protected static Point InterpolateCubicBezier(Point P0, Point P1, Point P2, Point P3, double t)\n        {\n            // B = (1 − t)^3 * P0 + 3 * t * (1 − t)^2 * P1 + 3 * t^2 * (1 − t) * P2 + t^3 * P3\n            return (Point)\n                 ((Vector)P0 * (1 - t) * (1 - t) * (1 - t)\n                + (Vector)P1 * 3 * t * (1 - t) * (1 - t)\n                + (Vector)P2 * 3 * t * t * (1 - t)\n                + (Vector)P3 * t * t * t);\n        }\n    }\n}\n"
  },
  {
    "path": "Nodify/Connections/ConnectionContainer.Avalonia.cs",
    "content": "namespace Nodify;\n\ninternal partial class ConnectionContainer\n{\n    static ConnectionContainer()\n    {\n        IsSelectedProperty.Changed.AddClassHandler<ConnectionContainer>(OnIsSelectedChanged);\n        SelectableMixin.Attach<ConnectionContainer>(IsSelectedProperty);\n    }\n}"
  },
  {
    "path": "Nodify/Connections/ConnectionContainer.cs",
    "content": "﻿using System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Input;\n\nnamespace Nodify\n{\n    internal partial class ConnectionContainer : ContentPresenter\n    {\n        #region Dependency properties\n\n        public static readonly DirectProperty<ConnectionContainer, bool> IsSelectableProperty = AvaloniaProperty.RegisterDirect<ConnectionContainer, bool>(nameof(IsSelectable), x => x.IsSelectable, (x, val) => x.IsSelectable = val);\n        public static readonly StyledProperty<bool> IsSelectedProperty = SelectingItemsControl.IsSelectedProperty.AddOwner<ConnectionContainer>(new StyledPropertyMetadata<bool>(defaultBindingMode: BindingMode.TwoWay));\n\n        private static void OnIsSelectedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)\n        {\n            var elem = (ConnectionContainer)d;\n            bool result = elem.IsSelectable && (bool)e.NewValue;\n            elem.IsSelected = result;\n            elem.OnSelectedChanged(result);\n        }\n\n        /// <summary>\n        /// Gets or sets whether this <see cref=\"ConnectionContainer\"/> can be selected.\n        /// </summary>\n        public bool IsSelectable\n        {\n            get => BaseConnection.GetIsSelectable(Connection ?? this);\n            set => BaseConnection.SetIsSelectable(Connection ?? this, value);\n        }\n\n        /// <summary>\n        /// Gets or sets a value that indicates whether this <see cref=\"ConnectionContainer\"/> is selected.\n        /// Can only be set if <see cref=\"IsSelectable\"/> is true.\n        /// </summary>\n        public bool IsSelected\n        {\n            get => (bool)GetValue(IsSelectedProperty);\n            set => SetValue(IsSelectedProperty, value);\n        }\n\n        #endregion\n\n        #region Routed events\n\n        public static readonly RoutedEvent SelectedEvent = RoutedEvent.Register<RoutedEventArgs>(nameof(Selected), RoutingStrategies.Bubble, typeof(ConnectionContainer));\n        public static readonly RoutedEvent UnselectedEvent = RoutedEvent.Register<RoutedEventArgs>(nameof(Unselected), RoutingStrategies.Bubble, typeof(ConnectionContainer));\n\n        /// <summary>\n        /// Occurs when this <see cref=\"ConnectionContainer\"/> is selected.\n        /// </summary>\n        public event RoutedEventHandler Selected\n        {\n            add => AddHandler(SelectedEvent, value);\n            remove => RemoveHandler(SelectedEvent, value);\n        }\n\n        /// <summary>\n        /// Occurs when this <see cref=\"ConnectionContainer\"/> is unselected.\n        /// </summary>\n        public event RoutedEventHandler Unselected\n        {\n            add => AddHandler(UnselectedEvent, value);\n            remove => RemoveHandler(UnselectedEvent, value);\n        }\n\n        #endregion\n\n        private ConnectionsMultiSelector Selector { get; }\n\n        private UIElement? _connection;\n        private UIElement? Connection => _connection ??= BaseConnection.PrioritizeBaseConnectionForSelection\n            ? this.GetChildOfType<BaseConnection>() ?? this.GetChildOfType<UIElement>()\n            : this.GetChildOfType<UIElement>();\n\n        internal ConnectionContainer(ConnectionsMultiSelector selector)\n        {\n            Selector = selector;\n        }\n\n        /// <summary>\n        /// Raises the <see cref=\"SelectedEvent\"/> or <see cref=\"UnselectedEvent\"/> based on <paramref name=\"newValue\"/>.\n        /// Called when the <see cref=\"IsSelected\"/> value is changed.\n        /// </summary>\n        /// <param name=\"newValue\">True if selected, false otherwise.</param>\n        protected void OnSelectedChanged(bool newValue)\n        {\n            BaseConnection.SetIsSelected(Connection, newValue);\n\n            RaiseEvent(new RoutedEventArgs(newValue ? SelectedEvent : UnselectedEvent, this));\n        }\n\n        protected override void OnPointerPressed(PointerPressedEventArgs e)\n        {\n            if (IsSelectable && EditorGestures.Mappings.Connection.Selection.Select.Matches(e.Source, e))\n            {\n                e.Handled = true;\n            }\n        }\n\n        protected override void OnPointerReleased(PointerReleasedEventArgs e)\n        {\n            EditorGestures.ConnectionGestures gestures = EditorGestures.Mappings.Connection;\n            if (gestures.Selection.Select.Matches(e.Source, e))\n            {\n                if (gestures.Selection.Append.Matches(e.Source, e))\n                {\n                    SetCurrentValue(IsSelectedProperty, true);\n                }\n                else if (gestures.Selection.Invert.Matches(e.Source, e))\n                {\n                    SetCurrentValue(IsSelectedProperty, !IsSelected);\n                }\n                else if (gestures.Selection.Remove.Matches(e.Source, e))\n                {\n                    SetCurrentValue(IsSelectedProperty, false);\n                }\n                else\n                {\n                    // Allow context menu on selection\n                    if (!(e.InitialPressMouseButton == MouseButton.Right && !e.GetCurrentPoint(this).Properties.IsRightButtonPressed) || !IsSelected)\n                    {\n                        Selector.UnselectAll();\n                    }\n\n                    SetCurrentValue(IsSelectedProperty, true);\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "Nodify/Connections/ConnectionsMultiSelector.Avalonia.cs",
    "content": "namespace Nodify;\n\ninternal partial class ConnectionsMultiSelector\n{\n    static ConnectionsMultiSelector()\n    {\n        SelectedItemsProperty.Changed.AddClassHandler<ConnectionsMultiSelector>(OnSelectedItemsSourceChanged);\n        CanSelectMultipleItemsProperty.Changed.AddClassHandler<ConnectionsMultiSelector>(OnCanSelectMultipleItemsChanged);\n    }\n\n    protected override Type StyleKeyOverride => typeof(ItemsControl);\n}"
  },
  {
    "path": "Nodify/Connections/ConnectionsMultiSelector.cs",
    "content": "﻿using System.Collections;\nusing System.Collections.Specialized;\nusing System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Controls.Primitives;\n\nnamespace Nodify\n{\n    internal partial class ConnectionsMultiSelector : MultiSelector\n    {\n        public new static readonly StyledProperty<IList> SelectedItemsProperty = NodifyEditor.SelectedItemsProperty.AddOwner<ConnectionsMultiSelector>();\n        public static readonly StyledProperty<bool> CanSelectMultipleItemsProperty = NodifyEditor.CanSelectMultipleItemsProperty.AddOwner<ConnectionsMultiSelector>(new StyledPropertyMetadata<bool>(true, coerce: CoerceCanSelectMultipleItems));\n\n        private static void OnCanSelectMultipleItemsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)\n            => ((ConnectionsMultiSelector)d).CanSelectMultipleItemsBase = (bool)e.NewValue;\n\n        private static bool CoerceCanSelectMultipleItems(DependencyObject d, bool baseValue)\n            => ((ConnectionsMultiSelector)d).CanSelectMultipleItemsBase = (bool)baseValue;\n\n        private static void OnSelectedItemsSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)\n            => ((ConnectionsMultiSelector)d).OnSelectedItemsSourceChanged((IList)e.OldValue, (IList)e.NewValue);\n\n        /// <summary>\n        /// Gets or sets the selected connections in the <see cref=\"NodifyEditor\"/>.\n        /// </summary>\n        public new IList? SelectedItems\n        {\n            get => (IList?)GetValue(SelectedItemsProperty);\n            set => SetValue(SelectedItemsProperty, value);\n        }\n\n        /// <summary>\n        /// Gets or sets whether multiple connections can be selected.\n        /// </summary>\n        public new bool CanSelectMultipleItems\n        {\n            get => (bool)GetValue(CanSelectMultipleItemsProperty);\n            set => SetValue(CanSelectMultipleItemsProperty, value);\n        }\n\n        private bool CanSelectMultipleItemsBase\n        {\n            get => base.CanSelectMultipleItems;\n            set => base.CanSelectMultipleItems = value;\n        }\n\n        protected override DependencyObject GetContainerForItemOverride()\n        {\n            return new ConnectionContainer(this);\n        }\n\n        protected override bool IsItemItsOwnContainerOverride(object item)\n            => item is ConnectionContainer;\n\n        private void OnSelectedItemsSourceChanged(IList oldValue, IList newValue)\n        {\n            if (oldValue is INotifyCollectionChanged oc)\n            {\n                oc.CollectionChanged -= OnSelectedItemsChanged;\n            }\n\n            if (newValue is INotifyCollectionChanged nc)\n            {\n                nc.CollectionChanged += OnSelectedItemsChanged;\n            }\n\n            IList selectedItems = base.SelectedItems;\n\n            BeginUpdateSelectedItems();\n            selectedItems.Clear();\n            if (newValue != null)\n            {\n                for (var i = 0; i < newValue.Count; i++)\n                {\n                    selectedItems.Add(newValue[i]);\n                }\n            }\n            EndUpdateSelectedItems();\n        }\n\n        private void OnSelectedItemsChanged(object? sender, NotifyCollectionChangedEventArgs e)\n        {\n            if (!CanSelectMultipleItems)\n                return;\n\n            switch (e.Action)\n            {\n                case NotifyCollectionChangedAction.Reset:\n                    base.SelectedItems.Clear();\n                    break;\n\n                case NotifyCollectionChangedAction.Add:\n                    IList? newItems = e.NewItems;\n                    if (newItems != null)\n                    {\n                        IList selectedItems = base.SelectedItems;\n                        for (var i = 0; i < newItems.Count; i++)\n                        {\n                            selectedItems.Add(newItems[i]);\n                        }\n                    }\n                    break;\n\n                case NotifyCollectionChangedAction.Remove:\n                    IList? oldItems = e.OldItems;\n                    if (oldItems != null)\n                    {\n                        IList selectedItems = base.SelectedItems;\n                        for (var i = 0; i < oldItems.Count; i++)\n                        {\n                            selectedItems.Remove(oldItems[i]);\n                        }\n                    }\n                    break;\n            }\n        }\n\n        protected override void OnSelectionChanged(SelectionModelSelectionChangedEventArgs e)\n        {\n            base.OnSelectionChanged(e);\n\n            IList? selected = SelectedItems;\n            if (selected != null)\n            {\n                var added = e.SelectedItems;\n                for (var i = 0; i < added.Count; i++)\n                {\n                    // Ensure no duplicates are added\n                    if (!selected.Contains(added[i]))\n                    {\n                        selected.Add(added[i]);\n                    }\n                }\n\n                var removed = e.DeselectedItems;\n                for (var i = 0; i < removed.Count; i++)\n                {\n                    selected.Remove(removed[i]);\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "Nodify/Connections/Connector.Avalonia.cs",
    "content": "using Avalonia;\nusing Avalonia.Controls;\n\nnamespace Nodify\n{\n    public partial class Connector\n    {\n        private bool ignoreNextOnPointerCaptureLost;\n\n        private IDisposable? capturedMouse;\n\n        public static readonly StyledProperty<HorizontalAlignment> HorizontalContentAlignmentProperty = ContentControl.HorizontalContentAlignmentProperty.AddOwner<Connector>();\n\n        public static readonly StyledProperty<VerticalAlignment> VerticalContentAlignmentProperty = ContentControl.VerticalContentAlignmentProperty.AddOwner<Connector>();\n\n        public HorizontalAlignment HorizontalContentAlignment\n        {\n            get => GetValue(HorizontalContentAlignmentProperty);\n            set => SetValue(HorizontalContentAlignmentProperty, value);\n        }\n\n        public VerticalAlignment VerticalContentAlignment\n        {\n            get => GetValue(VerticalContentAlignmentProperty);\n            set => SetValue(VerticalContentAlignmentProperty, value);\n        }\n\n        protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)\n        {\n            base.OnPropertyChanged(change);\n            if (change.Property == IsConnectedProperty)\n                PseudoClasses.Set(\":connected\", (bool)change.NewValue);\n        }\n\n        protected override void ReleaseMouseCapture()\n        {\n            if (capturedMouse != null)\n            {\n                capturedMouse.Dispose();\n                capturedMouse = null;\n            }\n            else if (currentPointerArgs != null)\n                base.ReleaseMouseCapture();\n        }\n    }\n}"
  },
  {
    "path": "Nodify/Connections/Connector.cs",
    "content": "﻿using System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Input;\n\nnamespace Nodify\n{\n    /// <summary>\n    /// Represents a connector control that can start and complete a <see cref=\"PendingConnection\"/>.\n    /// Has a <see cref=\"ElementConnector\"/> that the <see cref=\"Anchor\"/> is calculated from for the <see cref=\"PendingConnection\"/>. Center of this control is used if missing.\n    /// </summary>\n    [TemplatePart(Name = ElementConnector, Type = typeof(FrameworkElement))]\n    public partial class Connector : WpfControl\n    {\n        protected const string ElementConnector = \"PART_Connector\";\n\n        #region Routed Events\n\n        public static readonly RoutedEvent<PendingConnectionEventArgs> PendingConnectionStartedEvent = RoutedEvent.Register<PendingConnectionEventArgs>(nameof(PendingConnectionStarted), RoutingStrategies.Bubble, typeof(Connector));\n        public static readonly RoutedEvent<PendingConnectionEventArgs> PendingConnectionCompletedEvent = RoutedEvent.Register<PendingConnectionEventArgs>(nameof(PendingConnectionCompleted), RoutingStrategies.Bubble, typeof(Connector));\n        public static readonly RoutedEvent<PendingConnectionEventArgs> PendingConnectionDragEvent = RoutedEvent.Register<PendingConnectionEventArgs>(nameof(PendingConnectionDrag), RoutingStrategies.Bubble, typeof(Connector));\n        public static readonly RoutedEvent<ConnectorEventArgs> DisconnectEvent = RoutedEvent.Register<ConnectorEventArgs>(nameof(Disconnect), RoutingStrategies.Bubble, typeof(Connector));\n\n        /// <summary>Triggered by the <see cref=\"EditorGestures.ConnectorGestures.Connect\"/> gesture.</summary>\n        public event PendingConnectionEventHandler PendingConnectionStarted\n        {\n            add => AddHandler(PendingConnectionStartedEvent, value);\n            remove => RemoveHandler(PendingConnectionStartedEvent, value);\n        }\n\n        /// <summary>Triggered by the <see cref=\"EditorGestures.ConnectorGestures.Connect\"/> gesture.</summary>\n        public event PendingConnectionEventHandler PendingConnectionCompleted\n        {\n            add => AddHandler(PendingConnectionCompletedEvent, value);\n            remove => RemoveHandler(PendingConnectionCompletedEvent, value);\n        }\n\n        /// <summary>\n        /// Occurs when the mouse is changing position and the <see cref=\"Connector\"/> has mouse capture.\n        /// </summary>\n        public event PendingConnectionEventHandler PendingConnectionDrag\n        {\n            add => AddHandler(PendingConnectionDragEvent, value);\n            remove => RemoveHandler(PendingConnectionDragEvent, value);\n        }\n\n        /// <summary>Triggered by the <see cref=\"EditorGestures.ConnectorGestures.Disconnect\"/> gesture.</summary>\n        public event ConnectorEventHandler Disconnect\n        {\n            add => AddHandler(DisconnectEvent, value);\n            remove => RemoveHandler(DisconnectEvent, value);\n        }\n\n        #endregion\n\n        #region Dependency Properties\n\n        public static readonly StyledProperty<Point> AnchorProperty = AvaloniaProperty.Register<Connector, Point>(nameof(Anchor), BoxValue.Point);\n        public static readonly StyledProperty<bool> IsConnectedProperty = AvaloniaProperty.Register<Connector, bool>(nameof(IsConnected), BoxValue.False);\n        public static readonly StyledProperty<ICommand> DisconnectCommandProperty = AvaloniaProperty.Register<Connector, ICommand>(nameof(DisconnectCommand));\n        public static readonly DirectProperty<Connector, bool> IsPendingConnectionProperty = AvaloniaProperty.RegisterDirect<Connector, bool>(nameof(IsPendingConnection), x => x.IsPendingConnection);\n\n        /// <summary>\n        /// Gets the location where <see cref=\"Connection\"/>s can be attached to. \n        /// Bind with <see cref=\"System.Windows.Data.BindingMode.OneWayToSource\"/>\n        /// </summary>\n        public Point Anchor\n        {\n            get => (Point)GetValue(AnchorProperty);\n            set => SetValue(AnchorProperty, value);\n        }\n\n        /// <summary>\n        /// If this is set to false, the <see cref=\"Disconnect\"/> event will not be invoked and the connector will stop updating its <see cref=\"Anchor\"/> when moved, resized etc.\n        /// </summary>\n        public bool IsConnected\n        {\n            get => (bool)GetValue(IsConnectedProperty);\n            set => SetValue(IsConnectedProperty, value);\n        }\n\n        private bool isPendingConnection;\n        /// <summary>\n        /// Gets a value that indicates whether a <see cref=\"PendingConnection\"/> is in progress for this <see cref=\"Connector\"/>.\n        /// </summary>\n        public bool IsPendingConnection\n        {\n            get => isPendingConnection;\n            private set => SetAndRaise(IsPendingConnectionProperty, ref isPendingConnection, value);\n        }\n\n        /// <summary>\n        /// Invoked if the <see cref=\"Disconnect\"/> event is not handled.\n        /// Parameter is the <see cref=\"FrameworkElement.DataContext\"/> of this control.\n        /// </summary>\n        public ICommand? DisconnectCommand\n        {\n            get => (ICommand?)GetValue(DisconnectCommandProperty);\n            set => SetValue(DisconnectCommandProperty, value);\n        }\n\n        #endregion\n\n        static Connector()\n        {\n            DefaultStyleKeyProperty.OverrideMetadata(typeof(Connector), new FrameworkPropertyMetadata(typeof(Connector)));\n            FocusableProperty.OverrideDefaultValue<Connector>(true);\n            IsConnectedProperty.Changed.AddClassHandler<Connector>(OnIsConnectedChanged);\n        }\n\n        #region Fields\n\n        /// <summary>\n        /// Gets the <see cref=\"FrameworkElement\"/> used to calculate the <see cref=\"Anchor\"/>.\n        /// </summary>\n        protected FrameworkElement? Thumb { get; private set; }\n\n        /// <summary>\n        /// Gets the <see cref=\"ItemContainer\"/> that contains this <see cref=\"Connector\"/>.\n        /// </summary>\n        protected ItemContainer? Container { get; private set; }\n\n        /// <summary>\n        /// Gets the <see cref=\"NodifyEditor\"/> that owns this <see cref=\"Container\"/>.\n        /// </summary>\n        protected internal NodifyEditor? Editor { get; private set; }\n\n        /// <summary>\n        /// Gets or sets the safe zone outside the editor's viewport that will not trigger optimizations.\n        /// </summary>\n        public static double OptimizeSafeZone = 1000d;\n\n        /// <summary>\n        /// Gets or sets the minimum selected items needed to trigger optimizations when outside of the <see cref=\"OptimizeSafeZone\"/>.\n        /// </summary>\n        public static uint OptimizeMinimumSelectedItems = 100;\n\n        /// <summary>\n        /// Gets or sets if <see cref=\"Connector\"/>s should enable optimizations based on <see cref=\"OptimizeSafeZone\"/> and <see cref=\"OptimizeMinimumSelectedItems\"/>.\n        /// </summary>\n        public static bool EnableOptimizations = false;\n\n        /// <summary>\n        /// Gets or sets whether cancelling a pending connection is allowed.\n        /// </summary>\n        public static bool AllowPendingConnectionCancellation { get; set; } = true;\n\n        /// <summary>\n        /// Gets or sets whether the connection should be completed in two steps.\n        /// </summary>\n        public static bool EnableStickyConnections { get; set; }\n\n        private Point _lastUpdatedContainerPosition;\n        private Point _thumbCenter;\n        private bool _isHooked;\n\n        #endregion\n\n        /// <inheritdoc />\n        protected override void OnApplyTemplate(TemplateAppliedEventArgs e)\n        {\n            base.OnApplyTemplate(e);\n\n            Thumb = e.NameScope.Find<Control>(ElementConnector) ?? this;\n\n            Container = this.GetParentOfType<ItemContainer>();\n            Editor = Container?.Editor ?? this.GetParentOfType<NodifyEditor>();\n\n            Loaded += OnConnectorLoaded;\n            Unloaded += OnConnectorUnloaded;\n        }\n\n        #region Update connector\n\n        // Toggle events that could be used to update the Anchor\n        private void TrySetAnchorUpdateEvents(bool value)\n        {\n            if (Container != null && Editor != null)\n            {\n                // If events are not already hooked and we are asked to subscribe\n                if (value && !_isHooked)\n                {\n                    Container.PreviewLocationChanged += UpdateAnchorOptimized;\n                    Container.LocationChanged += OnLocationChanged;\n                    Container.SizeChanged += OnContainerSizeChanged;\n                    // I don't think this is actually needed?\n                    //Editor.ViewportUpdated += OnViewportUpdated;\n                    _isHooked = true;\n                }\n                // If events are already hooked and we are asked to unsubscribe\n                else if (_isHooked && !value)\n                {\n                    Container.PreviewLocationChanged -= UpdateAnchorOptimized;\n                    Container.LocationChanged -= OnLocationChanged;\n                    Container.SizeChanged -= OnContainerSizeChanged;\n                    //Editor.ViewportUpdated -= OnViewportUpdated;\n                    _isHooked = false;\n                }\n            }\n        }\n\n        private void OnContainerSizeChanged(object? sender, SizeChangedEventArgs e)\n            => UpdateAnchorOptimized(Container!.Location);\n\n        private void OnConnectorLoaded(object? sender, RoutedEventArgs? e)\n            => TrySetAnchorUpdateEvents(true);\n\n        private void OnConnectorUnloaded(object? sender, RoutedEventArgs e)\n            => TrySetAnchorUpdateEvents(false);\n\n        private static void OnIsConnectedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)\n        {\n            var con = (Connector)d;\n\n            if ((bool)e.NewValue)\n            {\n                con.UpdateAnchor();\n            }\n        }\n\n        /// <inheritdoc />\n        protected override void OnSizeChanged(SizeChangedInfo sizeInfo)\n        {\n            // Subscribe to events if not already subscribed \n            // Useful for advanced connectors that start collapsed because the loaded event is not called\n            Size newSize = sizeInfo.NewSize;\n            if (newSize.Width > 0d || newSize.Height > 0d)\n            {\n                TrySetAnchorUpdateEvents(true);\n\n                if (Container != null)\n                {\n                    UpdateAnchorOptimized(Container!.Location);\n                }\n            }\n        }\n\n        private void OnLocationChanged(object? sender, RoutedEventArgs e)\n            => UpdateAnchorOptimized(Container!.Location);\n\n        private void OnViewportUpdated(object? sender, RoutedEventArgs args)\n        {\n            if (Container != null && !Container.IsPreviewingLocation && _lastUpdatedContainerPosition != Container.Location)\n            {\n                UpdateAnchorOptimized(Container.Location);\n            }\n        }\n\n        /// <summary>\n        /// Updates the <see cref=\"Anchor\"/> and applies optimizations if needed based on <see cref=\"EnableOptimizations\"/> flag\n        /// </summary>\n        /// <param name=\"location\"></param>\n        protected void UpdateAnchorOptimized(Point location)\n        {\n            // Update only connectors that are connected\n            if (Editor != null && IsConnected)\n            {\n                bool shouldOptimize = EnableOptimizations && Editor.SelectedItems?.Count > OptimizeMinimumSelectedItems;\n\n                if (shouldOptimize)\n                {\n                    UpdateAnchorBasedOnLocation(Editor, location);\n                }\n                else\n                {\n                    UpdateAnchor(location);\n                }\n            }\n        }\n\n        private void UpdateAnchorBasedOnLocation(NodifyEditor editor, Point location)\n        {\n            var viewport = new Rect(editor.ViewportLocation, editor.ViewportSize);\n            double offset = OptimizeSafeZone / editor.ViewportZoom;\n\n            Rect area = viewport.Inflate(offset);\n\n            // Update only the connectors that are in the viewport or will be in the viewport\n            if (area.Contains(location))\n            {\n                UpdateAnchor(location);\n            }\n        }\n\n        /// <summary>\n        /// Updates the <see cref=\"Anchor\"/> relative to a location. (usually <see cref=\"Container\"/>'s location)\n        /// </summary>\n        /// <param name=\"location\">The relative location</param>\n        protected void UpdateAnchor(Point location)\n        {\n            _lastUpdatedContainerPosition = location;\n\n            if (Thumb != null && Container != null)\n            {\n                var thumbSize = Thumb.Bounds.Size.ToVector() /*RenderSize*/;\n                Vector containerMargin = Container.Bounds.Size.ToVector() /*RenderSize */ - Container.DesiredSize.ToVector();\n                Point relativeLocation = Thumb.TranslatePoint((Point)(thumbSize / 2 - containerMargin / 2), Container) ?? default;\n                SetCurrentValue(AnchorProperty, new Point(location.X + relativeLocation.X, location.Y + relativeLocation.Y));\n            }\n        }\n\n        /// <summary>\n        /// Updates the <see cref=\"Anchor\"/> based on <see cref=\"Container\"/>'s location.\n        /// </summary>\n        public void UpdateAnchor()\n        {\n            if (Container != null)\n            {\n                UpdateAnchor(Container.Location);\n            }\n        }\n\n        #endregion\n\n        #region Event Handlers\n\n        /// <inheritdoc />\n        protected override void OnPointerCaptureLost(PointerCaptureLostEventArgs e)\n        {\n            if (ignoreNextOnPointerCaptureLost)\n            {\n                ignoreNextOnPointerCaptureLost = false;\n                return;\n            }\n            // Always cancel if lost capture\n            OnConnectorDragCompleted(cancel: true, null);\n        }\n\n        /// <inheritdoc />\n        protected override void OnMouseDown(MouseButtonEventArgs e)\n        {\n            Focus();\n\n            this.CaptureMouseSafe();\n\n            e.Handled = true;\n\n            EditorGestures.ConnectorGestures gestures = EditorGestures.Mappings.Connector;\n            if (gestures.Disconnect.Matches(e.Source, e))\n            {\n                OnDisconnect();\n            }\n            else if (gestures.Connect.Matches(e.Source, e))\n            {\n                if (EnableStickyConnections && IsPendingConnection)\n                {\n                    OnConnectorDragCompleted(e: e);\n                }\n                else\n                {\n                    UpdateAnchor();\n                    OnConnectorDragStarted(e);\n                }\n            }\n        }\n\n        /// <inheritdoc />\n        protected override void OnMouseUp(MouseButtonEventArgs e)\n        {\n            bool releaseMouseCapture = false;\n            // Don't select the ItemContainer when starting a pending connecton for sticky connections\n            e.Handled = EnableStickyConnections && IsPendingConnection;\n\n            EditorGestures.ConnectorGestures gestures = EditorGestures.Mappings.Connector;\n            if (!EnableStickyConnections && gestures.Connect.Matches(e.Source, e))\n            {\n                OnConnectorDragCompleted(e: e);\n                e.Handled = true;\n            }\n            else if (AllowPendingConnectionCancellation && IsPendingConnection && gestures.CancelAction.Matches(e.Source, e))\n            {\n                // Cancel pending connection\n                OnConnectorDragCompleted(cancel: true, e: e);\n                ReleaseMouseCapture();\n                releaseMouseCapture = true;\n\n                // Don't show context menu\n                e.Handled = true;\n            }\n\n            if (IsMouseCaptured && !IsPendingConnection)\n            {\n                ReleaseMouseCapture();\n                releaseMouseCapture = true;\n            }\n            \n            // Avalonia hack: Avalonia contrary to WPF automatically releases mouse capture on mouse up\n            // and there is no way to prevent it. So we need to capture it again if we are still dragging\n            if (!releaseMouseCapture && EnableStickyConnections && IsPendingConnection)\n            {\n                ignoreNextOnPointerCaptureLost = true;\n                Dispatcher.UIThread.Post(() =>\n                {\n                    capturedMouse = e.Capture(this);\n                    ignoreNextOnPointerCaptureLost = false;\n                });\n            }\n        }\n\n        /// <inheritdoc />\n        protected override void OnKeyUp(KeyEventArgs e)\n        {\n            if (AllowPendingConnectionCancellation && EditorGestures.Mappings.Connector.CancelAction.Matches(e.Source, e))\n            {\n                // Cancel pending connection\n                OnConnectorDragCompleted(cancel: true);\n                ReleaseMouseCapture();\n            }\n        }\n\n        /// <inheritdoc />\n        protected override void OnMouseMove(MouseEventArgs e)\n        {\n            if (IsPendingConnection)\n            {\n                Vector offset = e.GetPosition(Thumb) - _thumbCenter;\n                OnConnectorDrag(offset, e);\n            }\n        }\n\n        protected virtual void OnConnectorDrag(Vector offset, MouseEventArgs e)\n        {\n            var args = new PendingConnectionEventArgs(DataContext, e)\n            {\n                RoutedEvent = PendingConnectionDragEvent,\n                OffsetX = offset.X,\n                OffsetY = offset.Y,\n                Anchor = Anchor,\n                Source = this\n            };\n\n            RaiseEvent(args);\n        }\n\n        protected virtual void OnConnectorDragStarted(MouseButtonEventArgs e)\n        {\n            if (Thumb != null)\n            {\n                _thumbCenter = new Point(Thumb.Bounds.Width / 2, Thumb.Bounds.Height / 2);\n            }\n\n            var args = new PendingConnectionEventArgs(DataContext, e)\n            {\n                RoutedEvent = PendingConnectionStartedEvent,\n                Anchor = Anchor,\n                Source = this\n            };\n\n            RaiseEvent(args);\n            IsPendingConnection = !args.Canceled;\n\n            if (IsMouseCaptured && !IsPendingConnection)\n            {\n                ReleaseMouseCapture();\n            }\n        }\n\n        protected virtual void OnConnectorDragCompleted(bool cancel = false, MouseButtonEventArgs? e = null)\n        {\n            if (IsPendingConnection)\n            {\n                FrameworkElement? elem = Editor != null ? PendingConnection.GetPotentialConnector(Editor, PendingConnection.GetAllowOnlyConnectorsAttached(Editor), e) : null;\n\n                var args = new PendingConnectionEventArgs(DataContext, e)\n                {\n                    TargetConnector = elem?.DataContext,\n                    RoutedEvent = PendingConnectionCompletedEvent,\n                    Anchor = Anchor,\n                    Source = this,\n                    Canceled = cancel\n                };\n\n                IsPendingConnection = false;\n                RaiseEvent(args);\n            }\n        }\n\n        protected virtual void OnDisconnect()\n        {\n            if (IsConnected && !IsPendingConnection)\n            {\n                object? connector = DataContext;\n                var args = new ConnectorEventArgs(connector)\n                {\n                    RoutedEvent = DisconnectEvent,\n                    Anchor = Anchor,\n                    Source = this\n                };\n\n                RaiseEvent(args);\n\n                // Raise DisconnectCommand if event is Disconnect not handled\n                if (!args.Handled && (DisconnectCommand?.CanExecute(connector) ?? false))\n                {\n                    DisconnectCommand.Execute(connector);\n                }\n            }\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "Nodify/Connections/CuttingLine.cs",
    "content": "﻿using System.Windows;\nusing System.Windows.Media;\nusing System.Windows.Shapes;\n\nnamespace Nodify\n{\n    public class CuttingLine : WpfShape\n    {\n        public static readonly StyledProperty<Point> StartPointProperty = AvaloniaProperty.Register<CuttingLine, Point>(nameof(StartPoint));\n        public static readonly StyledProperty<Point> EndPointProperty = AvaloniaProperty.Register<CuttingLine, Point>(nameof(EndPoint));\n\n        /// <summary>\n        /// Will be set for <see cref=\"BaseConnection\"/>s and custom connections when the cutting line intersects with them if <see cref=\"NodifyEditor.EnableCuttingLinePreview\"/> is true.\n        /// </summary>\n        public static readonly AttachedProperty<bool> IsOverElementProperty = PendingConnection.IsOverElementProperty.AddOwner<CuttingLine>();\n\n        public static bool GetIsOverElement(UIElement elem)\n            => (bool)elem.GetValue(IsOverElementProperty);\n\n        public static void SetIsOverElement(UIElement elem, bool value)\n            => elem.SetValue(IsOverElementProperty, value);\n\n        /// <summary>\n        /// Gets or sets whether cancelling a cutting operation is allowed.\n        /// </summary>\n        public static bool AllowCuttingCancellation { get; set; } = true;\n\n        /// <summary>\n        /// Gets or sets the start point.\n        /// </summary>\n        public Point StartPoint\n        {\n            get => (Point)GetValue(StartPointProperty);\n            set => SetValue(StartPointProperty, value);\n        }\n\n        /// <summary>\n        /// Gets or sets the end point.\n        /// </summary>\n        public Point EndPoint\n        {\n            get => (Point)GetValue(EndPointProperty);\n            set => SetValue(EndPointProperty, value);\n        }\n\n        protected override Geometry? CreateDefiningGeometry()\n        {\n            StreamGeometry _geometry = new StreamGeometry\n            {\n                //FillRule = FillRule.EvenOdd\n            };\n\n            using (StreamGeometryContext context = _geometry.Open())\n            {\n                context.BeginFigure(StartPoint, false, false);\n                context.LineTo(EndPoint, true, true);\n            }\n\n            return _geometry;\n        }\n\n        static CuttingLine()\n        {\n            DefaultStyleKeyProperty.OverrideMetadata(typeof(CuttingLine), new FrameworkPropertyMetadata(typeof(CuttingLine)));\n            IsHitTestVisibleProperty.OverrideMetadata(typeof(CuttingLine), new StyledPropertyMetadata<bool>(BoxValue.False));\n            AffectsGeometry<CuttingLine>(StartPointProperty, EndPointProperty);\n            AffectsRender<CuttingLine>(StartPointProperty, EndPointProperty);\n        }\n\n        protected override void Render(DrawingContext drawingContext)\n        {\n            base.Render(drawingContext);\n\n            drawingContext.DrawEllipse(Fill, null, StartPoint, StrokeThickness * 1.2, StrokeThickness * 1.2);\n            drawingContext.DrawEllipse(Fill, null, EndPoint, StrokeThickness * 1.2, StrokeThickness * 1.2);\n        }\n    }\n}\n"
  },
  {
    "path": "Nodify/Connections/LineConnection.cs",
    "content": "﻿using System;\nusing System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Media;\n\nnamespace Nodify\n{\n    /// <summary>\n    /// Represents a line that has an arrow indicating its <see cref=\"BaseConnection.Direction\"/>.\n    /// </summary>\n    public class LineConnection : BaseConnection\n    {\n        public static readonly StyledProperty<double> CornerRadiusProperty = AvaloniaProperty.Register<LineConnection, double>(nameof(CornerRadius), BoxValue.Double5);\n\n        /// <summary>\n        /// The radius of the corners between the line segments.\n        /// </summary>\n        public double CornerRadius\n        {\n            get => (double)GetValue(CornerRadiusProperty);\n            set => SetValue(CornerRadiusProperty, value);\n        }\n\n        static LineConnection()\n        {\n            DefaultStyleKeyProperty.OverrideMetadata(typeof(LineConnection), new FrameworkPropertyMetadata(typeof(LineConnection)));\n            NodifyEditor.CuttingConnectionTypes.Add(typeof(LineConnection));\n            AffectsGeometry<LineConnection>(CornerRadiusProperty);\n        }\n\n        protected override ((Point ArrowStartSource, Point ArrowStartTarget), (Point ArrowEndSource, Point ArrowEndTarget)) DrawLineGeometry(StreamGeometryContext context, Point source, Point target)\n        {\n            var (p0, p1) = GetLinePoints(source, target);\n\n            using var _ = context.BeginFigure(source, false, false);\n            if (CornerRadius > 0 && Spacing > 0)\n            {\n                AddSmoothCorner(context, source, p0, p1, CornerRadius);\n                AddSmoothCorner(context, p0, p1, target, CornerRadius);\n            }\n            else\n            {\n                context.LineTo(p0, true, true);\n                context.LineTo(p1, true, true);\n            }\n            context.LineTo(target, true, true);\n\n            return ((target, source), (source, target));\n        }\n\n        protected override void DrawDefaultArrowhead(StreamGeometryContext context, Point source, Point target, ConnectionDirection arrowDirection = ConnectionDirection.Forward, Orientation orientation = Orientation.Horizontal)\n        {\n            if (Spacing < 1d)\n            {\n                Vector delta = source - target;\n                double headWidth = ArrowSize.Width;\n                double headHeight = ArrowSize.Height / 2;\n\n                double angle = Math.Atan2(delta.Y, delta.X);\n                double sinT = Math.Sin(angle);\n                double cosT = Math.Cos(angle);\n\n                var from = new Point(target.X + (headWidth * cosT - headHeight * sinT), target.Y + (headWidth * sinT + headHeight * cosT));\n                var to = new Point(target.X + (headWidth * cosT + headHeight * sinT), target.Y - (headHeight * cosT - headWidth * sinT));\n\n                using var _ = context.BeginFigure(target, true, true);\n                context.LineTo(from, true, true);\n                context.LineTo(to, true, true);\n            }\n            else\n            {\n                base.DrawDefaultArrowhead(context, source, target, arrowDirection, orientation);\n            }\n        }\n\n        protected override void DrawDirectionalArrowsGeometry(StreamGeometryContext context, Point source, Point target)\n        {\n            var (p0, p1) = GetLinePoints(source, target);\n            var direction = p0 - p1;\n\n            double spacing = 1d / (DirectionalArrowsCount + 1);\n            for (int i = 1; i <= DirectionalArrowsCount; i++)\n            {\n                double t = (spacing * i + DirectionalArrowsOffset).WrapToRange(0d, 1d);\n                var to = InterpolateLineSegment(p0, p1, t);\n\n                base.DrawDirectionalArrowheadGeometry(context, direction, to);\n            }\n        }\n\n        private (Point P0, Point P1) GetLinePoints(Point source, Point target)\n        {\n            double direction = Direction == ConnectionDirection.Forward ? 1d : -1d;\n            var spacing = new Vector(Spacing * direction, 0d);\n            var spacingVertical = new Vector(spacing.Y, spacing.X);\n\n            var p0 = source + (SourceOrientation == Orientation.Vertical ? spacingVertical : spacing);\n            var p1 = target - (TargetOrientation == Orientation.Vertical ? spacingVertical : spacing);\n\n            return (p0, p1);\n        }\n\n        protected static Point InterpolateLineSegment(Point p0, Point p1, double t)\n        {\n            return (Point)((1 - t) * (Vector)p0 + t * (Vector)p1);\n        }\n\n        protected static ((Point SegmentStart, Point SegmentEnd), Point InterpolatedPoint) InterpolateLine(Point p0, Point p1, Point p2, Point p3, double t)\n        {\n            double length1 = (p1 - p0).Length();\n            double length2 = (p2 - p1).Length();\n            double length3 = (p3 - p2).Length();\n            double totalLength = length1 + length2 + length3;\n\n            double ratio1 = length1 / totalLength;\n            double ratio2 = length2 / totalLength;\n            double ratio3 = length3 / totalLength;\n\n            // Interpolate within the appropriate segment based on t\n            if (t <= ratio1)\n            {\n                return ((p0, p1), InterpolateLineSegment(p0, p1, t / ratio1));\n            }\n            else if (t <= ratio1 + ratio2)\n            {\n                return ((p1, p2), InterpolateLineSegment(p1, p2, (t - ratio1) / ratio2));\n            }\n\n            return ((p2, p3), InterpolateLineSegment(p2, p3, (t - ratio1 - ratio2) / ratio3));\n        }\n\n        protected static ((Point SegmentStart, Point SegmentEnd), Point InterpolatedPoint) InterpolateLine(Point p0, Point p1, Point p2, double t)\n        {\n            double length1 = (p1 - p0).Length();\n            double length2 = (p2 - p1).Length();\n            double totalLength = length1 + length2;\n\n            double ratio1 = length1 / totalLength;\n            double ratio2 = length2 / totalLength;\n\n            // Interpolate within the appropriate segment based on t\n            if (t <= ratio1)\n            {\n                return ((p0, p1), InterpolateLineSegment(p0, p1, t / ratio1));\n            }\n\n            return ((p1, p2), InterpolateLineSegment(p1, p2, (t - ratio1) / ratio2));\n        }\n\n        protected static void AddSmoothCorner(StreamGeometryContext context, Point start, Point corner, Point end, double radius)\n        {\n            double distAB = (corner - start).LengthSquared();\n            double distBC = (end - corner).LengthSquared();\n\n            double bendSize = Math.Sqrt(Math.Min(distAB, distBC)) / 2;\n            radius = Math.Min(bendSize, radius);\n\n            Vector directionToCorner = corner - start;\n            Vector directionFromCorner = end - corner;\n\n            if (directionToCorner.SquaredLength != 0)\n                directionToCorner = directionToCorner.Normalize();\n\n            if (directionFromCorner.SquaredLength != 0)\n                directionFromCorner = directionFromCorner.Normalize();\n\n            Point curveStart = corner - directionToCorner * radius;\n            Point curveEnd = corner + directionFromCorner * radius;\n\n            context.LineTo(curveStart, true, true);\n            context.QuadraticBezierTo(corner, curveEnd, true, true);\n        }\n    }\n}\n"
  },
  {
    "path": "Nodify/Connections/PendingConnection.Avalonia.cs",
    "content": "using Avalonia;\nusing Avalonia.Controls;\n\nnamespace Nodify\n{\n    public partial class PendingConnection\n    {\n        protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)\n        {\n            base.OnPropertyChanged(change);\n            if (change.Property == IsVisibleProperty)\n            {\n                if (change.NewValue is bool isVisible)\n                {\n                    if (isVisible)\n                    {\n                        SetCurrentValue(Visual.IsVisibleProperty, true);\n                        Opacity = 1;\n                    }\n                    else\n                    {\n                        ApplyTemplate();\n                        // hacky: we need to wait for the template to be applied before we set visible to false otherwise\n                        // the template will not be applied\n                        if (Editor == null)\n                        {\n                            Opacity = 0;\n                            return;\n                        }\n                        \n                        SetCurrentValue(Visual.IsVisibleProperty, false);\n                    }\n                }\n            }\n        }\n    }\n}"
  },
  {
    "path": "Nodify/Connections/PendingConnection.cs",
    "content": "﻿using System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Input;\nusing System.Windows.Media;\nusing System.Windows.Shapes;\n\nnamespace Nodify\n{\n    /// <summary>\n    /// Represents a pending connection usually started by a <see cref=\"Connector\"/> which invokes the <see cref=\"CompletedCommand\"/> when completed.\n    /// </summary>\n    public partial class PendingConnection : ContentControl\n    {\n        #region Dependency Properties\n\n        public static readonly StyledProperty<Point> SourceAnchorProperty = AvaloniaProperty.Register<PendingConnection, Point>(nameof(SourceAnchor), BoxValue.Point);\n        public static readonly StyledProperty<Point> TargetAnchorProperty = AvaloniaProperty.Register<PendingConnection, Point>(nameof(TargetAnchor), BoxValue.Point);\n        public static readonly StyledProperty<object> SourceProperty = AvaloniaProperty.Register<PendingConnection, object>(nameof(Source));\n        public static readonly StyledProperty<object> TargetProperty = AvaloniaProperty.Register<PendingConnection, object>(nameof(Target));\n        public static readonly StyledProperty<object> PreviewTargetProperty = AvaloniaProperty.Register<PendingConnection, object>(nameof(PreviewTarget));\n        public static readonly StyledProperty<bool> EnablePreviewProperty = AvaloniaProperty.Register<PendingConnection, bool>(nameof(EnablePreview), BoxValue.False);\n        public static readonly StyledProperty<double> StrokeThicknessProperty = Shape.StrokeThicknessProperty.AddOwner<PendingConnection>();\n        public static readonly StyledProperty<AvaloniaList<double>?> StrokeDashArrayProperty = Shape.StrokeDashArrayProperty.AddOwner<PendingConnection>();\n        public static readonly StyledProperty<IBrush?> StrokeProperty = Shape.StrokeProperty.AddOwner<PendingConnection>();\n        public static readonly StyledProperty<bool> AllowOnlyConnectorsProperty = AvaloniaProperty.Register<PendingConnection, bool>(nameof(AllowOnlyConnectors), BoxValue.True);\n        public static readonly StyledProperty<bool> EnableSnappingProperty = AvaloniaProperty.Register<PendingConnection, bool>(nameof(EnableSnapping), BoxValue.False);\n        public static readonly StyledProperty<ConnectionDirection> DirectionProperty = BaseConnection.DirectionProperty.AddOwner<PendingConnection>();\n        public new static readonly StyledProperty<bool> IsVisibleProperty = AvaloniaProperty.Register<PendingConnection, bool>(nameof(IsVisibleProperty), BoxValue.True, defaultBindingMode: BindingMode.TwoWay);\n        \n        /// <summary>\n        /// Gets or sets the starting point for the connection.\n        /// </summary>\n        public Point SourceAnchor\n        {\n            get => (Point)GetValue(SourceAnchorProperty);\n            set => SetValue(SourceAnchorProperty, value);\n        }\n\n        /// <summary>\n        /// Gets or sets the end point for the connection.\n        /// </summary>\n        public Point TargetAnchor\n        {\n            get => (Point)GetValue(TargetAnchorProperty);\n            set => SetValue(TargetAnchorProperty, value);\n        }\n\n        /// <summary>\n        /// Gets or sets the <see cref=\"Connector\"/>'s <see cref=\"FrameworkElement.DataContext\"/> that started this pending connection.\n        /// </summary>\n        public object? Source\n        {\n            get => GetValue(SourceProperty);\n            set => SetValue(SourceProperty, value);\n        }\n\n        /// <summary>\n        /// Gets or sets the <see cref=\"Connector\"/>'s <see cref=\"FrameworkElement.DataContext\"/> (or potentially an <see cref=\"ItemContainer\"/>'s <see cref=\"FrameworkElement.DataContext\"/> if <see cref=\"AllowOnlyConnectors\"/> is false) that the <see cref=\"Source\"/> can connect to.\n        /// Only set when the connection is completed (see <see cref=\"CompletedCommand\"/>).\n        /// </summary>\n        public object? Target\n        {\n            get => GetValue(TargetProperty);\n            set => SetValue(TargetProperty, value);\n        }\n\n        /// <summary>\n        /// <see cref=\"PreviewTarget\"/> will be updated with a potential <see cref=\"Connector\"/>'s <see cref=\"FrameworkElement.DataContext\"/> if this is true.\n        /// </summary>\n        public bool EnablePreview\n        {\n            get => (bool)GetValue(EnablePreviewProperty);\n            set => SetValue(EnablePreviewProperty, value);\n        }\n\n        /// <summary>\n        /// Gets or sets the <see cref=\"Connector\"/> or the <see cref=\"ItemContainer\"/> (if <see cref=\"AllowOnlyConnectors\"/> is false) that we're previewing.\n        /// </summary>\n        public object? PreviewTarget\n        {\n            get => GetValue(PreviewTargetProperty);\n            set => SetValue(PreviewTargetProperty, value);\n        }\n\n        /// <summary>\n        /// Enables snapping the <see cref=\"TargetAnchor\"/> to a possible <see cref=\"Target\"/> connector.\n        /// </summary>\n        public bool EnableSnapping\n        {\n            get => (bool)GetValue(EnableSnappingProperty);\n            set => SetValue(EnableSnappingProperty, value);\n        }\n\n        /// <summary>\n        /// If true will preview and connect only to <see cref=\"Connector\"/>s, otherwise will also enable <see cref=\"ItemContainer\"/>s.\n        /// </summary>\n        public bool AllowOnlyConnectors\n        {\n            get => (bool)GetValue(AllowOnlyConnectorsProperty);\n            set => SetValue(AllowOnlyConnectorsProperty, value);\n        }\n\n        /// <summary>\n        /// Gets or set the connection thickness.\n        /// </summary>\n        public double StrokeThickness\n        {\n            get => (double)GetValue(StrokeThicknessProperty);\n            set => SetValue(StrokeThicknessProperty, value);\n        }\n\n        /// <summary>\n        /// Gets or sets the pattern of dashes and gaps that is used to outline the connection.\n        /// </summary>\n        public AvaloniaList<double>? StrokeDashArray\n        {\n            get => GetValue(StrokeDashArrayProperty);\n            set => SetValue(StrokeDashArrayProperty, value);\n        }\n\n        /// <summary>\n        /// Gets or sets the stroke color of the connection.\n        /// </summary>\n        public IBrush Stroke\n        {\n            get => (IBrush?)GetValue(StrokeProperty);\n            set => SetValue(StrokeProperty, value);\n        }\n\n        /// <summary>\n        /// Gets or sets the visibility of the connection.\n        /// </summary>\n        public new bool IsVisible\n        {\n            get => (bool)GetValue(IsVisibleProperty);\n            set => SetValue(IsVisibleProperty, value);\n        }\n\n        /// <summary>\n        /// Gets or sets the direction of this connection.\n        /// </summary>\n        public ConnectionDirection Direction\n        {\n            get => (ConnectionDirection)GetValue(DirectionProperty);\n            set => SetValue(DirectionProperty, value);\n        }\n\n        #endregion\n\n        #region Attached Properties\n\n        private static readonly AttachedProperty<bool> AllowOnlyConnectorsAttachedProperty = AvaloniaProperty.RegisterAttached<PendingConnection, Control, bool>(\"AllowOnlyConnectorsAttached\", true);\n        /// <summary>\n        /// Will be set for <see cref=\"Connector\"/>s and <see cref=\"ItemContainer\"/>s when the pending connection is over the element if <see cref=\"EnablePreview\"/> or <see cref=\"EnableSnapping\"/> is true.\n        /// </summary>\n        public static readonly AttachedProperty<bool> IsOverElementProperty = AvaloniaProperty.RegisterAttached<PendingConnection, Control, bool>(\"IsOverElement\");\n\n        internal static bool GetAllowOnlyConnectorsAttached(UIElement elem)\n            => (bool)elem.GetValue(AllowOnlyConnectorsAttachedProperty);\n\n        internal static void SetAllowOnlyConnectorsAttached(UIElement elem, bool value)\n            => elem.SetValue(AllowOnlyConnectorsAttachedProperty, value);\n\n        public static bool GetIsOverElement(UIElement elem)\n            => (bool)elem.GetValue(IsOverElementProperty);\n\n        public static void SetIsOverElement(UIElement elem, bool value)\n            => elem.SetValue(IsOverElementProperty, value);\n\n        private static void OnAllowOnlyConnectorsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)\n        {\n            NodifyEditor? editor = ((PendingConnection)d).Editor;\n\n            if (editor != null)\n            {\n                SetAllowOnlyConnectorsAttached(editor, (bool)e.NewValue);\n            }\n        }\n\n        #endregion\n\n        #region Commands\n\n        public static readonly StyledProperty<ICommand> StartedCommandProperty = AvaloniaProperty.Register<PendingConnection, ICommand>(nameof(StartedCommand));\n        public static readonly StyledProperty<ICommand> CompletedCommandProperty = AvaloniaProperty.Register<PendingConnection, ICommand>(nameof(CompletedCommand));\n\n        /// <summary>\n        /// Gets or sets the command to invoke when the pending connection is started.\n        /// Will not be invoked if <see cref=\"NodifyEditor.ConnectionStartedCommand\"/> is used.\n        /// <see cref=\"Source\"/> will be set to the <see cref=\"Connector\"/>'s <see cref=\"FrameworkElement.DataContext\"/> that started this connection and will also be the command's parameter.\n        /// </summary>\n        public ICommand? StartedCommand\n        {\n            get => (ICommand?)GetValue(StartedCommandProperty);\n            set => SetValue(StartedCommandProperty, value);\n        }\n\n        /// <summary>\n        /// Gets or sets the command to invoke when the pending connection is completed.\n        /// Will not be invoked if <see cref=\"NodifyEditor.ConnectionCompletedCommand\"/> is used.\n        /// <see cref=\"Target\"/> will be set to the desired <see cref=\"Connector\"/>'s <see cref=\"FrameworkElement.DataContext\"/> and will also be the command's parameter.\n        /// </summary>\n        public ICommand? CompletedCommand\n        {\n            get => (ICommand?)GetValue(CompletedCommandProperty);\n            set => SetValue(CompletedCommandProperty, value);\n        }\n\n        #endregion\n\n        #region Fields\n\n        /// <summary>\n        /// Gets the <see cref=\"NodifyEditor\"/> that owns this <see cref=\"PendingConnection\"/>.\n        /// </summary>\n        protected NodifyEditor? Editor { get; private set; }\n\n        private FrameworkElement? _previousConnector;\n\n        #endregion\n\n        static PendingConnection()\n        {\n            DefaultStyleKeyProperty.OverrideMetadata(typeof(PendingConnection), new FrameworkPropertyMetadata(typeof(PendingConnection)));\n            AffectsRender<PendingConnection>(SourceAnchorProperty, TargetAnchorProperty);\n            AllowOnlyConnectorsProperty.Changed.AddClassHandler<PendingConnection>(OnAllowOnlyConnectorsChanged);\n            IsOverElementProperty.Changed.AddClassHandler<Control>((c, e) =>\n            {\n                c.Classes.Set(\"isOverElement\", (bool)e.NewValue);\n            });\n        }\n\n        /// <inheritdoc />\n        protected override void OnApplyTemplate(TemplateAppliedEventArgs e)\n        {\n            base.OnApplyTemplate(e);\n\n            Editor = this.GetParentOfType<NodifyEditor>();\n\n            if (Editor != null)\n            {\n                Editor.AddHandler(Connector.PendingConnectionStartedEvent, new PendingConnectionEventHandler(OnPendingConnectionStarted));\n                Editor.AddHandler(Connector.PendingConnectionDragEvent, new PendingConnectionEventHandler(OnPendingConnectionDrag));\n                Editor.AddHandler(Connector.PendingConnectionCompletedEvent, new PendingConnectionEventHandler(OnPendingConnectionCompleted));\n                SetAllowOnlyConnectorsAttached(Editor, AllowOnlyConnectors);\n            }\n        }\n\n        #region Event Handlers\n\n        protected virtual void OnPendingConnectionStarted(object? sender, PendingConnectionEventArgs e)\n        {\n            if (!e.Handled && !e.Canceled)\n            {\n                e.Handled = true;\n                e.Canceled = !StartedCommand?.CanExecute(e.SourceConnector) ?? false;\n\n                SetCurrentValue(TargetProperty, null);\n                IsVisible = !e.Canceled;\n                SetCurrentValue(SourceAnchorProperty, e.Anchor);\n                SetCurrentValue(TargetAnchorProperty, new Point(e.Anchor.X + e.OffsetX, e.Anchor.Y + e.OffsetY));\n                SetCurrentValue(SourceProperty, e.SourceConnector);\n\n                if (!e.Canceled)\n                {\n                    StartedCommand?.Execute(Source);\n                }\n\n                if(EnablePreview)\n                {\n                    SetCurrentValue(PreviewTargetProperty, e.SourceConnector);\n                }\n            }\n        }\n\n        protected virtual void OnPendingConnectionDrag(object? sender, PendingConnectionEventArgs e)\n        {\n            if (!e.Handled && IsVisible)\n            {\n                e.Handled = true;\n                SetCurrentValue(TargetAnchorProperty, new Point(e.Anchor.X + e.OffsetX, e.Anchor.Y + e.OffsetY));\n\n                if (Editor != null && (EnablePreview || EnableSnapping))\n                {\n                    // Look for a potential connector\n                    FrameworkElement? connector = GetPotentialConnector(Editor, AllowOnlyConnectors, e);\n\n                    // Update the connector's anchor and snap to it if snapping is enabled\n                    if (EnableSnapping && connector is Connector target)\n                    {\n                        target.UpdateAnchor();\n                        SetCurrentValue(TargetAnchorProperty, target.Anchor);\n                    }\n\n                    // If it's not the same connector\n                    if (connector != _previousConnector)\n                    {\n                        if (_previousConnector != null)\n                        {\n                            SetIsOverElement(_previousConnector, false);\n                        }\n\n                        // And we have a connector\n                        if (connector != null)\n                        {\n                            SetIsOverElement(connector, true);\n                        }\n\n                        // Update the preview target if enabled\n                        if (EnablePreview)\n                        {\n                            SetCurrentValue(PreviewTargetProperty, connector?.DataContext);\n                        }\n\n                        _previousConnector = connector;\n                    }\n                }\n            }\n        }\n\n        protected virtual void OnPendingConnectionCompleted(object? sender, PendingConnectionEventArgs e)\n        {\n            if (!e.Handled && IsVisible)\n            {\n                e.Handled = true;\n                IsVisible = false;\n\n                if (_previousConnector != null)\n                {\n                    SetIsOverElement(_previousConnector, false);\n                    _previousConnector = null;\n                }\n\n                if (!e.Canceled)\n                {\n                    SetCurrentValue(TargetProperty, e.TargetConnector);\n\n                    // Invoke the CompletedCommand if event is not handled\n                    if (CompletedCommand?.CanExecute(Target) ?? false)\n                    {\n                        CompletedCommand?.Execute(Target);\n                    }\n                }\n\n                if(EnablePreview)\n                {\n                    SetCurrentValue(PreviewTargetProperty, null);\n                }\n            }\n        }\n\n        #endregion\n\n        #region Helpers\n\n        /// <summary>Searches for a potential connector prioritizing <see cref=\"Connector\"/>s</summary>\n        /// <param name=\"editor\">The editor to scan for connectors or item containers.</param>\n        /// <param name=\"allowOnlyConnectors\">Will also look for <see cref=\"ItemContainer\"/>s if false.</param>\n        /// <returns>A connector, an item container, the editor or null.</returns>\n        internal static FrameworkElement? GetPotentialConnector(NodifyEditor editor, bool allowOnlyConnectors, EventArgs? e)\n        {\n            Point position = default;\n            if (e is MouseEventArgs mouseEventArgs)\n                position = mouseEventArgs.GetPosition(editor.ItemsHost);\n            else if (e is MouseButtonEventArgs mouseButtonEventArgs)\n                position = mouseButtonEventArgs.GetPosition(editor.ItemsHost);\n            else if (e is PendingConnectionEventArgs pendingConnectionEventArgs)\n                position = pendingConnectionEventArgs.GetPosition(editor.ItemsHost);\n            \n            Connector? connector = editor.ItemsHost.GetElementUnderMouse<Connector>(position);\n            if (connector != null && connector.Editor == editor)\n                return connector;\n\n            if (allowOnlyConnectors)\n                return null;\n\n            var itemContainer = editor.ItemsHost.GetElementUnderMouse<ItemContainer>(position);\n            if (itemContainer != null && itemContainer.Editor == editor)\n                return itemContainer;\n\n            return editor;\n        }\n\n        #endregion\n    }\n}"
  },
  {
    "path": "Nodify/Connections/StepConnection.cs",
    "content": "﻿using System;\nusing System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Media;\n\nnamespace Nodify\n{\n    public enum ConnectorPosition\n    {\n        Top,\n        Left,\n        Bottom,\n        Right\n    }\n\n    public class StepConnection : LineConnection\n    {\n        public static readonly AvaloniaProperty<ConnectorPosition> SourcePositionProperty = AvaloniaProperty.Register<StepConnection, ConnectorPosition>(nameof(SourcePosition), ConnectorPosition.Right);\n        public static readonly AvaloniaProperty<ConnectorPosition> TargetPositionProperty = AvaloniaProperty.Register<StepConnection, ConnectorPosition>(nameof(TargetPosition), ConnectorPosition.Left);\n\n        private static void OnConnectorPositionChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)\n        {\n            var connection = (StepConnection)d;\n            connection.CoerceValue(DirectionProperty);\n            connection.CoerceValue(SourceOrientationProperty);\n            connection.CoerceValue(TargetOrientationProperty);\n        }\n\n        static StepConnection()\n        {\n            SourcePositionProperty.Changed.AddClassHandler<StepConnection>(OnConnectorPositionChanged);\n            TargetPositionProperty.Changed.AddClassHandler<StepConnection>(OnConnectorPositionChanged);\n            AffectsRender<StepConnection>(SourcePositionProperty, TargetPositionProperty);\n            SourceOrientationProperty.OverrideMetadata<StepConnection>(new StyledPropertyMetadata<Orientation>(defaultValue: Orientation.Horizontal, coerce: CoerceSourceOrientation));\n            TargetOrientationProperty.OverrideMetadata<StepConnection>(new StyledPropertyMetadata<Orientation>(defaultValue: Orientation.Horizontal, coerce: CoerceTargetOrientation));\n            DirectionProperty.OverrideMetadata<StepConnection>(new StyledPropertyMetadata<ConnectionDirection>(defaultValue: ConnectionDirection.Forward, coerce: CoerceConnectionDirection));\n            NodifyEditor.CuttingConnectionTypes.Add(typeof(StepConnection));\n        }\n\n        private static Orientation CoerceSourceOrientation(DependencyObject d, Orientation baseValue)\n        {\n            var connection = (StepConnection)d;\n            return connection.SourcePosition == ConnectorPosition.Left || connection.SourcePosition == ConnectorPosition.Right\n                ? Orientation.Horizontal\n                : Orientation.Vertical;\n        }\n\n        private static Orientation CoerceTargetOrientation(DependencyObject d, Orientation baseValue)\n        {\n            var connection = (StepConnection)d;\n            return connection.TargetPosition == ConnectorPosition.Left || connection.TargetPosition == ConnectorPosition.Right\n                ? Orientation.Horizontal\n                : Orientation.Vertical;\n        }\n\n        private static ConnectionDirection CoerceConnectionDirection(DependencyObject d, ConnectionDirection baseValue)\n        {\n            var connection = (StepConnection)d;\n            return connection.TargetPosition == ConnectorPosition.Left || connection.TargetPosition == ConnectorPosition.Top\n               ? ConnectionDirection.Forward\n               : ConnectionDirection.Backward;\n        }\n\n        /// <summary>\n        /// Gets or sets the position of the source connector.\n        /// </summary>\n        public ConnectorPosition SourcePosition\n        {\n            get => (ConnectorPosition)GetValue(SourcePositionProperty);\n            set => SetValue(SourcePositionProperty, value);\n        }\n\n        /// <summary>\n        /// Gets or sets the position of the target connector.\n        /// </summary>\n        public ConnectorPosition TargetPosition\n        {\n            get => (ConnectorPosition)GetValue(TargetPositionProperty);\n            set => SetValue(TargetPositionProperty, value);\n        }\n\n        protected override ((Point ArrowStartSource, Point ArrowStartTarget), (Point ArrowEndSource, Point ArrowEndTarget)) DrawLineGeometry(StreamGeometryContext context, Point source, Point target)\n        {\n            var (p0, p1, p2, p3) = GetLinePoints(source, target);\n\n            if (CornerRadius > 0)\n            {\n                DrawSmoothLine(context);\n            }\n            else\n            {\n                DrawDefaultLine(context);\n            }\n\n            if (Spacing < 1d)\n            {\n                return ((p1, source), (p2, target));\n            }\n\n            return ((target, source), (source, target));\n\n            void DrawDefaultLine(StreamGeometryContext context)\n            {\n                context.BeginFigure(source, false, false);\n                context.LineTo(p0, true, true);\n                context.LineTo(p1, true, true);\n                context.LineTo(p2, true, true);\n                context.LineTo(p3, true, true);\n                context.LineTo(target, true, true);\n            }\n\n            void DrawSmoothLine(StreamGeometryContext context)\n            {\n                context.BeginFigure(source, false, false);\n                AddSmoothCorner(context, source, p0, p1, CornerRadius);\n\n                if (p1 == p2)\n                {\n                    // skip p1 or p2 because they overlap\n                    AddSmoothCorner(context, p0, p1, p3, CornerRadius);\n                }\n                else\n                {\n                    AddSmoothCorner(context, p0, p1, p2, CornerRadius);\n                    AddSmoothCorner(context, p1, p2, p3, CornerRadius);\n                }\n\n                AddSmoothCorner(context, p2, p3, target, CornerRadius);\n                context.LineTo(target, true, true);\n            }\n        }\n\n        protected override Point GetTextPosition(FormattedText text, Point source, Point target)\n        {\n            var (p0, p1, p2, p3) = GetLinePoints(source, target);\n\n            Vector delta1 = p1 - p0;\n            Vector delta2 = p2 - p1;\n            Vector delta3 = p3 - p2;\n\n            var max = GetMax(delta1, GetMax(delta2, delta3));\n\n            if (max == delta1)\n            {\n                return new Point((p0.X + p1.X - text.Width) / 2, (p0.Y + p1.Y - text.Height) / 2);\n            }\n            else if (max == delta2)\n            {\n                return new Point((p2.X + p1.X - text.Width) / 2, (p2.Y + p1.Y - text.Height) / 2);\n            }\n\n            return new Point((p3.X + p2.X - text.Width) / 2, (p3.Y + p2.Y - text.Height) / 2);\n\n            static Vector GetMax(in Vector a, in Vector b)\n                => a.SquaredLength > b.SquaredLength ? a : b;\n        }\n\n        protected override void DrawDirectionalArrowsGeometry(StreamGeometryContext context, Point source, Point target)\n        {\n            var (p0, p1, p2, p3) = GetLinePoints(source, target);\n\n            double spacing = 1d / (DirectionalArrowsCount + 1);\n            for (int i = 1; i <= DirectionalArrowsCount; i++)\n            {\n                double t = (spacing * i + DirectionalArrowsOffset).WrapToRange(0d, 1d);\n                var (segment, to) = InterpolateLine(p0, p1, p2, p3, t);\n\n                var direction = segment.SegmentStart - segment.SegmentEnd;\n                base.DrawDirectionalArrowheadGeometry(context, direction, to);\n            }\n        }\n\n        private (Point P0, Point P1, Point P2, Point P3) GetLinePoints(Point source, Point target)\n        {\n            var sourceDir = GetConnectorDirection(SourcePosition);\n            var targetDir = GetConnectorDirection(TargetPosition);\n\n            Point startPoint = source + new Vector(Spacing * sourceDir.X, Spacing * sourceDir.Y);\n            Point endPoint = target + new Vector(Spacing * targetDir.X, Spacing * targetDir.Y);\n\n            var connectionDir = GetConnectionDirection(startPoint, SourcePosition, endPoint);\n            bool horizontalConnection = connectionDir.X != 0;\n\n            if (IsOppositePosition(SourcePosition, TargetPosition))\n            {\n                var (p1, p2) = GetOppositePositionPoints();\n                return (startPoint, p1, p2, endPoint);\n            }\n\n            // same: left to left / top to top etc\n            if (SourcePosition == TargetPosition)\n            {\n                var p = GetSamePositionPoint();\n                return (startPoint, p, p, endPoint);\n            }\n\n            // mixed: right to bottom / left to top etc\n            bool isSameDir = horizontalConnection ? sourceDir.X == targetDir.Y : sourceDir.Y == targetDir.X;\n            bool startGreaterThanEnd = horizontalConnection ? startPoint.Y > endPoint.Y : startPoint.X > endPoint.X;\n\n            bool positiveDir = horizontalConnection ? sourceDir.X == 1 : sourceDir.Y == 1;\n            bool shouldFlip = positiveDir\n                ? isSameDir ? !startGreaterThanEnd : startGreaterThanEnd\n                : isSameDir ? startGreaterThanEnd : !startGreaterThanEnd;\n\n            if (shouldFlip)\n            {\n                var sourceTarget = new Point(startPoint.X, endPoint.Y);\n                var targetSource = new Point(endPoint.X, startPoint.Y);\n\n                var pf = horizontalConnection ? sourceTarget : targetSource;\n                return (startPoint, pf, pf, endPoint);\n            }\n\n            var pp = GetSamePositionPoint();\n            return (startPoint, pp, pp, endPoint);\n\n            (Point P1, Point P2) GetOppositePositionPoints()\n            {\n                var center = startPoint + (endPoint - startPoint) / 2;\n\n                (Point P1, Point P2) verticalSplit = (new Point(center.X, startPoint.Y), new Point(center.X, endPoint.Y));\n                (Point P1, Point P2) horizontalSplit = (new Point(startPoint.X, center.Y), new Point(endPoint.X, center.Y));\n\n                if (horizontalConnection)\n                {\n                    // left to right / right to left\n                    return sourceDir.X == connectionDir.X ? verticalSplit : horizontalSplit;\n                }\n\n                // top to bottom / bottom to top\n                return sourceDir.Y == connectionDir.Y ? horizontalSplit : verticalSplit;\n            }\n\n            Point GetSamePositionPoint()\n            {\n                var sourceTarget = new Point(startPoint.X, endPoint.Y);\n                var targetSource = new Point(endPoint.X, startPoint.Y);\n\n                if (horizontalConnection)\n                {\n                    // left to left / right to right\n                    return sourceDir.X == connectionDir.X ? targetSource : sourceTarget;\n                }\n\n                // top to top / bottom to bottom\n                return sourceDir.Y == connectionDir.Y ? sourceTarget : targetSource;\n            }\n\n            static Point GetConnectionDirection(in Point source, ConnectorPosition sourcePosition, in Point target)\n            {\n                return sourcePosition == ConnectorPosition.Left || sourcePosition == ConnectorPosition.Right\n                    ? new Point(Math.Sign(target.X - source.X), 0)\n                    : new Point(0, Math.Sign(target.Y - source.Y));\n            }\n\n            static Point GetConnectorDirection(ConnectorPosition position)\n                => position switch\n                {\n                    ConnectorPosition.Top => new Point(0, -1),\n                    ConnectorPosition.Left => new Point(-1, 0),\n                    ConnectorPosition.Bottom => new Point(0, 1),\n                    ConnectorPosition.Right => new Point(1, 0),\n                    _ => default,\n                };\n\n            static bool IsOppositePosition(ConnectorPosition sourcePosition, ConnectorPosition targetPosition)\n            {\n                return sourcePosition == ConnectorPosition.Left && targetPosition == ConnectorPosition.Right\n                    || sourcePosition == ConnectorPosition.Right && targetPosition == ConnectorPosition.Left\n                    || sourcePosition == ConnectorPosition.Top && targetPosition == ConnectorPosition.Bottom\n                    || sourcePosition == ConnectorPosition.Bottom && targetPosition == ConnectorPosition.Top;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "Nodify/DecoratorContainer.cs",
    "content": "﻿using System.Windows;\nusing System.Windows.Controls;\n\nnamespace Nodify\n{\n    /// <summary>\n    /// The container for all the items generated from the <see cref=\"NodifyEditor.Decorators\"/> collection.\n    /// </summary>\n    public class DecoratorContainer : ContentControl, INodifyCanvasItem\n    {\n        #region Dependency Properties\n\n        public static readonly StyledProperty<Point> LocationProperty = ItemContainer.LocationProperty.AddOwner<DecoratorContainer>();\n        public static readonly StyledProperty<Size> ActualSizeProperty = ItemContainer.ActualSizeProperty.AddOwner<DecoratorContainer>();\n\n        /// <summary>\n        /// Gets or sets the location of this <see cref=\"DecoratorContainer\"/> inside the <see cref=\"NodifyEditor.DecoratorsHost\"/>.\n        /// </summary>\n        public Point Location\n        {\n            get => (Point)GetValue(LocationProperty);\n            set => SetValue(LocationProperty, value);\n        }\n\n        /// <summary>\n        /// Gets the actual size of this <see cref=\"DecoratorContainer\"/>.\n        /// </summary>\n        public Size ActualSize\n        {\n            get => (Size)GetValue(ActualSizeProperty);\n            set => SetValue(ActualSizeProperty, value);\n        }\n\n        private static void OnLocationChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)\n        {\n            var item = (DecoratorContainer)d;\n            item.OnLocationChanged();\n        }\n\n        #endregion\n\n        #region Routed Events\n\n        public static readonly RoutedEvent<RoutedEventArgs> LocationChangedEvent = RoutedEvent.Register<RoutedEventArgs>(nameof(LocationChanged), RoutingStrategies.Bubble, typeof(DecoratorContainer));\n\n        /// <summary>\n        /// Occurs when the <see cref=\"Location\"/> of this <see cref=\"DecoratorContainer\"/> is changed.\n        /// </summary>\n        public event RoutedEventHandler LocationChanged\n        {\n            add => AddHandler(LocationChangedEvent, value);\n            remove => RemoveHandler(LocationChangedEvent, value);\n        }\n\n        /// <summary>\n        /// Raises the <see cref=\"LocationChangedEvent\"/>.\n        /// </summary>\n        protected void OnLocationChanged()\n        {\n            RaiseEvent(new RoutedEventArgs(LocationChangedEvent, this));\n        }\n\n        #endregion\n\n        static DecoratorContainer()\n        {\n            DefaultStyleKeyProperty.OverrideMetadata(typeof(DecoratorContainer), new FrameworkPropertyMetadata(typeof(DecoratorContainer)));\n            LocationProperty.OverrideMetadata<DecoratorContainer>(new StyledPropertyMetadata<Point>(default, BindingMode.TwoWay));\n            PanelUtilities.AffectsParentArrange<DecoratorContainer>(LocationProperty);\n            LocationProperty.Changed.AddClassHandler<DecoratorContainer>(OnLocationChanged);\n        }\n\n        protected override void OnSizeChanged(SizeChangedInfo sizeInfo)\n        {\n            base.OnSizeChanged(sizeInfo);\n            SetCurrentValue(ActualSizeProperty, sizeInfo.NewSize);\n        }\n    }\n}\n"
  },
  {
    "path": "Nodify/DecoratorsControl.cs",
    "content": "﻿using System.Windows;\nusing System.Windows.Controls;\n\nnamespace Nodify\n{\n    /// <summary>\n    /// An <see cref=\"ItemsControl\"/> that works with <see cref=\"DecoratorContainer\"/>s.\n    /// </summary>\n    internal class DecoratorsControl : ItemsControl\n    {\n        protected override Type StyleKeyOverride => typeof(ItemsControl);\n\n        /// <inheritdoc />\n        protected override bool NeedsContainerOverride(object? item, int index, out object? recycleKey) \n            => NeedsContainer<DecoratorContainer>(item, out recycleKey);\n\n        /// <inheritdoc />\n        protected override Control CreateContainerForItemOverride(object? item, int index, object? recycleKey) \n            => new DecoratorContainer();\n    }\n}\n"
  },
  {
    "path": "Nodify/EditorCommands.cs",
    "content": "﻿using System;\nusing System.Collections;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Windows;\nusing System.Windows.Controls.Primitives;\nusing System.Windows.Input;\n\nnamespace Nodify\n{\n    public static class EditorCommands\n    {\n        /// <summary>\n        /// Specifies the possible alignment values used by the <see cref=\"Align\"/> command.\n        /// </summary>\n        public enum Alignment\n        {\n            Top,\n            Left,\n            Bottom,\n            Right,\n            Middle,\n            Center\n        }\n\n        /// <summary>\n        /// Zoom in relative to the editor's viewport center.\n        /// </summary>\n        public static RoutedUICommand ZoomIn { get; } = new RoutedUICommand(\"Zoom in\", nameof(ZoomIn), typeof(EditorCommands), new InputGestureCollection\n        {\n           EditorGestures.Mappings.Editor.ZoomIn\n        });\n\n        /// <summary>\n        /// Zoom out relative to the editor's viewport center.\n        /// </summary>\n        public static RoutedUICommand ZoomOut { get; } = new RoutedUICommand(\"Zoom out\", nameof(ZoomOut), typeof(EditorCommands), new InputGestureCollection\n        {\n            EditorGestures.Mappings.Editor.ZoomOut\n        });\n\n        /// <summary>\n        /// Select all <see cref=\"ItemContainer\"/>s in the <see cref=\"NodifyEditor\"/>.\n        /// </summary>\n        public static RoutedUICommand SelectAll { get; } = ApplicationCommands.SelectAll;\n\n        /// <summary>\n        /// Moves the <see cref=\"NodifyEditor.ViewportLocation\"/> to the specified location.\n        /// Parameter is a <see cref=\"Point\"/> or a string that can be converted to a point.\n        /// </summary>\n        public static RoutedUICommand BringIntoView { get; } = new RoutedUICommand(\"Bring location into view\", nameof(BringIntoView), typeof(EditorCommands), new InputGestureCollection\n        {\n            EditorGestures.Mappings.Editor.ResetViewportLocation\n        });\n\n        /// <summary>\n        /// Scales the editor's viewport to fit all the <see cref=\"ItemContainer\"/>s if that's possible.\n        /// </summary>\n        public static RoutedUICommand FitToScreen { get; } = new RoutedUICommand(\"Fit to screen\", nameof(FitToScreen), typeof(EditorCommands), new InputGestureCollection\n        {\n            EditorGestures.Mappings.Editor.FitToScreen\n        });\n\n        /// <summary>\n        /// Aligns <see cref=\"NodifyEditor.SelectedItems\"/> using the specified alignment method.\n        /// Parameter is of type <see cref=\"Alignment\"/> or a string that can be converted to an alignment.\n        /// </summary>\n        public static RoutedUICommand Align { get; } = new RoutedUICommand(\"Align\", nameof(Align), typeof(EditorCommands));\n\n        internal static void Register(Type type)\n        {\n            CommandManager.RegisterClassCommandBinding(type, new CommandBinding(ZoomIn, OnZoomIn, OnQueryStatusZoomIn));\n            CommandManager.RegisterClassCommandBinding(type, new CommandBinding(ZoomOut, OnZoomOut, OnQueryStatusZoomOut));\n            CommandManager.RegisterClassCommandBinding(type, new CommandBinding(SelectAll, OnSelectAll, OnQuerySelectAllStatus));\n            CommandManager.RegisterClassCommandBinding(type, new CommandBinding(BringIntoView, OnBringIntoView, OnQueryBringIntoViewStatus));\n            CommandManager.RegisterClassCommandBinding(type, new CommandBinding(FitToScreen, OnFitToScreen, OnQueryFitToScreenStatus));\n            CommandManager.RegisterClassCommandBinding(type, new CommandBinding(Align, OnAlign, OnQueryAlignStatus));\n        }\n\n        private static void OnQueryAlignStatus(object? sender, CanExecuteRoutedEventArgs e)\n        {\n            if (sender is NodifyEditor editor)\n            {\n                e.CanExecute = editor.HasManyItemsSelected;\n            }\n        }\n\n        private static void OnAlign(object? sender, ExecutedRoutedEventArgs e)\n        {\n            if (sender is NodifyEditor editor)\n            {\n                var selected = editor.InternalSelectedItems;\n                if (selected.Count > 0)\n                {\n                    if (editor.ItemsDragStartedCommand?.CanExecute(null) ?? false)\n                    {\n                        editor.ItemsDragStartedCommand.Execute(null);\n                    }\n\n                    var containers = new List<ItemContainer>(selected.Count);\n\n                    for (var i = 0; i < selected.Count; i++)\n                    {\n                        containers.Add((ItemContainer)editor.ContainerFromItem(selected[i]));\n                    }\n\n                    if (e.Parameter is Alignment alignment)\n                    {\n                        AlignContainers(containers, alignment, e.Source as ItemContainer);\n                    }\n                    else if (e.Parameter is string str && Enum.TryParse(str, true, out alignment))\n                    {\n                        AlignContainers(containers, alignment, e.Source as ItemContainer);\n                    }\n                    else\n                    {\n                        AlignContainers(containers, Alignment.Top, e.Source as ItemContainer);\n                    }\n\n                    if (editor.ItemsDragCompletedCommand?.CanExecute(null) ?? false)\n                    {\n                        editor.ItemsDragCompletedCommand.Execute(null);\n                    }\n                }\n            }\n        }\n\n        private static void AlignContainers(List<ItemContainer> containers, Alignment alignment, ItemContainer? instigator = default)\n        {\n            switch (alignment)\n            {\n                case Alignment.Top:\n                    double top = instigator?.Location.Y ?? containers.Min(x => x.Location.Y);\n                    containers.ForEach(c => c.Location = new Point(c.Location.X, top));\n                    break;\n\n                case Alignment.Left:\n                    double left = instigator?.Location.X ?? containers.Min(x => x.Location.X);\n                    containers.ForEach(c => c.Location = new Point(left, c.Location.Y));\n                    break;\n\n                case Alignment.Bottom:\n                    double bottom = instigator != null ? instigator.Location.Y + instigator.Bounds.Height : containers.Max(x => x.Location.Y + x.Bounds.Height);\n                    containers.ForEach(c => c.Location = new Point(c.Location.X, bottom - c.Bounds.Height));\n                    break;\n\n                case Alignment.Right:\n                    double right = instigator != null ? instigator.Location.X + instigator.Bounds.Width : containers.Max(x => x.Location.X + x.Bounds.Width);\n                    containers.ForEach(c => c.Location = new Point(right - c.Bounds.Width, c.Location.Y));\n                    break;\n\n                case Alignment.Middle:\n                    double mid = instigator != null ? instigator.Location.Y + instigator.Bounds.Height / 2 : containers.Average(c => c.Location.Y + c.Bounds.Height / 2);\n                    containers.ForEach(c => c.Location = new Point(c.Location.X, mid - c.Bounds.Height / 2));\n                    break;\n\n                case Alignment.Center:\n                    double center = instigator != null ? instigator.Location.X + instigator.Bounds.Width / 2 : containers.Average(c => c.Location.X + c.Bounds.Width / 2);\n                    containers.ForEach(c => c.Location = new Point(center - c.Bounds.Width / 2, c.Location.Y));\n                    break;\n\n                default:\n                    throw new ArgumentOutOfRangeException(nameof(alignment), alignment, null);\n            }\n        }\n\n        private static void OnQueryBringIntoViewStatus(object? sender, CanExecuteRoutedEventArgs e)\n        {\n            if (sender is NodifyEditor editor)\n            {\n                e.CanExecute = !editor.DisablePanning;\n            }\n        }\n\n        private static void OnBringIntoView(object? sender, ExecutedRoutedEventArgs e)\n        {\n            if (sender is NodifyEditor editor)\n            {\n                switch (e.Parameter)\n                {\n                    case Point location:\n                        editor.BringIntoView(location);\n                        break;\n                    case string str:\n                        editor.BringIntoView(Point.Parse(str));\n                        break;\n                    default:\n                        editor.BringIntoView(new Point());\n                        break;\n                }\n            }\n        }\n\n        private static void OnQueryFitToScreenStatus(object? sender, CanExecuteRoutedEventArgs e)\n        {\n            if (sender is NodifyEditor editor)\n            {\n                e.CanExecute = editor.HasItems;\n            }\n        }\n\n        private static void OnFitToScreen(object? sender, ExecutedRoutedEventArgs e)\n        {\n            if (sender is NodifyEditor editor)\n            {\n                editor.FitToScreen();\n            }\n        }\n\n        private static void OnQuerySelectAllStatus(object? sender, CanExecuteRoutedEventArgs e)\n        {\n            if (sender is NodifyEditor editor)\n            {\n                e.CanExecute = !editor.IsSelecting && editor.CanSelectMultipleItems;\n            }\n        }\n\n        private static void OnSelectAll(object? sender, ExecutedRoutedEventArgs e)\n        {\n            if (sender is NodifyEditor editor)\n            {\n                editor.SelectAll();\n            }\n        }\n\n        private static void OnQueryStatusZoomIn(object? sender, CanExecuteRoutedEventArgs e)\n        {\n            if (sender is NodifyEditor editor)\n            {\n                e.CanExecute = editor.ViewportZoom < editor.MaxViewportZoom;\n            }\n        }\n\n        private static void OnZoomIn(object? sender, ExecutedRoutedEventArgs e)\n        {\n            if (sender is NodifyEditor editor)\n            {\n                editor.ZoomIn();\n            }\n        }\n\n        private static void OnQueryStatusZoomOut(object? sender, CanExecuteRoutedEventArgs e)\n        {\n            if (sender is NodifyEditor editor)\n            {\n                e.CanExecute = editor.ViewportZoom > editor.MinViewportZoom;\n            }\n        }\n\n        private static void OnZoomOut(object? sender, ExecutedRoutedEventArgs e)\n        {\n            if (sender is NodifyEditor editor)\n            {\n                editor.ZoomOut();\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "Nodify/EditorGestures.cs",
    "content": "﻿using System.Windows.Input;\nusing KeyGesture = Nodify.Compatibility.InputKeyGesture;\nnamespace Nodify\n{\n    /// <summary>Gestures used by built-in controls inside the <see cref=\"NodifyEditor\"/>.</summary>\n    public class EditorGestures\n    {\n        public static readonly EditorGestures Mappings = new EditorGestures();\n\n        /// <summary>Gestures for the selection.</summary>\n        public class SelectionGestures\n        {\n            /// <summary>Disable selection gestures.</summary>\n            public static readonly SelectionGestures None = new SelectionGestures(MouseAction.None);\n\n            public SelectionGestures(MouseAction mouseAction)\n            {\n                Replace = new MouseGesture(mouseAction);\n                Remove = new MouseGesture(mouseAction, ModifierKeys.Alt);\n                Append = new MouseGesture(mouseAction, ModifierKeys.Shift);\n                Invert = new MouseGesture(mouseAction, ModifierKeys.Control);\n                Select = new AnyGesture(Replace, Remove, Append, Invert);\n                Cancel = new KeyGesture(Key.Escape);\n            }\n\n            public SelectionGestures() : this(MouseAction.LeftClick)\n            {\n            }\n\n            /// <summary>Gesture to replace previous selection with the selected items.</summary>\n            /// <remarks>Defaults to <see cref=\"MouseAction.LeftClick\"/>.</remarks>\n            public InputGestureRef Replace { get; }\n\n            /// <summary>Gesture to remove the selected items from the previous selection.</summary>\n            /// <remarks>Defaults to <see cref=\"ModifierKeys.Alt\"/>+<see cref=\"MouseAction.LeftClick\"/>.</remarks>\n            public InputGestureRef Remove { get; }\n\n            /// <summary>Gesture to add the new selected items to the previous selection.</summary>\n            /// <remarks>Defaults to <see cref=\"ModifierKeys.Shift\"/>+<see cref=\"MouseAction.LeftClick\"/>.</remarks>\n            public InputGestureRef Append { get; }\n\n            /// <summary>Gesture to invert the selected items.</summary>\n            /// <remarks>Defaults to <see cref=\"ModifierKeys.Control\"/>+<see cref=\"MouseAction.LeftClick\"/>.</remarks>\n            public InputGestureRef Invert { get; }\n\n            /// <summary>Cancel the current selection operation reverting to the previous selection.</summary>\n            /// <remarks>Defaults to <see cref=\"Key.Escape\"/>.</remarks>\n            public InputGestureRef Cancel { get; }\n\n            /// <summary>Gesture used to start selecting using a <see cref=\"SelectionGestures\"/> strategy.</summary>\n            public InputGestureRef Select { get; }\n\n            /// <summary>Copies from the specified gestures.</summary>\n            /// <param name=\"gestures\">The gestures to copy.</param>\n            public void Apply(SelectionGestures gestures)\n            {\n                Replace.Value = gestures.Replace.Value;\n                Remove.Value = gestures.Remove.Value;\n                Append.Value = gestures.Append.Value;\n                Invert.Value = gestures.Invert.Value;\n                Select.Value = gestures.Select.Value;\n                Cancel.Value = gestures.Cancel.Value;\n            }\n        }\n\n        /// <summary>Gestures for the item containers.</summary>\n        public class ItemContainerGestures\n        {\n            public ItemContainerGestures()\n            {\n                Selection = new SelectionGestures();\n                Selection.Select.Value = new AnyGesture(\n                    Selection.Replace,\n                    Selection.Remove,\n                    Selection.Append,\n                    Selection.Invert,\n                    new MouseGesture(MouseAction.RightClick));\n\n                Drag = new AnyGesture(Selection.Replace, Selection.Remove, Selection.Append, Selection.Invert);\n                CancelAction = new AnyGesture(new MouseGesture(MouseAction.RightClick), new KeyGesture(Key.Escape));\n            }\n\n            /// <summary>Gesture to select the container using a <see cref=\"SelectionGestures\"/> strategy.</summary>\n            /// <remarks>Defaults to <see cref=\"MouseAction.RightClick\"/> or any of the <see cref=\"SelectionGestures\"/> gestures.</remarks>\n            public SelectionGestures Selection { get; }\n\n            /// <summary>Gesture to start and complete a dragging operation.</summary>\n            /// <remarks>Using a <see cref=\"Selection\"/> strategy to drag from a new selection. \n            /// <br /> Defaults to any of the <see cref=\"Selection\"/> gestures.\n            /// </remarks>\n            public InputGestureRef Drag { get; }\n\n            /// <summary>Gesture to cancel the dragging operation.</summary>\n            /// <remarks>Defaults to <see cref=\"MouseAction.RightClick\"/> or <see cref=\"Key.Escape\"/>.</remarks>\n            public InputGestureRef CancelAction { get; }\n\n            /// <summary>Copies from the specified gestures.</summary>\n            /// <param name=\"gestures\">The gestures to copy.</param>\n            public void Apply(ItemContainerGestures gestures)\n            {\n                Selection.Apply(gestures.Selection);\n                Drag.Value = gestures.Drag.Value;\n                CancelAction.Value = gestures.CancelAction.Value;\n            }\n        }\n\n        /// <summary>Gestures for the editor.</summary>\n        public class NodifyEditorGestures\n        {\n            public NodifyEditorGestures()\n            {\n                Selection = new SelectionGestures();\n                Cutting = new MouseGesture(MouseAction.LeftClick, ModifierKeys.Alt | ModifierKeys.Shift);\n                PushItems = new MouseGesture(MouseAction.LeftClick, ModifierKeys.Control | ModifierKeys.Shift);\n                Pan = new AnyGesture(new MouseGesture(MouseAction.RightClick), new MouseGesture(MouseAction.MiddleClick));\n                ZoomModifierKey = ModifierKeys.None;\n                ZoomIn = new MultiGesture(MultiGesture.Match.Any, new KeyGesture(Key.OemPlus, ModifierKeys.Control), new KeyGesture(Key.Add, ModifierKeys.Control));\n                ZoomOut = new MultiGesture(MultiGesture.Match.Any, new KeyGesture(Key.OemMinus, ModifierKeys.Control), new KeyGesture(Key.Subtract, ModifierKeys.Control));\n                ResetViewportLocation = new KeyGesture(Key.Home);\n                FitToScreen = new KeyGesture(Key.Home, ModifierKeys.Shift);\n                CancelAction = new AnyGesture(new MouseGesture(MouseAction.RightClick), new KeyGesture(Key.Escape));\n                PanWithMouseWheel = false;\n                PanHorizontalModifierKey = ModifierKeys.Shift;\n                PanVerticalModifierKey = ModifierKeys.None;\n\n                // Avalonia doesn't support Geometry Hit Testing, so Cutting can't work, hence disable it\n                // Follow https://github.com/AvaloniaUI/Avalonia/issues/16549 for more information\n                Cutting = new KeyGesture(Key.None);\n            }\n\n            /// <summary>Gesture used to start selecting using a <see cref=\"SelectionGestures\"/> strategy.</summary>\n            public SelectionGestures Selection { get; }\n\n            /// <summary>Gesture used to start cutting connections.</summary>\n            public InputGestureRef Cutting { get; }\n\n            /// <summary>Gesture used to start panning.</summary>\n            /// <remarks>Defaults to <see cref=\"MouseAction.RightClick\"/> or <see cref=\"MouseAction.MiddleClick\"/>.</remarks>\n            public InputGestureRef Pan { get; }\n\n            /// <summary>Whether panning using mouse wheel is allowed.</summary>\n            /// <remarks>Set the <see cref=\"ZoomModifierKey\"/> to allow zooming using the mouse wheel.</remarks>\n            public bool PanWithMouseWheel { get; set; }\n\n            /// <summary>The modifier key required to start panning vertically with the mouse wheel (see <see cref=\"PanWithMouseWheel\"/>)</summary>\n            /// <remarks>Defaults to <see cref=\"ModifierKeys.None\"/>.</remarks>\n            public ModifierKeys PanVerticalModifierKey { get; set; }\n\n            /// <summary>The modifier key required to start panning horizontally with the mouse wheel (see <see cref=\"PanWithMouseWheel\"/>)</summary>\n            /// <remarks>Defaults to <see cref=\"ModifierKeys.Shift\"/>.</remarks>\n            public ModifierKeys PanHorizontalModifierKey { get; set; }\n\n            /// <summary>Gesture used to start pushing.</summary>\n            /// <remarks>Defaults to <see cref=\"ModifierKeys.Control\"/>+<see cref=\"ModifierKeys.Shift\"/>+<see cref=\"MouseAction.LeftClick\"/>.</remarks>\n            public InputGestureRef PushItems { get; }\n\n            /// <summary>The key modifier required to start zooming by mouse wheel.</summary>\n            /// <remarks>Defaults to <see cref=\"ModifierKeys.None\"/>.</remarks>\n            public ModifierKeys ZoomModifierKey { get; set; }\n\n            /// <summary>Gesture used to zoom in.</summary>\n            /// <remarks>Defaults to <see cref=\"ModifierKeys.Control\"/>+<see cref=\"Key.OemPlus\"/>.</remarks>\n            public InputGestureRef ZoomIn { get; }\n\n            /// <summary>Gesture used to zoom out.</summary>\n            /// <remarks>Defaults to <see cref=\"ModifierKeys.Control\"/>+<see cref=\"Key.OemMinus\"/>.</remarks>\n            public InputGestureRef ZoomOut { get; }\n\n            /// <summary>Gesture used to move the editor's viewport location to (0, 0).</summary>\n            /// <remarks>Defaults to <see cref=\"Key.Home\"/>.</remarks>\n            public InputGestureRef ResetViewportLocation { get; }\n\n            /// <summary>Gesture used to fit as many containers as possible into the viewport.</summary>\n            /// <remarks>Defaults to <see cref=\"ModifierKeys.Shift\"/>+<see cref=\"Key.Home\"/>.</remarks>\n            public InputGestureRef FitToScreen { get; }\n\n            /// <summary>Gesture to cancel the current operation.</summary>\n            /// <remarks>Defaults to <see cref=\"MouseAction.RightClick\"/> or <see cref=\"Key.Escape\"/>.</remarks>\n            public InputGestureRef CancelAction { get; }\n\n            /// <summary>Copies from the specified gestures.</summary>\n            /// <param name=\"gestures\">The gestures to copy.</param>\n            public void Apply(NodifyEditorGestures gestures)\n            {\n                Selection.Apply(gestures.Selection);\n                Cutting.Value = gestures.Cutting.Value;\n                Pan.Value = gestures.Pan.Value;\n                ZoomModifierKey = gestures.ZoomModifierKey;\n                ZoomIn.Value = gestures.ZoomIn.Value;\n                ZoomOut.Value = gestures.ZoomOut.Value;\n                ResetViewportLocation.Value = gestures.ResetViewportLocation.Value;\n                FitToScreen.Value = gestures.FitToScreen.Value;\n                CancelAction.Value = gestures.CancelAction.Value;\n                PanWithMouseWheel = gestures.PanWithMouseWheel;\n                PanHorizontalModifierKey = gestures.PanHorizontalModifierKey;\n                PanVerticalModifierKey = gestures.PanVerticalModifierKey;\n                PushItems.Value = gestures.PushItems.Value;\n            }\n        }\n\n        /// <summary>Gestures used by the <see cref=\"Connector\"/>.</summary>\n        public class ConnectorGestures\n        {\n            public ConnectorGestures()\n            {\n                Disconnect = new MouseGesture(MouseAction.LeftClick, ModifierKeys.Alt);\n                Connect = new MouseGesture(MouseAction.LeftClick);\n                CancelAction = new AnyGesture(new MouseGesture(MouseAction.RightClick), new KeyGesture(Key.Escape));\n            }\n\n            /// <summary>Gesture to call the <see cref=\"Connector.DisconnectCommand\"/>.</summary>\n            /// <remarks>Defaults to <see cref=\"ModifierKeys.Alt\"/>+<see cref=\"MouseAction.LeftClick\"/>.</remarks>\n            public InputGestureRef Disconnect { get; }\n\n            /// <summary>Gesture to start and complete a pending connection.</summary>\n            /// <remarks>Defaults to <see cref=\"MouseAction.LeftClick\"/>.</remarks>\n            public InputGestureRef Connect { get; }\n\n            /// <summary>Gesture to cancel the pending connection.</summary>\n            /// <remarks>Defaults to <see cref=\"MouseAction.RightClick\"/> or <see cref=\"Key.Escape\"/>.</remarks>\n            public InputGestureRef CancelAction { get; }\n\n            /// <summary>Copies from the specified gestures.</summary>\n            /// <param name=\"gestures\">The gestures to copy.</param>\n            public void Apply(ConnectorGestures gestures)\n            {\n                Disconnect.Value = gestures.Disconnect.Value;\n                Connect.Value = gestures.Connect.Value;\n                CancelAction.Value = gestures.CancelAction.Value;\n            }\n        }\n\n        /// <summary>Gestures used by the <see cref=\"BaseConnection\"/>.</summary>\n        public class ConnectionGestures\n        {\n            public ConnectionGestures()\n            {\n                Split = new MouseGesture(MouseAction.LeftDoubleClick);\n                Selection = new SelectionGestures(MouseAction.LeftClick);\n                Disconnect = new MouseGesture(MouseAction.LeftClick, ModifierKeys.Alt);\n            }\n\n            /// <summary>Gesture to call the <see cref=\"BaseConnection.SplitCommand\"/> command.</summary>\n            /// <remarks>Defaults to <see cref=\"MouseAction.LeftDoubleClick\"/>.</remarks>\n            public InputGestureRef Split { get; }\n\n            /// <summary>Gesture used to start selecting using a <see cref=\"SelectionGestures\"/> strategy.</summary>\n            public SelectionGestures Selection { get; }\n\n            /// <summary>Gesture to call the <see cref=\"BaseConnection.DisconnectCommand\"/> command.</summary>\n            /// <remarks>Defaults to <see cref=\"ModifierKeys.Alt\"/>+<see cref=\"MouseAction.LeftClick\"/>.</remarks>\n            public InputGestureRef Disconnect { get; }\n\n            /// <summary>Copies from the specified gestures.</summary>\n            /// <param name=\"gestures\">The gestures to copy.</param>\n            public void Apply(ConnectionGestures gestures)\n            {\n                Split.Value = gestures.Split.Value;\n                Disconnect.Value = gestures.Disconnect.Value;\n                Selection.Apply(gestures.Selection);\n            }\n        }\n\n        /// <summary>Gestures for the <see cref=\"GroupingNode\"/>.</summary>\n        public class GroupingNodeGestures\n        {\n            /// <summary>The key modifier that will toggle between <see cref=\"GroupingMovementMode\"/>s.</summary>\n            /// <remarks>The modifier must be allowed by the <see cref=\"ItemContainer.Drag\"/> gesture.\n            /// <br /> Defaults to <see cref=\"ModifierKeys.Shift\"/>.\n            /// </remarks>\n            public ModifierKeys SwitchMovementMode { get; set; } = ModifierKeys.Shift;\n\n            /// <summary>Copies from the specified gestures.</summary>\n            /// <param name=\"gestures\">The gestures to copy.</param>\n            public void Apply(GroupingNodeGestures gestures)\n            {\n                SwitchMovementMode = gestures.SwitchMovementMode;\n            }\n        }\n\n        /// <summary>Gestures used by the <see cref=\"Nodify.Minimap\"/> control.</summary>\n        public class MinimapGestures\n        {\n            public MinimapGestures()\n            {\n                DragViewport = new MouseGesture(MouseAction.LeftClick);\n                ZoomModifierKey = ModifierKeys.None;\n            }\n\n            /// <summary>Gesture to move the viewport inside the <see cref=\"Minimap\" />.</summary>\n            public InputGestureRef DragViewport { get; }\n\n            /// <summary>The key modifier required to start zooming by mouse wheel.</summary>\n            /// <remarks>Defaults to <see cref=\"ModifierKeys.None\"/>.</remarks>\n            public ModifierKeys ZoomModifierKey { get; set; }\n\n            /// <summary>Copies from the specified gestures.</summary>\n            /// <param name=\"gestures\">The gestures to copy.</param>\n            public void Apply(MinimapGestures gestures)\n            {\n                DragViewport.Value = gestures.DragViewport.Value;\n                ZoomModifierKey = gestures.ZoomModifierKey;\n            }\n        }\n\n        /// <summary>Gestures for the editor.</summary>\n        public NodifyEditorGestures Editor { get; } = new NodifyEditorGestures();\n\n        /// <summary>Gestures for the item container.</summary>\n        public ItemContainerGestures ItemContainer { get; } = new ItemContainerGestures();\n\n        /// <summary>Gestures for the connector.</summary>\n        public ConnectorGestures Connector { get; } = new ConnectorGestures();\n\n        /// <summary>Gestures for the connection.</summary>\n        public ConnectionGestures Connection { get; } = new ConnectionGestures();\n\n        /// <summary>Gestures for the grouping node.</summary>\n        public GroupingNodeGestures GroupingNode { get; } = new GroupingNodeGestures();\n\n        /// <summary>Gestures for the minimap.</summary>\n        public MinimapGestures Minimap { get; } = new MinimapGestures();\n\n        /// <summary>Copies from the specified gestures.</summary>\n        /// <param name=\"gestures\">The gestures to copy.</param>\n        public void Apply(EditorGestures gestures)\n        {\n            Editor.Apply(gestures.Editor);\n            ItemContainer.Apply(gestures.ItemContainer);\n            Connector.Apply(gestures.Connector);\n            Connection.Apply(gestures.Connection);\n            GroupingNode.Apply(gestures.GroupingNode);\n            Minimap.Apply(gestures.Minimap);\n        }\n    }\n}\n"
  },
  {
    "path": "Nodify/EditorStates/ContainerDefaultState.cs",
    "content": "﻿using System.Windows.Input;\n\nnamespace Nodify\n{\n    /// <summary>The default state of the <see cref=\"ItemContainer\"/>.</summary>\n    public class ContainerDefaultState : ContainerState\n    {\n        private bool _canBeDragging;\n        private bool _canceled;\n\n        /// <summary>Creates a new instance of the <see cref=\"ContainerDefaultState\"/>.</summary>\n        /// <param name=\"container\">The owner of the state.</param>\n        public ContainerDefaultState(ItemContainer container) : base(container)\n        {\n        }\n\n        /// <inheritdoc />\n        public override void ReEnter(ContainerState from)\n        {\n            if (from is ContainerDraggingState drag)\n            {\n                Container.IsSelected = true;\n                _canceled = drag.Canceled;\n            }\n\n            _canBeDragging = false;\n        }\n\n        /// <inheritdoc />\n        public override void HandleMouseDown(MouseButtonEventArgs e)\n        {\n            _canceled = false;\n\n            EditorGestures.ItemContainerGestures gestures = EditorGestures.Mappings.ItemContainer;\n            if (gestures.Drag.Matches(e.Source, e))\n            {\n                _canBeDragging = Container.IsDraggable;\n\n                // Clear the selection if dragging an item that is not part of the selection will not add it to the selection\n                if (_canBeDragging && !Container.IsSelected && !gestures.Selection.Append.Matches(e.Source, e) && !gestures.Selection.Invert.Matches(e.Source, e))\n                {\n                    Editor.UnselectAll();\n                }\n            }\n        }\n\n        /// <inheritdoc />\n        public override void HandleMouseUp(MouseButtonEventArgs e)\n        {\n            EditorGestures.ItemContainerGestures gestures = EditorGestures.Mappings.ItemContainer;\n            if (!_canceled && gestures.Selection.Select.Matches(e.Source, e))\n            {\n                if (gestures.Selection.Append.Matches(e.Source, e))\n                {\n                    Container.IsSelected = true;\n                }\n                else if (gestures.Selection.Invert.Matches(e.Source, e))\n                {\n                    Container.IsSelected = !Container.IsSelected;\n                }\n                else if (gestures.Selection.Remove.Matches(e.Source, e))\n                {\n                    Container.IsSelected = false;\n                }\n                else\n                {\n                    // Allow context menu on selection\n                    if (!(e.ChangedButton == MouseButton.Right && e.RightButton == MouseButtonState.Released) || !Container.IsSelected)\n                    {\n                        Editor.UnselectAll();\n                    }\n\n                    Container.IsSelected = true;\n                }\n\n                _canBeDragging = false;\n            }\n\n            if(!_canceled && gestures.Drag.Matches(e.Source, e))\n            {\n                _canBeDragging = false;\n            }\n\n            _canceled = false;\n        }\n\n        /// <inheritdoc />\n        public override void HandleMouseMove(MouseEventArgs e)\n        {\n            if (_canBeDragging)\n            {\n                PushState(new ContainerDraggingState(Container), e);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "Nodify/EditorStates/ContainerDraggingState.cs",
    "content": "﻿using System.Windows;\nusing System.Windows.Controls.Primitives;\nusing System.Windows.Input;\n\nnamespace Nodify\n{\n    /// <summary>Dragging state of the container.</summary>\n    public class ContainerDraggingState : ContainerState\n    {\n        private Point _initialMousePosition;\n        private Point _previousMousePosition;\n        private Point _currentMousePosition;\n        public bool Canceled { get; set; } = ItemContainer.AllowDraggingCancellation;   // Because of LostMouseCapture that calls Exit\n\n        /// <summary>Constructs an instance of the <see cref=\"ContainerDraggingState\"/> state.</summary>\n        /// <param name=\"container\">The owner of the state.</param>\n        public ContainerDraggingState(ItemContainer container) : base(container)\n        {\n        }\n\n        /// <inheritdoc />\n        public override void Enter(ContainerState? from, MouseEventArgs? e)\n        {\n            _initialMousePosition = e?.GetPosition(Editor.ItemsHost) ?? default;\n\n            Container.IsSelected = true;\n            Container.IsPreviewingLocation = true;\n            Container.RaiseEvent(new DragStartedEventArgs(_initialMousePosition.X, _initialMousePosition.Y)\n            {\n                RoutedEvent = ItemContainer.DragStartedEvent\n            });\n\n            _previousMousePosition = _initialMousePosition;\n        }\n\n        /// <inheritdoc />\n        public override void Exit()\n        {\n            Container.IsPreviewingLocation = false;\n            var delta = _currentMousePosition - _initialMousePosition;\n            Container.RaiseEvent(new DragCompletedEventArgs(delta.X, delta.Y, Canceled)\n            {\n                RoutedEvent = ItemContainer.DragCompletedEvent\n            });\n        }\n\n        /// <inheritdoc />\n        public override void HandleMouseMove(MouseEventArgs e)\n        {\n            _currentMousePosition = e.GetPosition(Editor.ItemsHost);\n            var delta = _currentMousePosition - _previousMousePosition;\n            Container.RaiseEvent(new DragDeltaEventArgs(delta.X, delta.Y)\n            {\n                RoutedEvent = ItemContainer.DragDeltaEvent\n            });\n\n            _previousMousePosition = _currentMousePosition;\n        }\n\n        /// <inheritdoc />\n        public override void HandleMouseUp(MouseButtonEventArgs e)\n        {\n            EditorGestures.ItemContainerGestures gestures = EditorGestures.Mappings.ItemContainer;\n\n            bool canCancel = gestures.CancelAction.Matches(e.Source, e) && ItemContainer.AllowDraggingCancellation;\n            bool canComplete = gestures.Drag.Matches(e.Source, e);\n            if (canCancel || canComplete)\n            {\n                // Prevent canceling if drag and cancel are bound to the same mouse action\n                Canceled = !canComplete && canCancel;\n\n                // Handle right click if dragging or canceled and moved the mouse more than threshold so context menus don't open\n                if (e.ChangedButton == MouseButton.Right)\n                {\n                    double contextMenuTreshold = NodifyEditor.HandleRightClickAfterPanningThreshold * NodifyEditor.HandleRightClickAfterPanningThreshold;\n                    if ((_currentMousePosition - _initialMousePosition).LengthSquared() > contextMenuTreshold)\n                    {\n                        e.Handled = true;\n                    }\n                }\n\n                PopState();\n            }\n        }\n\n        /// <inheritdoc />\n        public override void HandleKeyUp(KeyEventArgs e)\n        {\n            Canceled = EditorGestures.Mappings.ItemContainer.CancelAction.Matches(e.Source, e) && ItemContainer.AllowDraggingCancellation;\n            if (Canceled)\n            {\n                PopState();\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "Nodify/EditorStates/ContainerState.cs",
    "content": "﻿using System.Windows.Input;\n\nnamespace Nodify\n{\n    /// <summary>The base class for container states.</summary>\n    public abstract class ContainerState\n    {\n        /// <summary>Constructs a new <see cref=\"ContainerState\"/>.</summary>\n        /// <param name=\"container\">The owner of the state.</param>\n        public ContainerState(ItemContainer container)\n        {\n            Container = container;\n        }\n\n        /// <summary>The owner of the state.</summary>\n        protected ItemContainer Container { get; }\n\n        /// <summary>The owner of the state.</summary>\n        protected NodifyEditor Editor => Container.Editor;\n\n        /// <inheritdoc cref=\"ItemContainer.OnMouseDown(MouseButtonEventArgs)\"/>\n        public virtual void HandleMouseDown(MouseButtonEventArgs e) { }\n\n        /// <inheritdoc cref=\"ItemContainer.OnMouseDown(MouseButtonEventArgs)\"/>\n        public virtual void HandleMouseUp(MouseButtonEventArgs e) { }\n\n        /// <inheritdoc cref=\"ItemContainer.OnMouseMove(MouseEventArgs)\"/>\n        public virtual void HandleMouseMove(MouseEventArgs e) { }\n\n        /// <inheritdoc cref=\"ItemContainer.OnMouseWheel(MouseWheelEventArgs)\"/>\n        public virtual void HandleMouseWheel(MouseWheelEventArgs e) { }\n\n        /// <inheritdoc cref=\"ItemContainer.OnKeyUp(KeyEventArgs)\"/>\n        public virtual void HandleKeyUp(KeyEventArgs e) { }\n\n        /// <inheritdoc cref=\"ItemContainer.OnKeyDown(KeyEventArgs)\"/>\n        public virtual void HandleKeyDown(KeyEventArgs e) { }\n\n        /// <summary>Called when <see cref=\"ItemContainer.PushState(ContainerState)\"/> or <see cref=\"ItemContainer.PopState\"/> is called.</summary>\n        /// <param name=\"from\">The state we enter from (is null for root state).</param>\n        public virtual void Enter(ContainerState? from, MouseEventArgs? e) { }\n\n        /// <summary>Called when <see cref=\"ItemContainer.PopState\"/> is called.</summary>\n        public virtual void Exit() { }\n\n        /// <summary>Called when <see cref=\"ItemContainer.PopState\"/> is called.</summary>\n        /// <param name=\"from\">The state we re-enter from.</param>\n        public virtual void ReEnter(ContainerState from) { }\n\n        /// <summary>Pushes a new state into the stack.</summary>\n        /// <param name=\"newState\">The new state.</param>\n        /// <param name=\"pointerEventArgs\"></param>\n        public virtual void PushState(ContainerState newState, MouseEventArgs e) => Container.PushState(newState, e);\n\n        /// <summary>Pops the current state from the stack.</summary>\n        public virtual void PopState() => Container.PopState();\n    }\n}\n"
  },
  {
    "path": "Nodify/EditorStates/EditorCuttingState.cs",
    "content": "﻿using System.Collections.Generic;\nusing System.Windows;\nusing System.Windows.Input;\nusing System.Windows.Media;\n\nnamespace Nodify\n{\n    public class EditorCuttingState : EditorState\n    {\n        private readonly LineGeometry _lineGeometry = new LineGeometry();\n        private List<FrameworkElement>? _previousConnections;\n\n        public bool Canceled { get; set; } = CuttingLine.AllowCuttingCancellation;\n\n        public EditorCuttingState(NodifyEditor editor) : base(editor)\n        {\n        }\n\n        public override void Enter(EditorState? from, MouseEventArgs e)\n        {\n            Canceled = false;\n\n            var startLocation = Editor.MouseLocation;\n            Editor.StartCutting(startLocation);\n\n            _lineGeometry.StartPoint = startLocation;\n            _lineGeometry.EndPoint = startLocation;\n        }\n\n        public override void Exit()\n        {\n            ResetConnectionStyle();\n\n            // TODO: This is not canceled on LostMouseCapture (add OnLostMouseCapture/OnCancel callback?)\n            if (Canceled)\n            {\n                Editor.CancelCutting();\n            }\n            else\n            {\n                Editor.EndCutting(Editor.MouseLocation);\n            }\n        }\n\n        public override void HandleMouseUp(MouseButtonEventArgs e)\n        {\n            EditorGestures.NodifyEditorGestures gestures = EditorGestures.Mappings.Editor;\n            if (gestures.Cutting.Matches(e.Source, e))\n            {\n                PopState();\n            }\n            else if (CuttingLine.AllowCuttingCancellation && gestures.CancelAction.Matches(e.Source, e))\n            {\n                Canceled = true;\n                e.Handled = true;   // prevents opening context menu\n\n                PopState();\n            }\n        }\n\n        public override void HandleMouseMove(MouseEventArgs e)\n        {\n            Editor.CuttingLineEnd = Editor.MouseLocation;\n\n            if (NodifyEditor.EnableCuttingLinePreview)\n            {\n                ResetConnectionStyle();\n\n                _lineGeometry.EndPoint = Editor.MouseLocation;\n                var connections = Editor.ConnectionsHost.GetIntersectingElements(_lineGeometry, NodifyEditor.CuttingConnectionTypes);\n                foreach (var connection in connections)\n                {\n                    CuttingLine.SetIsOverElement(connection, true);\n                }\n\n                _previousConnections = connections;\n            }\n        }\n\n        private void ResetConnectionStyle()\n        {\n            if (_previousConnections != null)\n            {\n                foreach (var connection in _previousConnections)\n                {\n                    CuttingLine.SetIsOverElement(connection, false);\n                }\n            }\n        }\n\n        public override void HandleKeyUp(KeyEventArgs e)\n        {\n            EditorGestures.NodifyEditorGestures gestures = EditorGestures.Mappings.Editor;\n            if (CuttingLine.AllowCuttingCancellation && gestures.CancelAction.Matches(e.Source, e))\n            {\n                Canceled = true;\n                PopState();\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "Nodify/EditorStates/EditorDefaultState.cs",
    "content": "﻿using System.Windows;\nusing System.Windows.Input;\nusing static Nodify.SelectionHelper;\n\nnamespace Nodify\n{\n    /// <summary>\n    /// The default state of the editor.\n    /// <br />\n    /// <br />  Default State\n    /// <br />  \t- mouse left down  \t-> Selecting State\n    /// <br />  \t- mouse right down  -> Panning State\n    /// <br /> \t\n    /// <br />  Selecting State\n    /// <br />  \t- mouse left up \t-> Default State\n    /// <br />  \t- mouse right down \t-> Panning State\n    /// <br /> \n    /// <br />  Panning State\n    /// <br />  \t- mouse right up\t-> previous state (Selecting State or Default State)\n    /// <br />  \t- mouse left up\t\t-> Panning State\n    /// <br />\t\n    /// </summary>\n    public class EditorDefaultState : EditorState\n    {\n        /// <summary>Constructs an instance of the <see cref=\"EditorDefaultState\"/> state.</summary>\n        /// <param name=\"editor\">The owner of the state.</param>\n        public EditorDefaultState(NodifyEditor editor) : base(editor)\n        {\n        }\n\n        /// <inheritdoc />\n        public override void HandleMouseDown(MouseButtonEventArgs e)\n        {\n            EditorGestures.NodifyEditorGestures gestures = EditorGestures.Mappings.Editor;\n            if (gestures.Cutting.Matches(e.Source, e))\n            {\n                PushState(new EditorCuttingState(Editor), e);\n            }\n            else if (gestures.PushItems.Matches(e.Source, e))\n            {\n                PushState(new EditorPushingItemsState(Editor), e);\n            }\n            else if (gestures.Selection.Select.Matches(e.Source, e))\n            {\n                SelectionType selectionType = GetSelectionType(e);\n                var selecting = new EditorSelectingState(Editor, selectionType);\n                PushState(selecting, e);\n            }\n            else if (!Editor.DisablePanning && gestures.Pan.Matches(e.Source, e))\n            {\n                PushState(new EditorPanningState(Editor), e);\n            }\n        }\n\n        public override void HandleMouseWheel(MouseWheelEventArgs e)\n        {\n            EditorGestures.NodifyEditorGestures gestures = EditorGestures.Mappings.Editor;\n            if (gestures.PanWithMouseWheel)\n            {\n                if (e.KeyModifiers == gestures.PanHorizontalModifierKey)\n                {\n                    Editor.ViewportLocation = new Point(Editor.ViewportLocation.X - e.Delta / Editor.ViewportZoom, Editor.ViewportLocation.Y);\n                    e.Handled = true;\n                }\n                else if (e.KeyModifiers == gestures.PanVerticalModifierKey)\n                {\n                    Editor.ViewportLocation = new Point(Editor.ViewportLocation.X, Editor.ViewportLocation.Y - e.Delta / Editor.ViewportZoom);\n                    e.Handled = true;\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "Nodify/EditorStates/EditorPanningState.cs",
    "content": "﻿using System.Windows;\nusing System.Windows.Input;\n\nnamespace Nodify\n{\n    /// <summary>The panning state of the editor.</summary>\n    public class EditorPanningState : EditorState\n    {\n        private Point _initialMousePosition;\n        private Point _previousMousePosition;\n        private Point _currentMousePosition;\n\n        /// <summary>Constructs an instance of the <see cref=\"EditorPanningState\"/> state.</summary>\n        /// <param name=\"editor\">The owner of the state.</param>\n        public EditorPanningState(NodifyEditor editor) : base(editor)\n        {\n        }\n\n        /// <inheritdoc />\n        public override void Exit()\n            => Editor.IsPanning = false;\n\n        /// <inheritdoc />\n        public override void Enter(EditorState? from, MouseEventArgs e)\n        {\n            _initialMousePosition = e.GetPosition(Editor);\n            _previousMousePosition = _initialMousePosition;\n            _currentMousePosition = _initialMousePosition;\n            Editor.IsPanning = true;\n        }\n\n        /// <inheritdoc />\n        public override void HandleMouseMove(MouseEventArgs e)\n        {\n            _currentMousePosition = e.GetPosition(Editor);\n            Editor.ViewportLocation -= (_currentMousePosition - _previousMousePosition) / Editor.ViewportZoom;\n            _previousMousePosition = _currentMousePosition;\n        }\n\n        /// <inheritdoc />\n        public override void HandleMouseUp(MouseButtonEventArgs e)\n        {\n            EditorGestures.NodifyEditorGestures gestures = EditorGestures.Mappings.Editor;\n            if (gestures.Pan.Matches(e.Source, e))\n            {\n                // Handle right click if panning and moved the mouse more than threshold so context menu doesn't open\n                if (e.ChangedButton == MouseButton.Right)\n                {\n                    double contextMenuTreshold = NodifyEditor.HandleRightClickAfterPanningThreshold * NodifyEditor.HandleRightClickAfterPanningThreshold;\n                    if ((_currentMousePosition - _initialMousePosition).LengthSquared() > contextMenuTreshold)\n                    {\n                        e.Handled = true;\n                    }\n                }\n\n                PopState();\n            }\n            else if (gestures.Selection.Select.Matches(e.Source, e) && Editor.IsSelecting)\n            {\n                PopState();\n                // Cancel selection and continue panning\n                if (Editor.State is EditorSelectingState && !Editor.DisablePanning)\n                {\n                    PopState();\n                    PushState(new EditorPanningState(Editor), e);\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "Nodify/EditorStates/EditorPushingItemsState.cs",
    "content": "﻿using System;\nusing System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Input;\n\nnamespace Nodify\n{\n    public class EditorPushingItemsState : EditorState\n    {\n        private Point _prevPosition;\n        private const int _minDragDistance = 10;\n\n        public bool Canceled { get; set; } = NodifyEditor.AllowPushItemsCancellation;\n\n        public EditorPushingItemsState(NodifyEditor editor) : base(editor)\n        {\n        }\n\n        public override void Enter(EditorState? from, MouseEventArgs e)\n        {\n            Canceled = false;\n\n            _prevPosition = Editor.MouseLocation;\n        }\n\n        public override void Exit()\n        {\n            if (!Editor.IsPushingItems)\n            {\n                return;\n            }\n\n            if (Canceled)\n            {\n                Editor.CancelPushingItems();\n            }\n            else\n            {\n                Editor.EndPushingItems();\n            }\n        }\n\n        public override void HandleMouseMove(MouseEventArgs e)\n        {\n            if (Editor.IsPushingItems)\n            {\n                Editor.PushItems(Editor.MouseLocation - _prevPosition);\n                _prevPosition = Editor.MouseLocation;\n            }\n            else\n            {\n                if (Math.Abs(Editor.MouseLocation.X - _prevPosition.X) >= _minDragDistance)\n                {\n                    Editor.StartPushingItems(_prevPosition, Orientation.Horizontal);\n                }\n                else if (Math.Abs(Editor.MouseLocation.Y - _prevPosition.Y) >= _minDragDistance)\n                {\n                    Editor.StartPushingItems(_prevPosition, Orientation.Vertical);\n                }\n            }\n        }\n\n        public override void HandleMouseUp(MouseButtonEventArgs e)\n        {\n            EditorGestures.NodifyEditorGestures gestures = EditorGestures.Mappings.Editor;\n            if (gestures.PushItems.Matches(e.Source, e))\n            {\n                PopState();\n            }\n            else if (NodifyEditor.AllowPushItemsCancellation && gestures.CancelAction.Matches(e.Source, e))\n            {\n                Canceled = true;\n                e.Handled = true;   // prevents opening context menu\n\n                PopState();\n            }\n        }\n\n        public override void HandleKeyUp(KeyEventArgs e)\n        {\n            EditorGestures.NodifyEditorGestures gestures = EditorGestures.Mappings.Editor;\n            if (NodifyEditor.AllowPushItemsCancellation && gestures.CancelAction.Matches(e.Source, e))\n            {\n                Canceled = true;\n                PopState();\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "Nodify/EditorStates/EditorSelectingState.cs",
    "content": "﻿using System.Windows.Input;\nusing static Nodify.SelectionHelper;\n\nnamespace Nodify\n{\n    /// <summary>The selecting state of the editor.</summary>\n    public class EditorSelectingState : EditorState\n    {\n        private readonly SelectionType _type;\n        private bool _canceled;\n\n        /// <summary>The selection helper.</summary>\n        protected SelectionHelper Selection { get; }\n\n        /// <summary>Constructs an instance of the <see cref=\"EditorSelectingState\"/> state.</summary>\n        /// <param name=\"editor\">The owner of the state.</param>\n        /// <param name=\"type\">The selection strategy.</param>\n        public EditorSelectingState(NodifyEditor editor, SelectionType type) : base(editor)\n        {\n            Selection = new SelectionHelper(editor);\n            _type = type;\n        }\n\n        /// <inheritdoc />\n        public override void Enter(EditorState? from, MouseEventArgs e)\n        {\n            Editor.UnselectAllConnection();\n\n            _canceled = false;\n            Selection.Start(Editor.MouseLocation, _type);\n        }\n\n        /// <inheritdoc />\n        public override void Exit()\n        {\n            if (_canceled)\n            {\n                Selection.Abort();\n            }\n            else\n            {\n                Selection.End();\n            }\n        }\n\n        /// <inheritdoc />\n        public override void HandleMouseMove(MouseEventArgs e)\n            => Selection.Update(Editor.MouseLocation);\n\n        /// <inheritdoc />\n        public override void HandleMouseDown(MouseButtonEventArgs e)\n        {\n            if (!Editor.DisablePanning && EditorGestures.Mappings.Editor.Pan.Matches(e.Source, e))\n            {\n                PushState(new EditorPanningState(Editor), e);\n            }\n        }\n\n        /// <inheritdoc />\n        public override void HandleMouseUp(MouseButtonEventArgs e)\n        {\n            EditorGestures.SelectionGestures gestures = EditorGestures.Mappings.Editor.Selection;\n\n            bool canCancel = gestures.Cancel.Matches(e.Source, e);\n            bool canComplete = gestures.Select.Matches(e.Source, e);\n            if (canCancel || canComplete)\n            {\n                _canceled = !canComplete && canCancel;\n                PopState();\n            }\n        }\n\n        /// <inheritdoc />\n        public override void HandleAutoPanning(MouseEventArgs e)\n            => HandleMouseMove(e);\n\n        public override void HandleKeyUp(KeyEventArgs e)\n        {\n            if (EditorGestures.Mappings.Editor.Selection.Cancel.Matches(e.Source, e))\n            {\n                _canceled = true;\n                PopState();\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "Nodify/EditorStates/EditorState.cs",
    "content": "﻿using System.Windows.Input;\n\nnamespace Nodify\n{\n    /// <summary>The base class for editor states.</summary>\n    public abstract class EditorState\n    {\n        /// <summary>Constructs a new <see cref=\"EditorState\"/>.</summary>\n        /// <param name=\"editor\">The owner of the state.</param>\n        public EditorState(NodifyEditor editor)\n        {\n            Editor = editor;\n        }\n\n        /// <summary>The owner of the state.</summary>\n        protected NodifyEditor Editor { get; }\n\n        /// <inheritdoc cref=\"NodifyEditor.OnMouseDown(MouseButtonEventArgs)\"/>\n        public virtual void HandleMouseDown(MouseButtonEventArgs e) { }\n\n        /// <inheritdoc cref=\"NodifyEditor.OnMouseUp(MouseButtonEventArgs)\"/>\n        public virtual void HandleMouseUp(MouseButtonEventArgs e) { }\n\n        /// <inheritdoc cref=\"NodifyEditor.OnMouseMove(MouseEventArgs)\"/>\n        public virtual void HandleMouseMove(MouseEventArgs e) { }\n\n        /// <inheritdoc cref=\"NodifyEditor.OnMouseWheel(MouseWheelEventArgs)\"/>\n        public virtual void HandleMouseWheel(MouseWheelEventArgs e) { }\n\n        /// <summary>Handles auto panning when mouse is outside the editor.</summary>\n        /// <param name=\"e\">The <see cref=\"MouseEventArgs\"/> that contains the event data.</param>\n        public virtual void HandleAutoPanning(MouseEventArgs e) { }\n\n        /// <inheritdoc cref=\"NodifyEditor.OnKeyUp(KeyEventArgs)\"/>\n        public virtual void HandleKeyUp(KeyEventArgs e) { }\n\n        /// <inheritdoc cref=\"NodifyEditor.OnKeyDown(KeyEventArgs)\"/>\n        public virtual void HandleKeyDown(KeyEventArgs e) { }\n\n        /// <summary>Called when <see cref=\"NodifyEditor.PushState(EditorState)\"/> is called.</summary>\n        /// <param name=\"from\">The state we enter from (is null for root state).</param>\n        public virtual void Enter(EditorState? from, MouseEventArgs e) { }\n\n        /// <summary>Called when <see cref=\"NodifyEditor.PopState\"/> is called.</summary>\n        public virtual void Exit() { }\n\n        /// <summary>Called when <see cref=\"NodifyEditor.PopState\"/> is called.</summary>\n        /// <param name=\"from\">The state we re-enter from.</param>\n        public virtual void ReEnter(EditorState from) { }\n\n        /// <summary>Pushes a new state into the stack.</summary>\n        /// <param name=\"newState\">The new state.</param>\n        public virtual void PushState(EditorState newState, MouseEventArgs e) => Editor.PushState(newState, e);\n\n        /// <summary>Pops the current state from the stack.</summary>\n        public virtual void PopState() => Editor.PopState();\n    }\n}\n"
  },
  {
    "path": "Nodify/Events/ConnectionEventArgs.cs",
    "content": "﻿using System;\nusing System.Windows;\n\nnamespace Nodify\n{\n    /// <summary>\n    /// Represents the method that will handle <see cref=\"BaseConnection\"/> related routed events.\n    /// </summary>\n    /// <param name=\"sender\">The object where the event handler is attached.</param>\n    /// <param name=\"e\">The event data.</param>\n    public delegate void ConnectionEventHandler(object? sender, ConnectionEventArgs e);\n\n    /// <summary>\n    /// Provides data for <see cref=\"BaseConnection\"/> related routed events.\n    /// </summary>\n    public class ConnectionEventArgs : RoutedEventArgs\n    {\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"ConnectionEventArgs\"/> class using the specified <see cref=\"Connection\"/>.\n        /// </summary>\n        /// <param name=\"connection\">The <see cref=\"FrameworkElement.DataContext\"/> of a related <see cref=\"BaseConnection\"/>.</param>\n        public ConnectionEventArgs(object connection)\n            => Connection = connection;\n\n        /// <summary>\n        /// Gets or sets the location where the connection should be split.\n        /// </summary>\n        public Point SplitLocation { get; set; }\n\n        /// <summary>\n        /// Gets the <see cref=\"FrameworkElement.DataContext\"/> of the <see cref=\"BaseConnection\"/> associated with this event.\n        /// </summary>\n        public object Connection { get; }\n    }\n}\n"
  },
  {
    "path": "Nodify/Events/ConnectorEventArgs.cs",
    "content": "﻿using System;\nusing System.Windows;\n\nnamespace Nodify\n{\n    /// <summary>\n    /// Represents the method that will handle <see cref=\"Connector\"/> related routed events.\n    /// </summary>\n    /// <param name=\"sender\">The object where the event handler is attached.</param>\n    /// <param name=\"e\">The event data.</param>\n    public delegate void ConnectorEventHandler(object? sender, ConnectorEventArgs e);\n\n    /// <summary>\n    /// Provides data for <see cref=\"Nodify.Connector\"/> related routed events.\n    /// </summary>\n    public class ConnectorEventArgs : RoutedEventArgs\n    {\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"ConnectorEventArgs\"/> class using the specified <see cref=\"Connector\"/>.\n        /// </summary>\n        /// <param name=\"connector\">The <see cref=\"FrameworkElement.DataContext\"/> of a related <see cref=\"Nodify.Connector\"/>.</param>\n        public ConnectorEventArgs(object connector)\n            => Connector = connector;\n\n        /// <summary>\n        /// Gets or sets the <see cref=\"Nodify.Connector.Anchor\"/> of the <see cref=\"Nodify.Connector\"/> associated with this event.\n        /// </summary>\n        public Point Anchor { get; set; }\n\n        /// <summary>\n        /// Gets the <see cref=\"FrameworkElement.DataContext\"/> of the <see cref=\"Nodify.Connector\"/> associated with this event.\n        /// </summary>\n        public object Connector { get; }\n    }\n}\n"
  },
  {
    "path": "Nodify/Events/PendingConnectionEventArgs.cs",
    "content": "﻿using System;\nusing System.Windows;\n\nnamespace Nodify\n{\n    /// <summary>\n    /// Represents the method that will handle <see cref=\"PendingConnection\"/> related routed events.\n    /// </summary>\n    /// <param name=\"sender\">The object where the event handler is attached.</param>\n    /// <param name=\"e\">The event data.</param>\n    public delegate void PendingConnectionEventHandler(object? sender, PendingConnectionEventArgs e);\n    \n    /// <summary>\n    /// Provides data for <see cref=\"PendingConnection\"/> related routed events.\n    /// </summary>\n    public class PendingConnectionEventArgs : RoutedEventArgs\n    {\n        private readonly MouseEventArgs? mouseEventArgs;\n\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"PendingConnectionEventArgs\"/> class using the specified <see cref=\"SourceConnector\"/>.\n        /// </summary>\n        /// <param name=\"sourceConnector\">The <see cref=\"FrameworkElement.DataContext\"/> of a related <see cref=\"Connector\"/>.</param>\n        public PendingConnectionEventArgs(object sourceConnector, MouseEventArgs? mouseEventArgs)\n        {\n            this.mouseEventArgs = mouseEventArgs;\n            SourceConnector = sourceConnector;\n        }\n        \n        /// <summary>\n        /// Gets or sets the <see cref=\"Connector.Anchor\"/> of the <see cref=\"Connector\"/> that raised this event.\n        /// </summary>\n        public Point Anchor { get; set; }\n        \n        /// <summary>\n        /// Gets the <see cref=\"FrameworkElement.DataContext\"/> of the <see cref=\"Connector\"/> that started this <see cref=\"PendingConnection\"/>.\n        /// </summary>\n        public object SourceConnector { get; }\n        \n        /// <summary>\n        /// Gets or sets the <see cref=\"FrameworkElement.DataContext\"/> of the target <see cref=\"Connector\"/> when the <see cref=\"PendingConnection\"/> is completed.\n        /// </summary>\n        public object? TargetConnector { get; set; }\n\n        /// <summary>\n        /// Gets or sets the distance from the <see cref=\"SourceConnector\"/> in the X axis.\n        /// </summary>\n        public double OffsetX { get; set; }\n\n        /// <summary>\n        /// Gets or sets the distance from the <see cref=\"SourceConnector\"/> in the Y axis.\n        /// </summary>\n        public double OffsetY { get; set; }\n\n        /// <summary>\n        /// Gets or sets a value that indicates whether this <see cref=\"PendingConnection\"/> was cancelled.\n        /// </summary>\n        public bool Canceled { get; set; }\n        \n        public Point GetPosition(Visual? relativeTo)\n        {\n            return mouseEventArgs?.GetPosition(relativeTo) ?? default;\n        }\n    }\n}\n"
  },
  {
    "path": "Nodify/Events/ResizeEventArgs.cs",
    "content": "﻿using System;\nusing System.Windows;\n\nnamespace Nodify\n{\n    /// <summary>\n    /// Represents the method that will handle resize related routed events.\n    /// </summary>\n    /// <param name=\"sender\">The sender of this event.</param>\n    /// <param name=\"e\">The event data.</param>\n    public delegate void ResizeEventHandler(object? sender, ResizeEventArgs e);\n\n    /// <summary>\n    /// Provides data for resize related routed events.\n    /// </summary>\n    public class ResizeEventArgs : RoutedEventArgs\n    {\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"ResizeEventArgs\"/> class with the previous and the new <see cref=\"Size\"/>.\n        /// </summary>\n        /// <param name=\"previousSize\">The previous size associated with this event.</param>\n        /// <param name=\"newSize\">The new size associated with this event.</param>\n        public ResizeEventArgs(Size previousSize, Size newSize)\n        {\n            PreviousSize = previousSize;\n            NewSize = newSize;\n        }\n\n        /// <summary>\n        /// Gets the previous size of the object.\n        /// </summary>\n        public Size PreviousSize { get; }\n\n        \n        /// <summary>\n        /// Gets the new size of the object.\n        /// </summary>\n        public Size NewSize { get; }\n    }\n}\n"
  },
  {
    "path": "Nodify/GlobalUsings.cs",
    "content": "global using Avalonia.Controls;\nglobal using Avalonia;\nglobal using Avalonia.Data.Converters;\nglobal using System.Windows.Input;\nglobal using Avalonia.Interactivity;\nglobal using Avalonia.Input;\nglobal using Avalonia.Controls.Metadata;\nglobal using Avalonia.Controls.Shapes;\nglobal using Avalonia.Controls.Documents;\nglobal using Avalonia.Data;\nglobal using Avalonia.Layout;\nglobal using Avalonia.Media;\nglobal using Nodify.Compatibility;\nglobal using System.Linq;\nglobal using Avalonia.Styling;\nglobal using Avalonia.Controls.Primitives;\nglobal using Avalonia.Controls.Presenters;\nglobal using Avalonia.Controls.Selection;\nglobal using Avalonia.Markup.Xaml.Templates;\nglobal using Avalonia.Metadata;\nglobal using Avalonia.Threading;\nglobal using Avalonia.Controls.Templates;\nglobal using Avalonia.VisualTree;\nglobal using Avalonia.Animation;\nglobal using Avalonia.Controls.Mixins;\nglobal using Avalonia.Collections;\nglobal using System.Runtime.CompilerServices;\nglobal using System.Collections.Generic;\nglobal using System.Threading;\n\nglobal using FrameworkElement = Avalonia.Controls.Control;\nglobal using DependencyObject = Avalonia.AvaloniaObject;\nglobal using UIElement = Avalonia.Controls.Control;\nglobal using DependencyPropertyChangedEventArgs = Avalonia.AvaloniaPropertyChangedEventArgs;\nglobal using SizeChangedInfo = Avalonia.Controls.SizeChangedEventArgs;\nglobal using System;\nglobal using Avalonia.Interactivity;\nglobal using RoutedEventHandler = System.EventHandler<Avalonia.Interactivity.RoutedEventArgs>;\nglobal using ModifierKeys = Avalonia.Input.KeyModifiers;\nglobal using UIElementCollection = Avalonia.Controls.Controls;"
  },
  {
    "path": "Nodify/Helpers/BindableStyleClasses.cs",
    "content": "namespace Nodify;\n\ninternal class BindableStyleClasses\n{\n    static BindableStyleClasses()\n    {\n        ClassesProperty.Changed.AddClassHandler<StyledElement>(HandleClassesChanged);\n    }\n\n    public static readonly AttachedProperty<object?> ClassesProperty = AvaloniaProperty.RegisterAttached<BindableStyleClasses, StyledElement, object?>(\n        \"Classes\");\n\n    private static void HandleClassesChanged(StyledElement element, AvaloniaPropertyChangedEventArgs e)\n    {\n        element.Classes.Clear();\n        if (e.NewValue != null)\n            element.Classes.Add(e.NewValue.ToString());\n    }\n\n    public static void SetClasses(StyledElement element, object? classes)\n    {\n        element.SetValue(ClassesProperty, classes);\n    }\n\n\n    public static object? GetClasses(StyledElement element)\n    {\n        return element.GetValue(ClassesProperty);\n    }\n}"
  },
  {
    "path": "Nodify/Helpers/BoxValue.cs",
    "content": "﻿using System.Windows;\n\nnamespace Nodify\n{\n    public static class BoxValue\n    {\n        public static readonly Point Point = default(Point);\n        public static readonly Size Size = default(Size);\n        public static readonly Rect Rect = default(Rect);\n        public static readonly bool False = false;\n        public static readonly bool True = true;\n        public static readonly double DoubleHalf = 0.5d;\n        public static readonly double Double0 = 0d;\n        public static readonly double Double1 = 1d;\n        public static readonly double Double2 = 2d;\n        public static readonly double Double5 = 5d;\n        public static readonly double Double45 = 45d;\n        public static readonly double Double1000 = 1000d;\n        public static readonly int Int0 = 0;\n        public static readonly int Int1 = 1;\n        public static readonly uint UInt1 = 1u;\n        public static readonly uint UInt0 = 0u;\n\n        public static readonly Thickness Thickness2 = new Thickness(2);\n        public static readonly Size ArrowSize = new Size(8, 8);\n        public static readonly Size ConnectionOffset = new Size(14, 0);\n    }\n}\n"
  },
  {
    "path": "Nodify/Helpers/DependencyObjectExtensions.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Windows;\nusing System.Windows.Input;\nusing System.Windows.Media;\nusing System.Windows.Media.Animation;\n\nnamespace Nodify\n{\n    internal static class DependencyObjectExtensions\n    {\n        public static T? GetParentOfType<T>(this DependencyObject child)\n            where T : DependencyObject\n        {\n            DependencyObject? current = child;\n\n            do\n            {\n                current = VisualTreeHelper.GetParent(current);\n                if (current == default)\n                {\n                    return default;\n                }\n\n            } while (!(current is T));\n\n            return (T)current;\n        }\n\n        public static T? GetChildOfType<T>(this DependencyObject? depObj) where T : DependencyObject\n        {\n            if (depObj == null)\n            {\n                return default;\n            }\n\n            if (depObj is Visual visual)\n                return visual.FindDescendantOfType<T>();\n\n            return default;\n        }\n\n        public static T? GetElementUnderMouse<T>(this UIElement container, Point relativePosition)\n            where T : UIElement\n        {\n            T? result = default;\n            VisualTreeHelper.HitTest(container, depObj =>\n            {\n                if (depObj is UIElement elem && elem.IsHitTestVisible)\n                {\n                    if (elem is T r)\n                    {\n                        result = r;\n                        return HitTestFilterBehavior.Stop;\n                    }\n\n                    return HitTestFilterBehavior.Continue;\n                }\n\n                return HitTestFilterBehavior.ContinueSkipSelfAndChildren;\n            }, hitResult =>\n            {\n                if (hitResult.VisualHit is T r)\n                {\n                    result = r;\n                    return HitTestResultBehavior.Stop;\n                }\n                return HitTestResultBehavior.Continue;\n            }, new PointHitTestParameters(relativePosition));\n\n            return result;\n        }\n\n        public static List<FrameworkElement> GetIntersectingElements(this UIElement container, Geometry geometry, IReadOnlyCollection<Type> supportedTypes)\n        {\n            var result = new List<FrameworkElement>();\n            VisualTreeHelper.HitTest(container, depObj =>\n            {\n                if (depObj is FrameworkElement elem && elem.IsHitTestVisible)\n                {\n                    if (supportedTypes.Contains(elem.GetType()))\n                    {\n                        return HitTestFilterBehavior.ContinueSkipChildren;\n                    }\n\n                    return HitTestFilterBehavior.ContinueSkipSelf;\n                }\n\n                return HitTestFilterBehavior.ContinueSkipSelfAndChildren;\n            }, hitResult =>\n            {\n                result.Add((FrameworkElement)hitResult.VisualHit);\n                return HitTestResultBehavior.Continue;\n            }, new GeometryHitTestParameters(geometry));\n\n            return result;\n        }\n\n        #region Animation\n\n        public static async System.Threading.Tasks.Task StartAnimation<T>(this Control animatableElement, AvaloniaProperty<T> dependencyProperty, T toValue, double animationDurationSeconds, CancellationToken token, EventHandler? completedEvent = null)\n        {\n            var fromValue = (T)animatableElement.GetValue(dependencyProperty);\n\n            var keyframe1 = new KeyFrame()\n            {\n                Setters = { new Setter(dependencyProperty, fromValue), }, KeyTime = TimeSpan.FromSeconds(0)\n            };\n\n            var keyframe2 = new KeyFrame()\n            {\n                Setters = { new Setter(dependencyProperty, toValue), }, KeyTime = TimeSpan.FromSeconds(animationDurationSeconds)\n            };\n\n            var animation = new Avalonia.Animation.Animation()\n            {\n                Duration = TimeSpan.FromSeconds(animationDurationSeconds), Children = { keyframe1, keyframe2 },\n            };\n\n            await animation.RunAsync(animatableElement, token);\n\n            if (!token.IsCancellationRequested)\n                completedEvent?.Invoke(animatableElement, EventArgs.Empty);\n        }\n\n        public static void StartLoopingAnimation<T>(this UIElement animatableElement, StyledProperty<T> dependencyProperty, T toValue, double durationInSeconds, CancellationToken token)\n        {\n            var fromValue = (T)animatableElement.GetValue(dependencyProperty);\n\n            var keyframe1 = new KeyFrame()\n            {\n                Setters = { new Setter(dependencyProperty, fromValue), }, KeyTime = TimeSpan.FromSeconds(0)\n            };\n\n            var keyframe2 = new KeyFrame()\n            {\n                Setters = { new Setter(dependencyProperty, toValue), }, KeyTime = TimeSpan.FromSeconds(durationInSeconds)\n            };\n\n            var animation = new Avalonia.Animation.Animation()\n            {\n                Duration = TimeSpan.FromSeconds(durationInSeconds), Children = { keyframe1, keyframe2 },\n                IterationCount = IterationCount.Infinite\n            };\n\n            animation.RunAsync(animatableElement, token);\n        }\n\n        public static void CancelAnimation<T>(this UIElement animatableElement, StyledProperty<T> dependencyProperty, CancellationTokenSource? tokenSource)\n            => tokenSource?.Cancel();\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "Nodify/Helpers/DraggingOptimized.cs",
    "content": "﻿using System.Collections.Generic;\nusing System.Linq;\nusing System.Windows;\nusing System.Windows.Media;\n\nnamespace Nodify\n{\n    /// <summary>\n    /// Commits the position changes at the end of the operation. Updates the RenderTransform to preview the container position.\n    /// </summary>\n    internal sealed class DraggingOptimized : IDraggingStrategy\n    {\n        private readonly uint _gridCellSize;\n        private readonly List<ItemContainer> _selectedContainers;\n        private Vector _dragAccumulator = new Vector(0, 0);\n\n        public DraggingOptimized(IEnumerable<ItemContainer> containers, uint gridCellSize)\n        {\n            _gridCellSize = gridCellSize;\n            _selectedContainers = containers.Where(c => c.IsDraggable).ToList();\n        }\n\n        public void Abort()\n        {\n            for (var i = 0; i < _selectedContainers.Count; i++)\n            {\n                ItemContainer container = _selectedContainers[i];\n                var r = (TranslateTransform)container.RenderTransform;\n\n                r.X = 0;\n                r.Y = 0;\n\n                container.OnPreviewLocationChanged(container.Location);\n            }\n\n            _selectedContainers.Clear();\n        }\n\n        public void End()\n        {\n            for (var i = 0; i < _selectedContainers.Count; i++)\n            {\n                ItemContainer container = _selectedContainers[i];\n                var r = (TranslateTransform)container.RenderTransform;\n\n                Point result = container.Location + new Vector(r.X, r.Y);\n\n                // Correct the final position\n                if (NodifyEditor.EnableSnappingCorrection && (r.X != 0 || r.Y != 0))\n                {\n                    result = new Point((int)result.X / _gridCellSize * _gridCellSize,\n                        (int)result.Y / _gridCellSize * _gridCellSize);\n                }\n\n                container.SetCurrentValue(ItemContainer.LocationProperty, result);\n\n                r.X = 0;\n                r.Y = 0;\n            }\n\n            _selectedContainers.Clear();\n        }\n\n        public void Update(Vector change)\n        {\n            _dragAccumulator += change;\n            var delta = new Vector((int)_dragAccumulator.X / _gridCellSize * _gridCellSize, (int)_dragAccumulator.Y / _gridCellSize * _gridCellSize);\n            _dragAccumulator -= delta;\n\n            if (delta.X != 0 || delta.Y != 0)\n            {\n                for (var i = 0; i < _selectedContainers.Count; i++)\n                {\n                    ItemContainer container = _selectedContainers[i];\n                    var r = (TranslateTransform)container.RenderTransform;\n\n                    r.X += delta.X; // Snapping without correction\n                    r.Y += delta.Y; // Snapping without correction\n\n                    container.OnPreviewLocationChanged(container.Location + new Vector(r.X, r.Y));\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "Nodify/Helpers/DraggingSimple.cs",
    "content": "﻿using System.Collections.Generic;\nusing System.Linq;\nusing System.Windows;\n\nnamespace Nodify\n{\n    internal interface IDraggingStrategy\n    {\n        void Update(Vector change);\n        void End();\n        void Abort();\n    }\n\n    internal sealed class DraggingSimple : IDraggingStrategy\n    {\n        private readonly uint _gridCellSize;\n        private readonly List<ItemContainer> _selectedContainers;\n        private Vector _dragOffset = new Vector(0, 0);\n        private Vector _dragAccumulator = new Vector(0, 0);\n\n        public DraggingSimple(IEnumerable<ItemContainer> containers, uint gridCellSize)\n        {\n            _gridCellSize = gridCellSize;\n            _selectedContainers = containers.Where(c => c.IsDraggable).ToList();\n        }\n\n        public void Abort()\n        {\n            for (var i = 0; i < _selectedContainers.Count; i++)\n            {\n                ItemContainer container = _selectedContainers[i];\n                container.SetCurrentValue(ItemContainer.LocationProperty, container.Location - _dragOffset);\n            }\n\n            _selectedContainers.Clear();\n        }\n\n        public void End()\n        {\n            for (var i = 0; i < _selectedContainers.Count; i++)\n            {\n                ItemContainer container = _selectedContainers[i];\n                Point result = container.Location;\n\n                // Correct the final position\n                if (NodifyEditor.EnableSnappingCorrection)\n                {\n                    result = new Point(\n                    (int)result.X / _gridCellSize * _gridCellSize,\n                    (int)result.Y / _gridCellSize * _gridCellSize);\n                }\n\n                container.SetCurrentValue(ItemContainer.LocationProperty, result);\n            }\n\n            _selectedContainers.Clear();\n        }\n\n        public void Update(Vector change)\n        {\n            _dragAccumulator += change;\n            var delta = new Vector((int)_dragAccumulator.X / _gridCellSize * _gridCellSize, (int)_dragAccumulator.Y / _gridCellSize * _gridCellSize);\n            _dragAccumulator -= delta;\n\n            if (delta.X != 0 || delta.Y != 0)\n            {\n                _dragOffset += delta;\n\n                for (var i = 0; i < _selectedContainers.Count; i++)\n                {\n                    ItemContainer container = _selectedContainers[i];\n                    container.SetCurrentValue(ItemContainer.LocationProperty, new Point(container.Location.X + delta.X, container.Location.Y + delta.Y));\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "Nodify/Helpers/MathExtensions.cs",
    "content": "﻿namespace Nodify\n{\n    internal static class MathExtensions\n    {\n        /// <summary> Wraps a value within a specified range.</summary>\n        public static double WrapToRange(this double value, double min, double max)\n        {\n            double range = max - min;\n            value = (value - min) % range;\n\n            return value < 0 ? value + range + min : value + min;\n        }\n\n        public static double Clamp(this double value, double min, double max)\n        {\n            if (value < min)\n            {\n                return min;\n            }\n            else if (value > max)\n            {\n                return max;\n            }\n \n            return value;\n        }\n    }\n}\n"
  },
  {
    "path": "Nodify/Helpers/MultiGesture.cs",
    "content": "﻿using System.Windows.Input;\n\nnamespace Nodify\n{\n    /// <summary>Combines multiple input gestures.</summary>\n    public class MultiGesture : InputGesture\n    {\n        public static readonly MultiGesture None = new MultiGesture(Match.Any);\n\n        /// <summary>The strategy used by <see cref=\"Matches(object, InputEventArgs)\"/>.</summary>\n        public enum Match\n        {\n            /// <summary>At least one gesture must match.</summary>\n            Any,\n            /// <summary>All gestures must match.</summary>\n            All\n        }\n\n        private readonly InputGesture[] _gestures;\n        private readonly Match _match;\n\n        /// <summary>Constructs an instance of a <see cref=\"MultiGesture\"/>.</summary>\n        /// <param name=\"match\">The matching strategy.</param>\n        /// <param name=\"gestures\">The input gestures.</param>\n        public MultiGesture(Match match, params InputGesture[] gestures)\n        {\n            _gestures = gestures;\n            _match = match;\n        }\n\n        /// <inheritdoc />\n        public override bool Matches(object targetElement, EventArgs inputEventArgs)\n        {\n            if (_match == Match.Any)\n            {\n                return MatchesAny(targetElement, inputEventArgs);\n            }\n\n            return MatchesAll(targetElement, inputEventArgs);\n        }\n\n        private bool MatchesAll(object targetElement, EventArgs inputEventArgs)\n        {\n            for (int i = 0; i < _gestures.Length; i++)\n            {\n                if (!_gestures[i].Matches(targetElement, inputEventArgs))\n                {\n                    return false;\n                }\n            }\n\n            return true;\n        }\n\n        private bool MatchesAny(object targetElement, EventArgs inputEventArgs)\n        {\n            for (int i = 0; i < _gestures.Length; i++)\n            {\n                if (_gestures[i].Matches(targetElement, inputEventArgs))\n                {\n                    return true;\n                }\n            }\n\n            return false;\n        }\n    }\n\n    /// <inheritdoc cref=\"MultiGesture.Match.Any\" />\n    public sealed class AnyGesture : MultiGesture\n    {\n        public AnyGesture(params InputGesture[] gestures) : base(Match.Any, gestures)\n        {\n        }\n    }\n\n    /// <inheritdoc cref=\"MultiGesture.Match.All\" />\n    public sealed class AllGestures : MultiGesture\n    {\n        public AllGestures(params InputGesture[] gestures) : base(Match.All, gestures)\n        {\n        }\n    }\n\n    /// <summary>\n    /// An input gesture that allows changing its logic at runtime without changing its reference.\n    /// Useful for classes that capture the object reference without the posibility of updating it. (e.g. <see cref=\"EditorCommands\"/>)\n    /// </summary>\n    public sealed class InputGestureRef : InputGesture\n    {\n        /// <summary>The referenced gesture.</summary>\n        public InputGesture Value { get; set; } = MultiGesture.None;\n\n        private InputGestureRef() { }\n\n        public override bool Matches(object targetElement, EventArgs inputEventArgs)\n        {\n            return Value.Matches(targetElement, inputEventArgs);\n        }\n\n        public static implicit operator InputGestureRef(MouseGesture gesture)\n            => new InputGestureRef { Value = gesture };\n\n        public static implicit operator InputGestureRef(KeyGesture gesture)\n            => new InputGestureRef { Value = new InputKeyGesture(gesture) };\n\n        public static implicit operator InputGestureRef(InputKeyGesture gesture)\n            => new InputGestureRef { Value = gesture };\n\n        public static implicit operator InputGestureRef(MultiGesture gesture)\n            => new InputGestureRef { Value = gesture };\n    }\n}\n"
  },
  {
    "path": "Nodify/Helpers/PushItemsStrategy.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Diagnostics;\nusing System.Linq;\nusing System.Windows;\n\nnamespace Nodify\n{\n    internal interface IPushStrategy\n    {\n        Rect Start(Point position);\n        Rect Push(Vector amount);\n        Rect End();\n        Rect Cancel();\n        Rect OnViewportChanged();\n    }\n\n    internal abstract class BasePushStrategy : IPushStrategy\n    {\n        private IDraggingStrategy? _draggingStrategy;\n        private const double _minOffset = 2;\n        private double _actualOffset;\n        private double _initialPosition;\n\n        protected readonly NodifyEditor Editor;\n        protected const double OffscreenOffset = 100d;\n\n        public BasePushStrategy(NodifyEditor editor)\n        {\n            Editor = editor;\n        }\n\n        public Rect Start(Point position)\n        {\n            var containers = GetFilteredContainers(position);\n            _draggingStrategy = Editor.CreateDraggingStrategy(containers);\n\n            _initialPosition = GetInitialPosition(position);\n            _actualOffset = 0;\n\n            return CalculatePushedArea(_initialPosition, _actualOffset);\n        }\n\n        public Rect Push(Vector amount)\n        {\n            Debug.Assert(_draggingStrategy != null);\n\n            var offset = GetPushOffset(amount);\n            _draggingStrategy!.Update(offset);\n\n            _actualOffset += offset.X;\n            _actualOffset += offset.Y;\n\n            double newPosition = _actualOffset >= 0 ? _initialPosition : Editor.SnapToGrid(_initialPosition + _actualOffset);\n            double newOffset = Math.Max(_minOffset, Editor.SnapToGrid(_actualOffset));\n\n            return CalculatePushedArea(newPosition, newOffset);\n        }\n\n        public Rect End()\n        {\n            Debug.Assert(_draggingStrategy != null);\n            _draggingStrategy!.End();\n            return new Rect();\n        }\n\n        public Rect Cancel()\n        {\n            Debug.Assert(_draggingStrategy != null);\n            _draggingStrategy!.Abort();\n            return new Rect();\n        }\n\n        protected abstract IEnumerable<ItemContainer> GetFilteredContainers(Point position);\n        protected abstract double GetInitialPosition(Point position);\n        protected abstract Vector GetPushOffset(Vector offset);\n        protected abstract Rect CalculatePushedArea(double position, double offset);\n        public abstract Rect OnViewportChanged();\n    }\n\n    internal sealed class HorizontalPushStrategy : BasePushStrategy\n    {\n        public HorizontalPushStrategy(NodifyEditor editor) : base(editor)\n        {\n        }\n\n        protected override IEnumerable<ItemContainer> GetFilteredContainers(Point position)\n            => Editor.ItemContainers.Where(item => item.Location.X >= position.X);\n\n        protected override double GetInitialPosition(Point position)\n            => position.X;\n\n        protected override Vector GetPushOffset(Vector offset)\n            => new Vector(offset.X, 0d);\n\n        protected override Rect CalculatePushedArea(double position, double offset)\n            => new Rect(position, Editor.ViewportLocation.Y - OffscreenOffset, offset, Editor.ViewportSize.Height + OffscreenOffset * 2);\n\n        public override Rect OnViewportChanged()\n            => CalculatePushedArea(Editor.PushedArea.X, Editor.PushedArea.Width);\n    }\n\n    internal sealed class VerticalPushStrategy : BasePushStrategy\n    {\n        public VerticalPushStrategy(NodifyEditor editor) : base(editor)\n        {\n        }\n\n        protected override IEnumerable<ItemContainer> GetFilteredContainers(Point position)\n            => Editor.ItemContainers.Where(item => item.Location.Y >= position.Y);\n\n        protected override double GetInitialPosition(Point position)\n            => position.Y;\n\n        protected override Vector GetPushOffset(Vector offset)\n            => new Vector(0d, offset.Y);\n\n        protected override Rect CalculatePushedArea(double position, double offset)\n            => new Rect(Editor.ViewportLocation.X - OffscreenOffset, position, Editor.ViewportSize.Width + OffscreenOffset * 2, offset);\n\n        public override Rect OnViewportChanged()\n            => CalculatePushedArea(Editor.PushedArea.Y, Editor.PushedArea.Height);\n    }\n}\n"
  },
  {
    "path": "Nodify/Helpers/SelectionHelper.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Input;\n\nnamespace Nodify\n{\n    /// <summary>\n    /// Helps with selecting <see cref=\"ItemContainer\"/>s and updating the <see cref=\"NodifyEditor.SelectedArea\"/> and <see cref=\"NodifyEditor.IsSelecting\"/> properties.\n    /// </summary>\n    public sealed class SelectionHelper\n    {\n        private readonly NodifyEditor _host;\n        private Point _startLocation;\n        private SelectionType _selectionType;\n        private bool _isRealtime;\n        private IReadOnlyList<ItemContainer> _initialSelection = new List<ItemContainer>();\n\n        /// <summary>Constructs a new instance of a <see cref=\"SelectionHelper\"/>.</summary>\n        /// <param name=\"host\">The editor to select items from.</param>\n        public SelectionHelper(NodifyEditor host)\n            => _host = host;\n\n        /// <summary>Available selection logic.</summary>\n        public enum SelectionType\n        {\n            /// <summary>Replaces the old selection.</summary>\n            Replace,\n            /// <summary>Removes items from existing selection.</summary>\n            Remove,\n            /// <summary>Adds items to the current selection.</summary>\n            Append,\n            /// <summary>Inverts the selection.</summary>\n            Invert\n        }\n\n        /// <summary>Attempts to start a new selection.</summary>\n        /// <param name=\"location\">The location inside the graph.</param>\n        /// <param name=\"selectionType\">The type of selection.</param>\n        /// <remarks>Will not do anything if selection is in progress.</remarks>\n        public void Start(Point location, SelectionType selectionType)\n        {\n            if (!_host.IsSelecting)\n            {\n                _selectionType = selectionType;\n                _initialSelection = _host.SelectedContainers;\n\n                _isRealtime = _host.EnableRealtimeSelection;\n                _startLocation = location;\n\n                _host.SelectedArea = new Rect();\n                _host.IsSelecting = true;\n            }\n        }\n\n        /// <summary>Update the end location for the selection.</summary>\n        /// <param name=\"endLocation\">An absolute location.</param>\n        public void Update(Point endLocation)\n        {\n            double left = endLocation.X < _startLocation.X ? endLocation.X : _startLocation.X;\n            double top = endLocation.Y < _startLocation.Y ? endLocation.Y : _startLocation.Y;\n            double width = Math.Abs(endLocation.X - _startLocation.X);\n            double height = Math.Abs(endLocation.Y - _startLocation.Y);\n\n            _host.SelectedArea = new Rect(left, top, width, height);\n\n            if (_isRealtime)\n            {\n                PreviewSelection(_host.SelectedArea);\n            }\n        }\n\n        /// <summary>Commits the current selection to the editor.</summary>\n        public void End()\n        {\n            if (_host.IsSelecting)\n            {\n                PreviewSelection(_host.SelectedArea);\n\n                _host.ApplyPreviewingSelection();\n                _host.IsSelecting = false;\n            }\n        }\n\n        /// <summary>Aborts the current selection.</summary>\n        public void Abort()\n        {\n            if (_host.IsSelecting)\n            {\n                _host.ClearPreviewingSelection();\n                _host.IsSelecting = false;\n            }\n        }\n\n        private void PreviewSelection(Rect area)\n        {\n            switch (_selectionType)\n            {\n                case SelectionType.Replace:\n                    PreviewSelectArea(area);\n                    break;\n\n                case SelectionType.Remove:\n                    PreviewSelectContainers(_initialSelection);\n\n                    PreviewUnselectArea(area);\n                    break;\n\n                case SelectionType.Append:\n                    PreviewUnselectAll();\n                    PreviewSelectContainers(_initialSelection);\n\n                    PreviewSelectArea(area, true);\n                    break;\n\n                case SelectionType.Invert:\n                    PreviewUnselectAll();\n                    PreviewSelectContainers(_initialSelection);\n\n                    PreviewInvertSelection(area);\n                    break;\n\n                default:\n                    throw new NotImplementedException(nameof(SelectionType));\n            }\n        }\n\n        private void PreviewUnselectAll()\n        {\n            ItemCollection items = _host.Items;\n            for (var i = 0; i < items.Count; i++)\n            {\n                var container = (ItemContainer)_host.ItemContainerGenerator.ContainerFromIndex(i);\n                container.IsPreviewingSelection = false;\n            }\n        }\n\n        private void PreviewSelectArea(Rect area, bool append = false, bool fit = false)\n        {\n            if (!append)\n            {\n                PreviewUnselectAll();\n            }\n\n            if (area.X != 0 || area.Y != 0 || area.Width > 0 || area.Height > 0)\n            {\n                ItemCollection items = _host.Items;\n                for (var i = 0; i < items.Count; i++)\n                {\n                    var container = (ItemContainer)_host.ItemContainerGenerator.ContainerFromIndex(i);\n                    if (container.IsSelectableInArea(area, fit))\n                    {\n                        container.IsPreviewingSelection = true;\n                    }\n                }\n            }\n        }\n\n        private void PreviewUnselectArea(Rect area, bool fit = false)\n        {\n            ItemCollection items = _host.Items;\n            for (var i = 0; i < items.Count; i++)\n            {\n                var container = (ItemContainer)_host.ItemContainerGenerator.ContainerFromIndex(i);\n                if (container.IsSelectableInArea(area, fit))\n                {\n                    container.IsPreviewingSelection = false;\n                }\n            }\n        }\n\n        private static void PreviewSelectContainers(IReadOnlyList<ItemContainer> containers)\n        {\n            for (var i = 0; i < containers.Count; i++)\n            {\n                containers[i].IsPreviewingSelection = true;\n            }\n        }\n\n        private void PreviewInvertSelection(Rect area, bool fit = false)\n        {\n            ItemCollection items = _host.Items;\n            for (var i = 0; i < items.Count; i++)\n            {\n                var container = (ItemContainer)_host.ItemContainerGenerator.ContainerFromIndex(i);\n                if (container.IsSelectableInArea(area, fit))\n                {\n                    container.IsPreviewingSelection = !container.IsPreviewingSelection;\n                }\n            }\n        }\n\n        internal static SelectionType GetSelectionType(MouseButtonEventArgs e)\n        {\n            EditorGestures.SelectionGestures gestures = EditorGestures.Mappings.Editor.Selection;\n            if (gestures.Append.Matches(e.Source, e))\n            {\n                return SelectionType.Append;\n            }\n\n            if (gestures.Invert.Matches(e.Source, e))\n            {\n                return SelectionType.Invert;\n            }\n\n            if (gestures.Remove.Matches(e.Source, e))\n            {\n                return SelectionType.Remove;\n            }\n\n            return SelectionType.Replace;\n        }\n    }\n}\n"
  },
  {
    "path": "Nodify/Helpers/UnscaleTransformConverter.cs",
    "content": "﻿using System;\nusing System.Globalization;\nusing System.Windows;\nusing System.Windows.Data;\nusing System.Windows.Media;\n\nnamespace Nodify\n{\n    internal class UnscaleTransformConverter : IValueConverter\n    {\n        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)\n        {\n            if (value is TransformGroup transformGroup)\n                return new MatrixTransform(transformGroup.Children[0].Value.Invert());\n            return null;\n        }\n\n        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)\n        {\n            return value;\n        }\n    }\n\n    internal class ScaleDoubleConverter : IMultiValueConverter\n    {\n        public object Convert(IList<object?> values, Type targetType, object parameter, CultureInfo culture)\n        {\n            if (values.Count == 2 && values[0] is double d1 && values[1] is double d2)\n                return d1 * d2;\n            return null;\n        }\n\n        public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)\n        {\n            throw new NotImplementedException();\n        }\n    }\n\n    internal class ScalePointConverter : IMultiValueConverter\n    {\n        public object? Convert(IList<object?> values, Type targetType, object? parameter, CultureInfo culture)\n        {\n            if (values.Any(x => x is UnsetValueType)) return false;\n\n            Point result = (Point)((Vector)(Point)values[0]! * (double)values[1]!);\n            return result;\n        }\n\n        public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)\n        {\n            throw new NotImplementedException();\n        }\n    }\n}\n"
  },
  {
    "path": "Nodify/ItemContainer.Avalonia.cs",
    "content": "using Avalonia;\nusing Avalonia.Controls;\n\nnamespace Nodify\n{\n    public partial class ItemContainer\n    {\n        protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)\n        {\n            base.OnPropertyChanged(change);\n            if (change.Property == IsPreviewingSelectionProperty)\n            {\n                UpdatePseudoClasses();\n            }\n        }\n\n        private void UpdatePseudoClasses()\n        {\n            PseudoClasses.Set(\":previewing-selection\", IsPreviewingSelection == true);\n            PseudoClasses.Set(\":not-previewing-selection\", IsPreviewingSelection == false);\n            PseudoClasses.Set(\":null-previewing-selection\", IsPreviewingSelection == null);\n        }\n    }\n}"
  },
  {
    "path": "Nodify/ItemContainer.cs",
    "content": "﻿using System.Collections.Generic;\nusing System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Controls.Primitives;\nusing System.Windows.Input;\nusing System.Windows.Media;\n\nnamespace Nodify\n{\n    /// <summary>\n    /// Delegate used to notify when an <see cref=\"ItemContainer\"/> is previewing a new location.\n    /// </summary>\n    /// <param name=\"newLocation\">The new location.</param>\n    public delegate void PreviewLocationChanged(Point newLocation);\n\n    /// <summary>\n    /// The container for all the items generated by the <see cref=\"ItemsControl.ItemsSource\"/> of the <see cref=\"NodifyEditor\"/>.\n    /// </summary>\n    public partial class ItemContainer : ContentControl, INodifyCanvasItem, ISelectable\n    {\n        #region Dependency Properties\n\n        public static readonly StyledProperty<IBrush> HighlightBrushProperty = AvaloniaProperty.Register<ItemContainer, IBrush>(nameof(HighlightBrush));\n        public static readonly StyledProperty<IBrush> SelectedBrushProperty = AvaloniaProperty.Register<ItemContainer, IBrush>(nameof(SelectedBrush));\n        public static readonly StyledProperty<Thickness> SelectedBorderThicknessProperty = AvaloniaProperty.Register<ItemContainer, Thickness>(nameof(SelectedBorderThickness), BoxValue.Thickness2);\n        public static readonly StyledProperty<bool> IsSelectableProperty = AvaloniaProperty.Register<ItemContainer, bool>(nameof(IsSelectable), BoxValue.True);\n        public static readonly StyledProperty<bool> IsSelectedProperty = SelectingItemsControl.IsSelectedProperty.AddOwner<ItemContainer>();\n        public static readonly DirectProperty<ItemContainer, bool?> IsPreviewingSelectionProperty = AvaloniaProperty.RegisterDirect<ItemContainer, bool?>(nameof(IsPreviewingSelection), x => x.IsPreviewingSelection);\n        public static readonly StyledProperty<Point> LocationProperty = AvaloniaProperty.Register<ItemContainer, Point>(nameof(Location), BoxValue.Point, defaultBindingMode: BindingMode.TwoWay);\n        public static readonly StyledProperty<Size> ActualSizeProperty = AvaloniaProperty.Register<ItemContainer, Size>(nameof(ActualSize), BoxValue.Size);\n        public static readonly StyledProperty<Size?> DesiredSizeForSelectionProperty = AvaloniaProperty.Register<ItemContainer, Size?>(nameof(DesiredSizeForSelection), null); //, FrameworkPropertyMetadataOptions.NotDataBindable);\n        public static readonly DirectProperty<ItemContainer, bool> IsPreviewingLocationProperty = AvaloniaProperty.RegisterDirect<ItemContainer, bool>(nameof(IsPreviewingLocation), x => x.IsPreviewingLocation);\n        public static readonly StyledProperty<bool> IsDraggableProperty = AvaloniaProperty.Register<ItemContainer, bool>(nameof(IsDraggable), BoxValue.True);\n\n        /// <summary>\n        /// Gets or sets the brush used when the <see cref=\"PendingConnection.IsOverElementProperty\"/> attached property is true for this <see cref=\"ItemContainer\"/>.\n        /// </summary>\n        public IBrush HighlightBrush\n        {\n            get => (IBrush)GetValue(HighlightBrushProperty);\n            set => SetValue(HighlightBrushProperty, value);\n        }\n\n        /// <summary>\n        /// Gets or sets the brush used when <see cref=\"IsSelected\"/> or <see cref=\"IsPreviewingSelection\"/> is true.\n        /// </summary>\n        public IBrush SelectedBrush\n        {\n            get => (IBrush)GetValue(SelectedBrushProperty);\n            set => SetValue(SelectedBrushProperty, value);\n        }\n\n        /// <summary>\n        /// Gets or sets the border thickness used when <see cref=\"IsSelected\"/> or <see cref=\"IsPreviewingSelection\"/> is true.\n        /// </summary>\n        public Thickness SelectedBorderThickness\n        {\n            get => (Thickness)GetValue(SelectedBorderThicknessProperty);\n            set => SetValue(SelectedBorderThicknessProperty, value);\n        }\n\n        /// <summary>\n        /// Gets or sets the location of this <see cref=\"ItemContainer\"/> inside the <see cref=\"NodifyEditor\"/> in graph space coordinates.\n        /// </summary>\n        public Point Location\n        {\n            get => (Point)GetValue(LocationProperty);\n            set => SetValue(LocationProperty, value);\n        }\n\n        /// <summary>\n        /// Gets or sets a value that indicates whether this <see cref=\"ItemContainer\"/> is selected.\n        /// Can only be set if <see cref=\"IsSelectable\"/> is true.\n        /// </summary>\n        public bool IsSelected\n        {\n            get => (bool)GetValue(IsSelectedProperty);\n            set => SetValue(IsSelectedProperty, value);\n        }\n\n        private bool? isPreviewingSelection;\n        /// <summary>\n        /// Gets a value indicating whether this <see cref=\"ItemContainer\"/> is about to change its <see cref=\"IsSelected\"/> state.\n        /// </summary>\n        public bool? IsPreviewingSelection\n        {\n            get => isPreviewingSelection;\n            internal set => SetAndRaise(IsPreviewingSelectionProperty, ref isPreviewingSelection, value);\n        }\n\n        /// <summary>\n        /// Gets or sets whether this <see cref=\"ItemContainer\"/> can be selected.\n        /// </summary>\n        public bool IsSelectable\n        {\n            get => (bool)GetValue(IsSelectableProperty);\n            set => SetValue(IsSelectableProperty, value);\n        }\n\n        private bool isPreviewingLocation;\n        /// <summary>\n        /// Gets a value indicating whether this <see cref=\"ItemContainer\"/> is previewing a new location but didn't logically move there.\n        /// </summary>\n        public bool IsPreviewingLocation\n        {\n            get => isPreviewingLocation;\n            internal set => SetAndRaise(IsPreviewingLocationProperty, ref isPreviewingLocation, value);\n        }\n\n        /// <summary>\n        /// Gets the actual size of this <see cref=\"ItemContainer\"/>.\n        /// </summary>\n        public Size ActualSize\n        {\n            get => (Size)GetValue(ActualSizeProperty);\n            set => SetValue(ActualSizeProperty, value);\n        }\n\n        /// <summary>\n        /// Overrides the size to check against when calculating if this <see cref=\"ItemContainer\"/> can be part of the current <see cref=\"NodifyEditor.SelectedArea\"/>.\n        /// Defaults to <see cref=\"UIElement.RenderSize\"/>.\n        /// </summary>\n        public Size? DesiredSizeForSelection\n        {\n            get => (Size?)GetValue(DesiredSizeForSelectionProperty);\n            set => SetValue(DesiredSizeForSelectionProperty, value);\n        }\n\n        /// <summary>\n        /// Gets or sets whether this <see cref=\"ItemContainer\"/> can be dragged.\n        /// </summary>\n        public bool IsDraggable\n        {\n            get => (bool)GetValue(IsDraggableProperty);\n            set => SetValue(IsDraggableProperty, value);\n        }\n\n        private static void OnLocationChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)\n        {\n            var item = (ItemContainer)d;\n            item.OnLocationChanged();\n\n            if (!item.Editor.IsBulkUpdatingItems)\n            {\n                item.Editor.ItemsHost.InvalidateArrange();\n            }\n        }\n\n        #endregion\n\n        #region Routed Events\n\n        public static readonly RoutedEvent<DragStartedEventArgs> DragStartedEvent = RoutedEvent.Register<DragStartedEventArgs>(nameof(DragStarted), RoutingStrategies.Bubble, typeof(ItemContainer));\n        public static readonly RoutedEvent<DragCompletedEventArgs> DragCompletedEvent = RoutedEvent.Register<DragCompletedEventArgs>(nameof(DragCompleted), RoutingStrategies.Bubble, typeof(ItemContainer));\n        public static readonly RoutedEvent<DragDeltaEventArgs> DragDeltaEvent = RoutedEvent.Register<DragDeltaEventArgs>(nameof(DragDelta), RoutingStrategies.Bubble, typeof(ItemContainer));\n        public static readonly RoutedEvent<RoutedEventArgs> SelectedEvent = RoutedEvent.Register<RoutedEventArgs>(nameof(Selected), RoutingStrategies.Bubble, typeof(ItemContainer));\n        public static readonly RoutedEvent<RoutedEventArgs> UnselectedEvent = RoutedEvent.Register<RoutedEventArgs>(nameof(Unselected), RoutingStrategies.Bubble, typeof(ItemContainer));\n        public static readonly RoutedEvent<RoutedEventArgs> LocationChangedEvent = RoutedEvent.Register<RoutedEventArgs>(nameof(LocationChanged), RoutingStrategies.Bubble, typeof(ItemContainer));\n\n        /// <summary>\n        /// Occurs when the <see cref=\"Location\"/> of this <see cref=\"ItemContainer\"/> is changed.\n        /// </summary>\n        public event RoutedEventHandler LocationChanged\n        {\n            add => AddHandler(LocationChangedEvent, value);\n            remove => RemoveHandler(LocationChangedEvent, value);\n        }\n\n        /// <summary>\n        /// Occurs when this <see cref=\"ItemContainer\"/> is the instigator of a drag operation.\n        /// </summary>\n        public event EventHandler<DragEventArgs> DragStarted\n        {\n            add => AddHandler(DragStartedEvent, value);\n            remove => RemoveHandler(DragStartedEvent, value);\n        }\n\n        /// <summary>\n        /// Occurs when this <see cref=\"ItemContainer\"/> is being dragged.\n        /// </summary>\n        public event EventHandler<DragEventArgs> DragDelta\n        {\n            add => AddHandler(DragDeltaEvent, value);\n            remove => RemoveHandler(DragDeltaEvent, value);\n        }\n\n        /// <summary>\n        /// Occurs when this <see cref=\"ItemContainer\"/> completed the drag operation.\n        /// </summary>\n        public event EventHandler<DragEventArgs> DragCompleted\n        {\n            add => AddHandler(DragCompletedEvent, value);\n            remove => RemoveHandler(DragCompletedEvent, value);\n        }\n\n        /// <summary>\n        /// Occurs when this <see cref=\"ItemContainer\"/> is selected.\n        /// </summary>\n        public event RoutedEventHandler Selected\n        {\n            add => AddHandler(SelectedEvent, value);\n            remove => RemoveHandler(SelectedEvent, value);\n        }\n\n        /// <summary>\n        /// Occurs when this <see cref=\"ItemContainer\"/> is unselected.\n        /// </summary>\n        public event RoutedEventHandler Unselected\n        {\n            add => AddHandler(UnselectedEvent, value);\n            remove => RemoveHandler(UnselectedEvent, value);\n        }\n\n        /// <summary>\n        /// Raises the <see cref=\"LocationChangedEvent\"/> and sets <see cref=\"IsPreviewingLocation\"/> to false.\n        /// </summary>\n        protected void OnLocationChanged()\n        {\n            IsPreviewingLocation = false;\n            RaiseEvent(new RoutedEventArgs(LocationChangedEvent, this));\n        }\n\n        /// <summary>\n        /// Raises the <see cref=\"SelectedEvent\"/> or <see cref=\"UnselectedEvent\"/> based on <paramref name=\"newValue\"/>.\n        /// Called when the <see cref=\"IsSelected\"/> value is changed.\n        /// </summary>\n        /// <param name=\"newValue\">True if selected, false otherwise.</param>\n        protected void OnSelectedChanged(bool newValue)\n        {\n            // Don't raise the event if the editor is selecting\n            if (!Editor.IsSelecting)\n            {\n                RaiseEvent(new RoutedEventArgs(newValue ? SelectedEvent : UnselectedEvent, this));\n            }\n        }\n\n        private static void OnIsSelectedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)\n        {\n            var elem = (ItemContainer)d;\n            bool result = elem.IsSelectable && (bool)e.NewValue;\n            elem.IsSelected = result;\n            elem.OnSelectedChanged(result);\n        }\n\n        #endregion\n\n        #region Fields\n\n        /// <summary>\n        /// Gets or sets whether cancelling a dragging operation is allowed.\n        /// </summary>\n        public static bool AllowDraggingCancellation { get; set; } = true;\n\n        /// <summary>\n        /// The <see cref=\"NodifyEditor\"/> that owns this <see cref=\"ItemContainer\"/>.\n        /// </summary>\n        public NodifyEditor Editor { get; }\n\n        /// <summary>\n        /// The calculated margin when the container is selected or previewing selection.\n        /// </summary>\n        public Thickness SelectedMargin => new Thickness(\n            BorderThickness.Left - SelectedBorderThickness.Left,\n            BorderThickness.Top - SelectedBorderThickness.Top,\n            BorderThickness.Right - SelectedBorderThickness.Right,\n            BorderThickness.Bottom - SelectedBorderThickness.Bottom);\n\n        #endregion\n\n        /// <summary>\n        /// Occurs when the <see cref=\"ItemContainer\"/> is previewing a new location.\n        /// </summary>\n        public event PreviewLocationChanged? PreviewLocationChanged;\n\n        /// <summary>\n        /// Raises the <see cref=\"PreviewLocationChanged\"/> event and sets the <see cref=\"IsPreviewingLocation\"/> property to true.\n        /// </summary>\n        /// <param name=\"newLocation\">The new location.</param>\n        protected internal void OnPreviewLocationChanged(Point newLocation)\n        {\n            IsPreviewingLocation = true;\n            PreviewLocationChanged?.Invoke(newLocation);\n        }\n\n        static ItemContainer()\n        {\n            DefaultStyleKeyProperty.OverrideMetadata(typeof(ItemContainer), new FrameworkPropertyMetadata(typeof(ItemContainer)));\n            SelectableMixin.Attach<ItemContainer>(IsSelectedProperty);\n            PressedMixin.Attach<ItemContainer>();\n            FocusableProperty.OverrideDefaultValue<ItemContainer>(true);\n            LocationProperty.Changed.AddClassHandler<ItemContainer>(OnLocationChanged);\n            IsSelectedProperty.OverrideMetadata<ItemContainer>(new StyledPropertyMetadata<bool>(false, BindingMode.TwoWay));\n            IsSelectableProperty.Changed.AddClassHandler<ItemContainer>(OnIsSelectedChanged);\n        }\n\n        /// <summary>\n        /// Constructs an instance of an <see cref=\"ItemContainer\"/> in the specified <see cref=\"NodifyEditor\"/>.\n        /// </summary>\n        /// <param name=\"editor\"></param>\n        public ItemContainer(NodifyEditor editor)\n        {\n            Editor = editor;\n            _states.Push(GetInitialState());\n            UpdatePseudoClasses();\n        }\n\n        protected override void OnApplyTemplate(TemplateAppliedEventArgs e)\n        {\n            base.OnApplyTemplate(e);\n\n            State.Enter(null, null);\n        }\n\n        /// <inheritdoc />\n        protected override void OnSizeChanged(SizeChangedInfo sizeInfo)\n        {\n            SetCurrentValue(ActualSizeProperty, sizeInfo.NewSize);\n            base.OnSizeChanged(sizeInfo);\n        }\n\n        /// <summary>\n        /// Checks if <paramref name=\"position\"/> is selectable.\n        /// </summary>\n        /// <param name=\"position\">A position relative to this <see cref=\"ItemContainer\"/>.</param>\n        /// <returns>True if <paramref name=\"position\"/> is selectable.</returns>\n        protected virtual bool IsSelectableLocation(Point position)\n        {\n            Size size = DesiredSizeForSelection ?? Bounds.Size;// RenderSize;\n            return position.X >= 0 && position.Y >= 0 && position.X <= size.Width && position.Y <= size.Height;\n        }\n\n        /// <summary>\n        /// Checks if <paramref name=\"area\"/> contains or intersects with this <see cref=\"ItemContainer\"/> taking into consideration the <see cref=\"DesiredSizeForSelection\"/>.\n        /// </summary>\n        /// <param name=\"area\">The area to check if contains or intersects this <see cref=\"ItemContainer\"/>.</param>\n        /// <param name=\"isContained\">If true will check if <paramref name=\"area\"/> contains this, otherwise will check if <paramref name=\"area\"/> intersects with this.</param>\n        /// <returns>True if <paramref name=\"area\"/> contains or intersects this <see cref=\"ItemContainer\"/>.</returns>\n        public virtual bool IsSelectableInArea(Rect area, bool isContained)\n        {\n            var bounds = new Rect(Location, DesiredSizeForSelection ?? Bounds.Size /* RenderSize */);\n            return isContained ? area.Contains(bounds) : area.Intersects(bounds);\n        }\n\n        #region State Handling\n\n        private readonly Stack<ContainerState> _states = new Stack<ContainerState>();\n\n        /// <summary>The current state of the container.</summary>\n        public ContainerState State => _states.Peek();\n\n        /// <summary>Creates the initial state of the container.</summary>\n        /// <returns>The initial state.</returns>\n        protected virtual ContainerState GetInitialState()\n            => new ContainerDefaultState(this);\n\n        /// <summary>Pushes the given state to the stack.</summary>\n        /// <param name=\"state\">The new state of the container.</param>\n        /// <remarks>Calls <see cref=\"ContainerState.Enter\"/> on the new state.</remarks>\n        public void PushState(ContainerState state, MouseEventArgs? e)\n        {\n            var prev = State;\n            _states.Push(state);\n            state.Enter(prev, e);\n        }\n\n        /// <summary>Pops the current <see cref=\"State\"/> from the stack.</summary>\n        /// <remarks>It doesn't pop the initial state. (see <see cref=\"GetInitialState\"/>)\n        /// <br />Calls <see cref=\"ContainerState.Exit\"/> on the current state.\n        /// <br />Calls <see cref=\"ContainerState.ReEnter\"/> on the previous state.\n        /// </remarks>\n        public void PopState()\n        {\n            // Never remove the default state\n            if (_states.Count > 1)\n            {\n                ContainerState prev = _states.Pop();\n                prev.Exit();\n                State.ReEnter(prev);\n            }\n        }\n\n        /// <summary>Pops all states from the container.</summary>\n        /// <remarks>It doesn't pop the initial state. (see <see cref=\"GetInitialState\"/>)</remarks>\n        public void PopAllStates()\n        {\n            while (_states.Count > 1)\n            {\n                PopState();\n            }\n        }\n\n        /// <inheritdoc />\n        protected override void OnPointerPressed(PointerPressedEventArgs e)\n        {\n            if (IsSelectableLocation(e.GetPosition(this)))\n            {\n                Focus();\n\n                e.Pointer.Capture(this);\n                this.PropagateMouseCapturedWithin(true);\n\n                State.HandleMouseDown(new MouseButtonEventArgs(e));\n            }\n        }\n\n        /// <inheritdoc />\n        protected override void OnPointerReleased(PointerReleasedEventArgs e)\n        {\n            if (IsSelectableLocation(e.GetPosition(this)) || ReferenceEquals(e.Pointer.Captured, this))\n            {\n                State.HandleMouseUp(new MouseButtonEventArgs(e));\n            }\n\n            // Release the mouse capture if all the mouse buttons are released\n            if (ReferenceEquals(e.Pointer.Captured, this) && e.GetCurrentPoint(this).Properties is { IsLeftButtonPressed: false, IsMiddleButtonPressed: false, IsRightButtonPressed: false })\n            {\n                e.Pointer.Capture(null);\n                this.PropagateMouseCapturedWithin(false);\n            }\n        }\n\n        /// <inheritdoc />\n        protected override void OnPointerMoved(PointerEventArgs e)\n        {\n            State.HandleMouseMove(new MouseMoveEventArgs(e));\n        }\n\n        /// <inheritdoc />\n        protected override void OnPointerWheelChanged(PointerWheelEventArgs e)\n        {\n            State.HandleMouseWheel(new MouseWheelEventArgs(e));\n        }\n\n        /// <inheritdoc />\n        protected override void OnPointerCaptureLost(PointerCaptureLostEventArgs e)\n            => PopAllStates();\n\n        protected override void OnKeyUp(KeyEventArgs e)\n            => State.HandleKeyUp(e);\n\n        protected override void OnKeyDown(KeyEventArgs e)\n            => State.HandleKeyDown(e);\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "Nodify/Minimap/Minimap.Avalonia.cs",
    "content": "namespace Nodify;\n\npublic partial class Minimap\n{\n    private Point viewportLocation;\n    \n    protected override bool NeedsContainerOverride(object? item, int index, out object? recycleKey)\n    {\n        recycleKey = null;\n        return !IsItemItsOwnContainerOverride(item);\n    }\n\n    protected override Control CreateContainerForItemOverride(object? item, int index, object? recycleKey)\n    {\n        return GetContainerForItemOverride() as Control;\n    }\n}"
  },
  {
    "path": "Nodify/Minimap/Minimap.cs",
    "content": "﻿using System;\nusing System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Controls.Primitives;\nusing System.Windows.Input;\nusing System.Windows.Shapes;\n\nnamespace Nodify\n{\n    /// <summary>\n    /// A minimap control that can position the viewport, and zoom in and out.\n    /// </summary>\n    [StyleTypedProperty(Property = nameof(ViewportStyle), StyleTargetType = typeof(Rectangle))]\n    [StyleTypedProperty(Property = nameof(ItemContainerTheme), StyleTargetType = typeof(MinimapItem))]\n    [TemplatePart(Name = ElementItemsHost, Type = typeof(Panel))]\n    public partial class Minimap : ItemsControl\n    {\n        protected const string ElementItemsHost = \"PART_ItemsHost\";\n\n        public static readonly DirectProperty<Minimap, Point> ViewportLocationProperty = NodifyEditor.ViewportLocationProperty.AddOwner<Minimap>(e => e.ViewportLocation, (e, v) => e.ViewportLocation = v, default, BindingMode.TwoWay);\n        public static readonly StyledProperty<Size> ViewportSizeProperty = NodifyEditor.ViewportSizeProperty.AddOwner<Minimap>();\n        public static readonly StyledProperty<ControlTheme> ViewportStyleProperty = AvaloniaProperty.Register<Minimap, ControlTheme>(nameof(ViewportStyle));\n        public static readonly StyledProperty<Rect> ExtentProperty = NodifyCanvas.ExtentProperty.AddOwner<Minimap>();\n        public static readonly StyledProperty<Rect> ItemsExtentProperty = AvaloniaProperty.Register<Minimap, Rect>(nameof(ItemsExtent));\n        public static readonly StyledProperty<Size> MaxViewportOffsetProperty = AvaloniaProperty.Register<Minimap, Size>(nameof(MaxViewportOffset), new Size(2000, 2000));\n        public static readonly StyledProperty<bool> ResizeToViewportProperty = AvaloniaProperty.Register<Minimap, bool>(nameof(ResizeToViewport));\n        public static readonly StyledProperty<bool> IsReadOnlyProperty = TextBox.IsReadOnlyProperty.AddOwner<Minimap>();\n\n        public static readonly RoutedEvent ZoomEvent = RoutedEvent.Register<ZoomEventArgs>(nameof(Zoom), RoutingStrategies.Bubble, typeof(Minimap));\n\n        /// <inheritdoc cref=\"NodifyEditor.ViewportLocation\" />\n        public Point ViewportLocation\n        {\n            get => viewportLocation;\n            set => SetAndRaise(ViewportLocationProperty, ref viewportLocation, value);\n        }\n\n        /// <inheritdoc cref=\"NodifyEditor.ViewportSize\" />\n        public Size ViewportSize\n        {\n            get => (Size)GetValue(ViewportSizeProperty);\n            set => SetValue(ViewportSizeProperty, value);\n        }\n\n        /// <summary>\n        /// Gets or sets the style to use for the viewport rectangle.\n        /// </summary>\n        public ControlTheme ViewportStyle\n        {\n            get => (ControlTheme)GetValue(ViewportStyleProperty);\n            set => SetValue(ViewportStyleProperty, value);\n        }\n\n        /// <summary>The area covered by the items and the viewport rectangle in graph space.</summary>\n        public Rect Extent\n        {\n            get => (Rect)GetValue(ExtentProperty);\n            set => SetValue(ExtentProperty, value);\n        }\n\n        /// <summary>The area covered by the <see cref=\"MinimapItem\"/>s in graph space.</summary>\n        public Rect ItemsExtent\n        {\n            get => (Rect)GetValue(ItemsExtentProperty);\n            set => SetValue(ItemsExtentProperty, value);\n        }\n\n        /// <summary>The max position from the <see cref=\"NodifyEditor.ItemsExtent\"/> that the viewport can move to.</summary>\n        public Size MaxViewportOffset\n        {\n            get => (Size)GetValue(MaxViewportOffsetProperty);\n            set => SetValue(MaxViewportOffsetProperty, value);\n        }\n\n        /// <summary>Whether the minimap should resize to also display the whole viewport.</summary>\n        public bool ResizeToViewport\n        {\n            get => (bool)GetValue(ResizeToViewportProperty);\n            set => SetValue(ResizeToViewportProperty, value);\n        }\n\n        /// <summary>Whether the minimap can move and zoom the viewport.</summary>\n        public bool IsReadOnly\n        {\n            get => (bool)GetValue(IsReadOnlyProperty);\n            set => SetValue(IsReadOnlyProperty, value);\n        }\n\n        /// <summary>Triggered when zooming in or out using the mouse wheel.</summary>\n        public event ZoomEventHandler Zoom\n        {\n            add => AddHandler(ZoomEvent, value);\n            remove => RemoveHandler(ZoomEvent, value);\n        }\n\n        /// <summary>\n        /// Gets the panel that holds all the <see cref=\"MinimapItem\"/>s.\n        /// </summary>\n        protected internal ItemsPresenter ItemsHost { get; private set; } = default!;\n\n        static Minimap()\n        {\n            DefaultStyleKeyProperty.OverrideMetadata(typeof(Minimap), new FrameworkPropertyMetadata(typeof(Minimap)));\n            ClipToBoundsProperty.OverrideMetadata(typeof(Minimap), new StyledPropertyMetadata<bool>(BoxValue.True));\n        }\n\n        protected override void OnApplyTemplate(TemplateAppliedEventArgs e)\n        {\n            base.OnApplyTemplate(e);\n\n            ItemsHost = e.NameScope.Get<ItemsPresenter>(\"PART_ItemsPresenter\");\n        }\n\n        protected bool IsDragging { get; private set; }\n\n        protected override void OnPointerPressed(PointerPressedEventArgs e)\n        {\n            var gestures = EditorGestures.Mappings.Minimap;\n            if (!IsReadOnly && gestures.DragViewport.Matches(this, e))\n            {\n                e.Pointer.Capture(this);\n                this.PropagateMouseCapturedWithin(true);\n                IsDragging = true;\n\n                SetViewportLocation(e.GetPosition(ItemsHost));\n\n                e.Handled = true;\n            }\n        }\n\n        protected override void OnPointerMoved(PointerEventArgs e)\n        {\n            if (IsDragging)\n            {\n                SetViewportLocation(e.GetPosition(ItemsHost));\n            }\n        }\n\n        private void SetViewportLocation(Point location)\n        {\n            var position = location - new Vector(ViewportSize.Width / 2, ViewportSize.Height / 2) + (Vector)Extent.Position;\n\n            if (MaxViewportOffset.Width != 0 || MaxViewportOffset.Height != 0)\n            {\n                double maxRight = ResizeToViewport ? ItemsExtent.Right : Math.Max(ItemsExtent.Right, ItemsExtent.Left + ViewportSize.Width);\n                double maxBottom = ResizeToViewport ? ItemsExtent.Bottom : Math.Max(ItemsExtent.Bottom, ItemsExtent.Top + ViewportSize.Height);\n\n                position = new Point(\n                             position.X.Clamp(ItemsExtent.Left - ViewportSize.Width / 2 - MaxViewportOffset.Width, maxRight - ViewportSize.Width / 2 + MaxViewportOffset.Width),\n                             position.Y.Clamp(ItemsExtent.Top - ViewportSize.Height / 2 - MaxViewportOffset.Height, maxBottom - ViewportSize.Height / 2 + MaxViewportOffset.Height)\n                             );\n            }\n\n            ViewportLocation = position;\n        }\n\n        protected override void OnPointerReleased(PointerReleasedEventArgs e)\n        {\n            var gestures = EditorGestures.Mappings.Minimap;\n            if (IsDragging && gestures.DragViewport.Matches(this, e))\n            {\n                IsDragging = false;\n            }\n\n            var props = e.GetCurrentPoint(this).Properties;\n            if (/*IsMouseCaptured && */ !props.IsRightButtonPressed && !props.IsLeftButtonPressed && !props.IsMiddleButtonPressed)\n            {\n                e.Pointer.Capture(null);\n                this.PropagateMouseCapturedWithin(false);\n            }\n        }\n\n        protected override void OnPointerWheelChanged(PointerWheelEventArgs e)\n        {\n            if (!IsReadOnly && !e.Handled && EditorGestures.Mappings.Minimap.ZoomModifierKey == e.KeyModifiers)\n            {\n                double zoom = Math.Pow(2.0, e.Delta.Length / 3.0 / NodifyEditor.MouseWheelDeltaForOneLine);\n                var location = ViewportLocation + ViewportSize.ToVector() / 2;\n\n                var args = new ZoomEventArgs(zoom, location)\n                {\n                    RoutedEvent = ZoomEvent,\n                    Source = this\n                };\n                RaiseEvent(args);\n\n                e.Handled = true;\n            }\n        }\n\n        protected DependencyObject GetContainerForItemOverride()\n            => new MinimapItem();\n\n        protected bool IsItemItsOwnContainerOverride(object item)\n            => item is MinimapItem;\n    }\n}\n"
  },
  {
    "path": "Nodify/Minimap/MinimapItem.cs",
    "content": "﻿using System.Windows;\nusing System.Windows.Controls;\n\nnamespace Nodify\n{\n    public class MinimapItem : ContentControl\n    {\n        public static readonly StyledProperty<Point> LocationProperty = ItemContainer.LocationProperty.AddOwner<MinimapItem>(new StyledPropertyMetadata<Point>(BoxValue.Point, BindingMode.TwoWay));\n\n        /// <summary>\n        /// Gets or sets the location of this <see cref=\"MinimapItem\"/> inside the <see cref=\"Minimap\"/>.\n        /// </summary>\n        public Point Location\n        {\n            get => (Point)GetValue(LocationProperty);\n            set => SetValue(LocationProperty, value);\n        }\n\n        static MinimapItem()\n        {\n            PanelUtilities.AffectsParentArrange<DecoratorContainer>(LocationProperty);\n        }\n    }\n}\n"
  },
  {
    "path": "Nodify/Minimap/MinimapPanel.Avalonia.cs",
    "content": "namespace Nodify;\n\ninternal partial class MinimapPanel\n{\n    private Point viewportLocation;\n    \n    static MinimapPanel()\n    {\n        AffectsMeasure<MinimapPanel>(ViewportSizeProperty, ViewportLocationProperty);\n    }\n}"
  },
  {
    "path": "Nodify/Minimap/MinimapPanel.cs",
    "content": "﻿using System;\nusing System.Windows;\nusing System.Windows.Controls;\n\nnamespace Nodify\n{\n    internal partial class MinimapPanel : Panel\n    {\n        public static readonly DirectProperty<MinimapPanel, Point> ViewportLocationProperty = NodifyEditor.ViewportLocationProperty.AddOwner<MinimapPanel>(e => e.ViewportLocation, (e, v) => e.ViewportLocation = v);\n        public static readonly StyledProperty<Size> ViewportSizeProperty = NodifyEditor.ViewportSizeProperty.AddOwner<MinimapPanel>();\n        public static readonly StyledProperty<Rect> ExtentProperty = NodifyCanvas.ExtentProperty.AddOwner<MinimapPanel>();\n        public static readonly StyledProperty<Rect> ItemsExtentProperty = Minimap.ItemsExtentProperty.AddOwner<MinimapPanel>();\n        public static readonly StyledProperty<bool> ResizeToViewportProperty = Minimap.ResizeToViewportProperty.AddOwner<MinimapPanel>();\n\n        /// <inheritdoc cref=\"Minimap.ViewportLocation\" />\n        public Point ViewportLocation\n        {\n            get => viewportLocation;\n            set => SetAndRaise(ViewportLocationProperty, ref viewportLocation, value);\n        }\n\n        /// <inheritdoc cref=\"Minimap.ViewportSize\" />\n        public Size ViewportSize\n        {\n            get => (Size)GetValue(ViewportSizeProperty);\n            set => SetValue(ViewportSizeProperty, value);\n        }\n\n        /// <inheritdoc cref=\"Minimap.Extent\" />\n        public Rect Extent\n        {\n            get => (Rect)GetValue(ExtentProperty);\n            set => SetValue(ExtentProperty, value);\n        }\n\n        /// <inheritdoc cref=\"Minimap.Extent\" />\n        public Rect ItemsExtent\n        {\n            get => (Rect)GetValue(ItemsExtentProperty);\n            set => SetValue(ItemsExtentProperty, value);\n        }\n\n        /// <inheritdoc cref=\"Minimap.ResizeToViewport\" />\n        public bool ResizeToViewport\n        {\n            get => (bool)GetValue(ResizeToViewportProperty);\n            set => SetValue(ResizeToViewportProperty, value);\n        }\n\n        protected override Size MeasureOverride(Size availableSize)\n        {\n            double minX = double.MaxValue;\n            double minY = double.MaxValue;\n\n            double maxX = double.MinValue;\n            double maxY = double.MinValue;\n\n            UIElementCollection children = Children;\n            for (int i = 0; i < children.Count; i++)\n            {\n                var item = (MinimapItem)children[i];\n                item.Measure(availableSize);\n\n                Size size = item.DesiredSize;\n\n                if (item.Location.X < minX)\n                {\n                    minX = item.Location.X;\n                }\n\n                if (item.Location.Y < minY)\n                {\n                    minY = item.Location.Y;\n                }\n\n                double sizeX = item.Location.X + size.Width;\n                if (sizeX > maxX)\n                {\n                    maxX = sizeX;\n                }\n\n                double sizeY = item.Location.Y + size.Height;\n                if (sizeY > maxY)\n                {\n                    maxY = sizeY;\n                }\n            }\n\n            var itemsExtent = minX == double.MaxValue\n                ? new Rect(0, 0, 0, 0)\n                : new Rect(minX, minY, maxX - minX, maxY - minY);\n\n            SetCurrentValue(ItemsExtentProperty, itemsExtent);\n\n            if (ResizeToViewport)\n            {\n                itemsExtent.Union(new Rect(ViewportLocation, ViewportSize));\n            }\n\n            SetCurrentValue(ExtentProperty, itemsExtent);\n\n            double width = Math.Max(itemsExtent.Size.Width, ViewportSize.Width);\n            double height = Math.Max(itemsExtent.Height, ViewportSize.Height);\n            return new Size(width, height);\n        }\n\n        protected override Size ArrangeOverride(Size finalSize)\n        {\n            UIElementCollection children = Children;\n            for (int i = 0; i < children.Count; i++)\n            {\n                var item = (MinimapItem)children[i];\n                item.Arrange(new Rect(item.Location - (Vector)Extent.Position, item.DesiredSize));\n            }\n\n            return finalSize;\n        }\n    }\n}\n"
  },
  {
    "path": "Nodify/Minimap/SubtractConverter.cs",
    "content": "﻿using System;\nusing System.Globalization;\nusing System.Windows.Data;\n\nnamespace Nodify\n{\n    internal class SubtractConverter : IMultiValueConverter\n    {\n        public object? Convert(IList<object?> values, Type targetType, object? parameter, CultureInfo culture)\n        {\n            if (values.Any(x => x is UnsetValueType)) return false;\n\n            double result = (double)values[0]! - (double)values[1]!;\n            return result;\n        }\n\n        public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)\n        {\n            throw new NotImplementedException();\n        }\n    }\n}\n"
  },
  {
    "path": "Nodify/Minimap/ZoomEventArgs.cs",
    "content": "﻿using System;\nusing System.Windows;\n\nnamespace Nodify\n{\n    /// <summary>\n    /// Represents the method that will handle <see cref=\"Minimap.Zoom\"/> routed event.\n    /// </summary>\n    /// <param name=\"sender\">The object where the event handler is attached.</param>\n    /// <param name=\"e\">The event data.</param>\n    public delegate void ZoomEventHandler(object sender, ZoomEventArgs e);\n\n    /// <summary>\n    /// Provides data for <see cref=\"Minimap.Zoom\"/> routed event.\n    /// </summary>\n    public class ZoomEventArgs : RoutedEventArgs\n    {\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"ZoomEventArgs\"/> class using the specified <see cref=\"Zoom\"/> and <see cref=\"Location\"/>.\n        /// </summary>\n        public ZoomEventArgs(double zoom, Point location)\n        {\n            Zoom = zoom;\n            Location = location;\n        }\n\n        /// <summary>\n        /// Gets the zoom amount.\n        /// </summary>\n        public double Zoom { get; }\n\n        /// <summary>\n        /// Gets the location where the editor should zoom in.\n        /// </summary>\n        public Point Location { get; }\n    }\n}\n"
  },
  {
    "path": "Nodify/Nodes/GroupingNode.cs",
    "content": "﻿using System;\nusing System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Controls.Primitives;\nusing System.Windows.Input;\nusing System.Windows.Media;\n\nnamespace Nodify\n{\n    /// <summary>\n    /// Specifies the possible movement modes of a <see cref=\"GroupingNode\"/>.\n    /// </summary>\n    public enum GroupingMovementMode\n    {\n        /// <summary>\n        /// The <see cref=\"GroupingNode\"/> will move its content when moved.\n        /// </summary>\n        Group,\n\n        /// <summary>\n        /// The <see cref=\"GroupingNode\"/> will not move its content when moved.\n        /// </summary>\n        Self\n    }\n\n    /// <summary>\n    /// Defines a panel with a header that groups <see cref=\"ItemContainer\"/>s inside it and can be resized.\n    /// </summary>\n    [TemplatePart(Name = ElementResizeThumb, Type = typeof(FrameworkElement))]\n    [TemplatePart(Name = ElementHeader, Type = typeof(FrameworkElement))]\n    [TemplatePart(Name = ElementContent, Type = typeof(FrameworkElement))]\n    public class GroupingNode : HeaderedContentControl\n    {\n        protected static readonly GroupingMovementMode GroupMovementBoxed = GroupingMovementMode.Group;\n\n        protected const string ElementResizeThumb = \"PART_ResizeThumb\";\n        protected const string ElementHeader = \"PART_Header\";\n        protected const string ElementContent = \"PART_Content\";\n\n        #region Routed Events\n\n        public static readonly RoutedEvent<ResizeEventArgs> ResizeStartedEvent = RoutedEvent.Register<ResizeEventArgs>(nameof(ResizeStarted), RoutingStrategies.Bubble, typeof(GroupingNode));\n        public static readonly RoutedEvent<ResizeEventArgs> ResizeCompletedEvent = RoutedEvent.Register<ResizeEventArgs>(nameof(ResizeCompleted), RoutingStrategies.Bubble, typeof(GroupingNode));\n\n        /// <summary>\n        /// Occurs when the node finished resizing.\n        /// </summary>\n        public event ResizeEventHandler ResizeCompleted\n        {\n            add => AddHandler(ResizeCompletedEvent, value);\n            remove => RemoveHandler(ResizeCompletedEvent, value);\n        }\n\n        /// <summary>\n        /// Occurs when the node started resizing.\n        /// </summary>\n        public event ResizeEventHandler ResizeStarted\n        {\n            add => AddHandler(ResizeStartedEvent, value);\n            remove => RemoveHandler(ResizeStartedEvent, value);\n        }\n\n        #endregion\n\n        #region Dependency Properties\n\n        public static readonly StyledProperty<IBrush> HeaderBrushProperty = Node.HeaderBrushProperty.AddOwner<GroupingNode>();\n        public static readonly StyledProperty<bool> CanResizeProperty = AvaloniaProperty.Register<GroupingNode, bool>(nameof(CanResize), BoxValue.True);\n        public static readonly StyledProperty<Size> ActualSizeProperty = AvaloniaProperty.Register<GroupingNode, Size>(nameof(ActualSize), BoxValue.Size, defaultBindingMode: BindingMode.TwoWay);\n        public static readonly StyledProperty<GroupingMovementMode> MovementModeProperty = AvaloniaProperty.Register<GroupingNode, GroupingMovementMode>(nameof(MovementMode), GroupMovementBoxed);\n        public static readonly StyledProperty<ICommand> ResizeCompletedCommandProperty = AvaloniaProperty.Register<GroupingNode, ICommand>(nameof(ResizeCompletedCommand));\n        public static readonly StyledProperty<ICommand> ResizeStartedCommandProperty = AvaloniaProperty.Register<GroupingNode, ICommand>(nameof(ResizeStartedCommand));\n        \n        private static void OnActualSizeChanged(AvaloniaObject d, AvaloniaPropertyChangedEventArgs e)\n        {\n            var node = (GroupingNode)d;\n            var newSize = (Size)e.NewValue;\n            node.Width = newSize.Width;\n            node.Height = newSize.Height;\n        }\n\n        /// <summary>\n        /// Gets or sets the brush used for the background of the <see cref=\"HeaderedContentControl.Header\"/> of this <see cref=\"GroupingNode\"/>.\n        /// </summary>\n        public IBrush HeaderBrush\n        {\n            get => (IBrush)GetValue(HeaderBrushProperty);\n            set => SetValue(HeaderBrushProperty, value);\n        }\n\n        /// <summary>\n        /// Gets or sets a value that indicates whether this <see cref=\"GroupingNode\"/> can be resized.\n        /// </summary>\n        public bool CanResize\n        {\n            get => (bool)GetValue(CanResizeProperty);\n            set => SetValue(CanResizeProperty, value);\n        }\n\n        /// <summary>\n        /// Gets or sets the actual size of this <see cref=\"GroupingNode\"/>.\n        /// </summary>\n        public Size ActualSize\n        {\n            get => (Size)GetValue(ActualSizeProperty);\n            set => SetValue(ActualSizeProperty, value);\n        }\n\n        /// <summary>\n        /// Gets or sets the default movement mode which can be temporarily changed by holding the <see cref=\"SwitchMovementModeModifierKey\"/> while dragging by the header.\n        /// </summary>\n        public GroupingMovementMode MovementMode\n        {\n            get => (GroupingMovementMode)GetValue(MovementModeProperty);\n            set => SetValue(MovementModeProperty, value);\n        }\n\n        /// <summary>\n        /// Invoked when the <see cref=\"ResizeCompleted\"/> event is not handled.\n        /// Parameter is the <see cref=\"ItemContainer.ActualSize\"/> of the container.\n        /// </summary>\n        public ICommand? ResizeCompletedCommand\n        {\n            get => (ICommand?)GetValue(ResizeCompletedCommandProperty);\n            set => SetValue(ResizeCompletedCommandProperty, value);\n        }\n\n        /// <summary>\n        /// Invoked when the <see cref=\"ResizeStarted\"/> event is not handled.\n        /// Parameter is the <see cref=\"ItemContainer.ActualSize\"/> of the container.\n        /// </summary>\n        public ICommand? ResizeStartedCommand\n        {\n            get => (ICommand?)GetValue(ResizeStartedCommandProperty);\n            set => SetValue(ResizeStartedCommandProperty, value);\n        }\n\n        #endregion\n\n        #region Fields\n\n        /// <summary>\n        /// Gets the <see cref=\"NodifyEditor\"/> that owns this <see cref=\"GroupingNode\"/>.\n        /// </summary>\n        protected NodifyEditor? Editor { get; private set; }\n\n        /// <summary>\n        /// Gets the <see cref=\"NodifyEditor\"/> that owns this <see cref=\"Container\"/>.\n        /// </summary>\n        protected ItemContainer? Container { get; private set; }\n\n        /// <summary>\n        /// Gets the <see cref=\"FrameworkElement\"/> used to resize this <see cref=\"GroupingNode\"/>.\n        /// </summary>\n        protected FrameworkElement? ResizeThumb;\n\n        /// <summary>\n        /// Gets the <see cref=\"HeaderedContentControl.Header\"/> control of this <see cref=\"GroupingNode\"/>.\n        /// </summary>\n        protected FrameworkElement? HeaderControl;\n\n        /// <summary>\n        /// Gets the <see cref=\"System.Windows.Controls.ContentControl\"/> control of this <see cref=\"GroupingNode\"/>.\n        /// </summary>\n        protected FrameworkElement? ContentControl;\n\n        private double _minHeight = 30;\n        private double _minWidth = 30;\n\n        #endregion\n\n        static GroupingNode()\n        {\n            DefaultStyleKeyProperty.OverrideMetadata(typeof(GroupingNode), new FrameworkPropertyMetadata(typeof(GroupingNode)));\n            ActualSizeProperty.Changed.AddClassHandler<GroupingNode>(OnActualSizeChanged);\n            ZIndexProperty.OverrideMetadata<GroupingNode>(new StyledPropertyMetadata<int>(-1));\n            ZIndexProperty.Changed.AddClassHandler<GroupingNode>(OnZIndexPropertyChanged);\n        }\n\n        private static void OnZIndexPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)\n        {\n            var node = (GroupingNode)d;\n            if (node.Container != null)\n            {\n                node.Container.SetCurrentValue(ZIndexProperty, e.NewValue);\n            }\n        }\n\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"GroupingNode\"/> class.\n        /// </summary>\n        public GroupingNode()\n        {\n            AddHandler(Thumb.DragDeltaEvent, OnResize);\n            AddHandler(Thumb.DragCompletedEvent, OnResizeCompleted);\n            AddHandler(Thumb.DragStartedEvent, OnResizeStarted);\n\n            Loaded += OnNodeLoaded;\n            Unloaded += OnNodeUnloaded;\n        }\n\n        private void OnNodeLoaded(object? sender, RoutedEventArgs e)\n        {\n            if (HeaderControl != null)\n            {\n                HeaderControl.PointerPressed += OnHeaderMouseDown;\n                HeaderControl.SizeChanged += OnHeaderSizeChanged;\n                CalculateDesiredHeaderSize();\n            }\n        }\n\n        private void OnNodeUnloaded(object? sender, RoutedEventArgs e)\n        {\n            if (HeaderControl != null)\n            {\n                HeaderControl.PointerPressed -= OnHeaderMouseDown;\n                HeaderControl.SizeChanged -= OnHeaderSizeChanged;\n            }\n        }\n\n        private void OnHeaderMouseDown(object? sender, PointerPressedEventArgs e)\n        {\n            EditorGestures.ItemContainerGestures gestures = EditorGestures.Mappings.ItemContainer;\n            if (Container != null && Editor != null && gestures.Drag.Matches(e.Source, e))\n            {\n                // Switch the default movement mode if necessary\n                var prevMovementMode = MovementMode;\n                if (e.KeyModifiers == EditorGestures.Mappings.GroupingNode.SwitchMovementMode)\n                {\n                    SetCurrentValue(MovementModeProperty, MovementMode == GroupingMovementMode.Group ? GroupingMovementMode.Self : GroupingMovementMode.Group);\n                }\n\n                // Select the content and move with it\n                if (gestures.Selection.Append.Matches(e.Source, e))\n                {\n                    Editor.SelectArea(new Rect(Container.Location, Bounds.Size /* RenderSize */), append: true, fit: true);\n                }\n                else if (gestures.Selection.Remove.Matches(e.Source, e))\n                {\n                    Editor.UnselectArea(new Rect(Container.Location, Bounds.Size /* RenderSize */), fit: true);\n                }\n                else if (gestures.Selection.Invert.Matches(e.Source, e))\n                {\n                    if (Container.IsSelected)\n                    {\n                        Editor.UnselectArea(new Rect(Container.Location, Bounds.Size /* RenderSize */), fit: true);\n                        Container.IsSelected = true;\n                    }\n                    else\n                    {\n                        Editor.SelectArea(new Rect(Container.Location, Bounds.Size /* RenderSize */), append: true, fit: true);\n                    }\n                }\n                else if (gestures.Selection.Replace.Matches(e.Source, e) || EditorGestures.Mappings.ItemContainer.Drag.Matches(e.Source, e))\n                {\n                    Editor.SelectArea(new Rect(Container.Location, Bounds.Size /* RenderSize */), append: Container.IsSelected, fit: true);\n                }\n\n                // Deselect content\n                if (MovementMode == GroupingMovementMode.Self)\n                {\n                    Editor.UnselectArea(new Rect(Container.Location, Bounds.Size /* RenderSize */), fit: true);\n                    Container.IsSelected = true;\n                }\n\n                // Switch the default movement mode back\n                SetCurrentValue(MovementModeProperty, prevMovementMode);\n            }\n        }\n\n        /// <inheritdoc />\n        protected override void OnApplyTemplate(TemplateAppliedEventArgs e)\n        {\n            base.OnApplyTemplate(e);\n\n            ResizeThumb = e.NameScope.Find<Control>(ElementResizeThumb);\n            HeaderControl = e.NameScope.Find<Control>(ElementHeader);\n            ContentControl = e.NameScope.Find<Control>(ElementContent);\n\n            Container = this.GetParentOfType<ItemContainer>();\n            Editor = Container?.Editor ?? this.GetParentOfType<NodifyEditor>();\n\n            if (Container != null)\n            {\n                Container.SetCurrentValue(ZIndexProperty, this.GetValue(ZIndexProperty));\n            }\n        }\n\n        private void OnResize(object? sender, VectorEventArgs e)\n        {\n            if (CanResize && ReferenceEquals(e.Source, ResizeThumb))\n            {\n                double resultWidth = Bounds.Width + e.Vector.X;\n                double resultHeight = Bounds.Height + e.Vector.Y;\n\n                // Snap to grid\n                if (Editor != null)\n                {\n                    uint cellSize = Editor.GridCellSize;\n                    resultWidth = (int)resultWidth / cellSize * cellSize;\n                    resultHeight = (int)resultHeight / cellSize * cellSize;\n                }\n\n                Width = Math.Max(_minWidth, resultWidth);\n                Height = Math.Max(_minHeight, resultHeight);\n\n                e.Handled = true;\n            }\n        }\n\n        private void OnResizeStarted(object? sender, VectorEventArgs e)\n        {\n            SetCurrentValue(ActualSizeProperty, Bounds.Size);\n            var args = new ResizeEventArgs(ActualSize, ActualSize)\n            {\n                RoutedEvent = ResizeStartedEvent,\n                Source = this\n            };\n\n            RaiseEvent(args);\n\n            // Raise ResizeStartedCommand if ResizeStartedEvent event is not handled\n            if (!args.Handled && (ResizeStartedCommand?.CanExecute(ActualSize) ?? false))\n            {\n                ResizeStartedCommand.Execute(ActualSize);\n            }\n        }\n\n        private void OnResizeCompleted(object? sender, VectorEventArgs e)\n        {\n            Size previousSize = ActualSize;\n            var newSize = Bounds.Size;\n            SetCurrentValue(ActualSizeProperty, newSize);\n\n            var args = new ResizeEventArgs(previousSize, newSize)\n            {\n                RoutedEvent = ResizeCompletedEvent,\n                Source = this\n            };\n\n            RaiseEvent(args);\n\n            // Raise ResizeCompletedCommand if ResizeCompletedEvent event is not handled\n            if (!args.Handled && (ResizeCompletedCommand?.CanExecute(newSize) ?? false))\n            {\n                ResizeCompletedCommand.Execute(newSize);\n            }\n        }\n\n        private void OnHeaderSizeChanged(object? sender, SizeChangedEventArgs e)\n            => CalculateDesiredHeaderSize();\n\n        private void CalculateDesiredHeaderSize()\n        {\n            if (HeaderControl != null && ResizeThumb != null)\n            {\n                _minHeight = Math.Max(HeaderControl.Bounds.Height + ResizeThumb.Bounds.Height, MinHeight);\n                _minWidth = Math.Max(ResizeThumb.Bounds.Width, MinWidth);\n\n                // If there's content don't resize it\n                if (ContentControl != null)\n                {\n                    _minWidth = Math.Max(_minWidth, ContentControl.DesiredSize.Width);\n                    _minHeight = Math.Max(_minHeight, _minHeight + ContentControl.DesiredSize.Height);\n                }\n            }\n\n            // Allow selecting only by the header\n            if (Container != null)\n            {\n                Container.DesiredSizeForSelection = new Size(Bounds.Width, Math.Max(HeaderControl?.Bounds.Height ?? _minHeight, MinHeight));\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "Nodify/Nodes/KnotNode.cs",
    "content": "﻿using System.Windows;\nusing System.Windows.Controls;\n\nnamespace Nodify\n{\n    /// <summary>\n    /// Represents a control that owns a <see cref=\"Connector\"/>.\n    /// </summary>\n    public class KnotNode : ContentControl\n    {\n        static KnotNode()\n        {\n            DefaultStyleKeyProperty.OverrideMetadata(typeof(KnotNode), new FrameworkPropertyMetadata(typeof(KnotNode)));\n        }\n    }\n}\n"
  },
  {
    "path": "Nodify/Nodes/Node.Avalonia.cs",
    "content": "using Avalonia;\nusing Avalonia.Controls;\n\nnamespace Nodify\n{\n    public partial class Node\n    {\n        protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)\n        {\n            base.OnPropertyChanged(change);\n            if (change.Property == HeaderProperty)\n            {\n                PseudoClasses.Set(\":has-header\", change.NewValue != null);\n            }\n        }\n    }\n}"
  },
  {
    "path": "Nodify/Nodes/Node.cs",
    "content": "﻿using System.Collections;\nusing System.Collections.ObjectModel;\nusing System.Collections.Specialized;\nusing System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Media;\n\nnamespace Nodify\n{\n    /// <summary>\n    /// Represents a control that has a list of <see cref=\"Input\"/> <see cref=\"Connector\"/>s and a list of <see cref=\"Output\"/> <see cref=\"Connector\"/>s.\n    /// </summary>\n    [TemplatePart(Name = ElementInputItemsControl, Type = typeof(ItemsControl))]\n    [TemplatePart(Name = ElementOutputItemsControl, Type = typeof(ItemsControl))]\n    public partial class Node : HeaderedContentControl\n    {\n        protected const string ElementInputItemsControl = \"PART_Input\";\n        protected const string ElementOutputItemsControl = \"PART_Output\";\n\n        #region Dependency Properties\n\n        public static readonly StyledProperty<IBrush> ContentBrushProperty = AvaloniaProperty.Register<Node, IBrush>(nameof(ContentBrush));\n        public static readonly StyledProperty<IBrush> HeaderBrushProperty = AvaloniaProperty.Register<Node, IBrush>(nameof(HeaderBrush));\n        public static readonly StyledProperty<IBrush> FooterBrushProperty = AvaloniaProperty.Register<Node, IBrush>(nameof(FooterBrush));\n        public static readonly StyledProperty<object> FooterProperty = AvaloniaProperty.Register<Node, object>(nameof(Footer));\n        public static readonly StyledProperty<DataTemplate> FooterTemplateProperty = AvaloniaProperty.Register<Node, DataTemplate>(nameof(FooterTemplate));\n        public static readonly StyledProperty<DataTemplate> InputConnectorTemplateProperty = AvaloniaProperty.Register<Node, DataTemplate>(nameof(InputConnectorTemplate));\n        public static readonly DirectProperty<Node, bool> HasFooterProperty = AvaloniaProperty.RegisterDirect<Node, bool>(nameof(HasFooter), x => x.HasFooter);\n        public static readonly StyledProperty<DataTemplate> OutputConnectorTemplateProperty = AvaloniaProperty.Register<Node, DataTemplate>(nameof(OutputConnectorTemplate));\n        public static readonly StyledProperty<IEnumerable> InputProperty = AvaloniaProperty.Register<Node, IEnumerable>(nameof(Input));\n        public static readonly StyledProperty<IEnumerable> OutputProperty = AvaloniaProperty.Register<Node, IEnumerable>(nameof(Output));\n        public static readonly StyledProperty<ControlTheme> ContentContainerStyleProperty = AvaloniaProperty.Register<Node, ControlTheme>(nameof(ContentContainerStyle));\n        public static readonly StyledProperty<ControlTheme> HeaderContainerStyleProperty = AvaloniaProperty.Register<Node, ControlTheme>(nameof(HeaderContainerStyle));\n        public static readonly StyledProperty<ControlTheme> FooterContainerStyleProperty = AvaloniaProperty.Register<Node, ControlTheme>(nameof(FooterContainerStyle));\n\n        /// <summary>\n        /// Gets or sets the brush used for the background of the <see cref=\"ContentControl.Content\"/> of this <see cref=\"Node\"/>.\n        /// </summary>\n        public IBrush ContentBrush\n        {\n            get => (IBrush)GetValue(ContentBrushProperty);\n            set => SetValue(ContentBrushProperty, value);\n        }\n\n        /// <summary>\n        /// Gets or sets the brush used for the background of the <see cref=\"HeaderedContentControl.Header\"/> of this <see cref=\"Node\"/>.\n        /// </summary>\n        public IBrush HeaderBrush\n        {\n            get => (IBrush)GetValue(HeaderBrushProperty);\n            set => SetValue(HeaderBrushProperty, value);\n        }\n\n        /// <summary>\n        /// Gets or sets the brush used for the background of the <see cref=\"Node.Footer\"/> of this <see cref=\"Node\"/>.\n        /// </summary>\n        public IBrush FooterBrush\n        {\n            get => (IBrush)GetValue(FooterBrushProperty);\n            set => SetValue(FooterBrushProperty, value);\n        }\n\n        /// <summary>\n        /// Gets or sets the data for the footer of this control.\n        /// </summary>\n        public object Footer\n        {\n            get => GetValue(FooterProperty);\n            set => SetValue(FooterProperty, value);\n        }\n\n        /// <summary>\n        /// Gets or sets the template used to display the content of the control's footer.\n        /// </summary>\n        public DataTemplate FooterTemplate\n        {\n            get => (DataTemplate)GetValue(FooterTemplateProperty);\n            set => SetValue(FooterTemplateProperty, value);\n        }\n\n        /// <summary>\n        /// Gets or sets the template used to display the content of the control's <see cref=\"Input\"/> connectors.\n        /// </summary>\n        public DataTemplate InputConnectorTemplate\n        {\n            get => (DataTemplate)GetValue(InputConnectorTemplateProperty);\n            set => SetValue(InputConnectorTemplateProperty, value);\n        }\n\n        /// <summary>\n        /// Gets or sets the template used to display the content of the control's <see cref=\"Output\"/> connectors.\n        /// </summary>\n        public DataTemplate OutputConnectorTemplate\n        {\n            get => (DataTemplate)GetValue(OutputConnectorTemplateProperty);\n            set => SetValue(OutputConnectorTemplateProperty, value);\n        }\n\n        /// <summary>\n        /// Gets or sets the data for the input <see cref=\"Connector\"/>s of this control.\n        /// </summary>\n        public IEnumerable Input\n        {\n            get => (IEnumerable)GetValue(InputProperty);\n            set => SetValue(InputProperty, value);\n        }\n\n        /// <summary>\n        /// Gets or sets the data for the output <see cref=\"Connector\"/>s of this control.\n        /// </summary>\n        public IEnumerable Output\n        {\n            get => (IEnumerable)GetValue(OutputProperty);\n            set => SetValue(OutputProperty, value);\n        }\n\n        /// <summary>\n        /// Gets or sets the style for the content container.\n        /// </summary>\n        public ControlTheme ContentContainerStyle\n        {\n            get => GetValue(ContentContainerStyleProperty);\n            set => SetValue(ContentContainerStyleProperty, value);\n        }\n\n        /// <summary>\n        /// Gets or sets the style for the header container.\n        /// </summary>\n        public ControlTheme HeaderContainerStyle\n        {\n            get => GetValue(HeaderContainerStyleProperty);\n            set => SetValue(HeaderContainerStyleProperty, value);\n        }\n\n        /// <summary>\n        /// Gets or sets the style for the footer container.\n        /// </summary>\n        public ControlTheme FooterContainerStyle\n        {\n            get => GetValue(FooterContainerStyleProperty);\n            set => SetValue(FooterContainerStyleProperty, value);\n        }\n\n        /// <summary>\n        /// Gets a value that indicates whether the <see cref=\"Footer\"/> is <see langword=\"null\" />.\n        /// </summary>\n        public bool HasFooter => Footer != null;\n\n        private static void OnFooterChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)\n        {\n            Node node = (Node)d;\n            node.RaisePropertyChanged(HasFooterProperty, e.OldValue != null, e.NewValue != null);\n        }\n\n        #endregion\n\n        /// <inheritdoc cref=\"ItemsControl.GroupStyle\"/>\n        public ObservableCollection<GroupStyle> InputGroupStyle { get; } = new ObservableCollection<GroupStyle>();\n        /// <inheritdoc cref=\"ItemsControl.GroupStyle\"/>\n        public ObservableCollection<GroupStyle> OutputGroupStyle { get; } = new ObservableCollection<GroupStyle>();\n\n        protected ItemsControl? InputItemsControl { get; private set; }\n        protected ItemsControl? OutputItemsControl { get; private set; }\n\n        static Node()\n        {\n            DefaultStyleKeyProperty.OverrideMetadata(typeof(Node), new FrameworkPropertyMetadata(typeof(Node)));\n            FooterProperty.Changed.AddClassHandler<Node>(OnFooterChanged);\n        }\n\n        public Node()\n        {\n            InputGroupStyle.CollectionChanged += OnInputGroupStyleCollectionChanged;\n            OutputGroupStyle.CollectionChanged += OnOutputGroupStyleCollectionChanged;\n        }\n\n        protected override void OnApplyTemplate(TemplateAppliedEventArgs e)\n        {\n            base.OnApplyTemplate(e);\n\n            InputItemsControl = e.NameScope.Get<ItemsControl>(ElementInputItemsControl);\n            OutputItemsControl = e.NameScope.Get<ItemsControl>(ElementOutputItemsControl);\n\n            if (InputItemsControl != null)\n            {\n                foreach (var style in InputGroupStyle)\n                {\n                    // Not Supported in Avalonia\n                    // InputItemsControl.GroupStyle.Add(style);\n                }\n            }\n\n            if (OutputItemsControl != null)\n            {\n                foreach (var style in OutputGroupStyle)\n                {\n                    // Not Supported in Avalonia\n                    // OutputItemsControl.GroupStyle.Add(style);\n                }\n            }\n        }\n\n        private void OnInputGroupStyleCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)\n        {\n            if (InputItemsControl != null)\n            {\n                // Not Supported in Avalonia\n                // SynchronizeCollection(InputItemsControl.GroupStyle, e);\n            }\n        }\n\n        private void OnOutputGroupStyleCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)\n        {\n            if (OutputItemsControl != null)\n            {\n                // Not Supported in Avalonia\n                // SynchronizeCollection(OutputItemsControl.GroupStyle, e);\n            }\n        }\n\n        private static void SynchronizeCollection(ObservableCollection<GroupStyle> collection, NotifyCollectionChangedEventArgs e)\n        {\n            switch (e.Action)\n            {\n                case NotifyCollectionChangedAction.Add:\n                    if (e.NewItems != null)\n                    {\n                        for (int i = 0; i < e.NewItems.Count; i++)\n                        {\n                            var item = (GroupStyle)e.NewItems[i]!;\n                            collection.Add(item);\n                        }\n                    }\n                    break;\n                case NotifyCollectionChangedAction.Remove:\n                    if (e.OldItems != null)\n                    {\n                        for (int i = 0; i < e.OldItems.Count; i++)\n                        {\n                            var item = (GroupStyle)e.OldItems[i]!;\n                            collection.Remove(item);\n                        }\n                    }\n                    break;\n                case NotifyCollectionChangedAction.Replace:\n                    collection[e.NewStartingIndex] = (GroupStyle)e.NewItems![0]!;\n                    break;\n                case NotifyCollectionChangedAction.Move:\n                    collection.Move(e.OldStartingIndex, e.NewStartingIndex);\n                    break;\n                case NotifyCollectionChangedAction.Reset:\n                    collection.Clear();\n                    break;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "Nodify/Nodes/NodeInput.Avalonia.cs",
    "content": "namespace Nodify;\n\npublic partial class NodeInput\n{\n    /// <inheritdoc />\n    public NodeInput()\n    {\n        UpdatePseudoClasses();\n    }\n\n    /// <inheritdoc />\n    protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)\n    {\n        base.OnPropertyChanged(change);\n        if (change.Property == OrientationProperty)\n            UpdatePseudoClasses();\n    }\n\n    private void UpdatePseudoClasses()\n    {\n        PseudoClasses.Set(\"vertial\", Orientation == Orientation.Vertical);\n    }\n}"
  },
  {
    "path": "Nodify/Nodes/NodeInput.cs",
    "content": "﻿using System.Windows;\nusing System.Windows.Controls;\n\nnamespace Nodify\n{\n    /// <summary>\n    /// Represents the default control for the <see cref=\"Node.InputConnectorTemplate\"/>.\n    /// </summary>\n    public partial class NodeInput : Connector\n    {\n        #region Dependency Properties\n\n        public static readonly StyledProperty<object?> HeaderProperty = HeaderedContentControl.HeaderProperty.AddOwner<NodeInput>();\n        public static readonly StyledProperty<IDataTemplate?> HeaderTemplateProperty = HeaderedContentControl.HeaderTemplateProperty.AddOwner<NodeInput>();\n        public static readonly StyledProperty<ControlTemplate> ConnectorTemplateProperty = AvaloniaProperty.Register<NodeInput, ControlTemplate>(nameof(ConnectorTemplate));\n        public static readonly StyledProperty<Orientation> OrientationProperty = StackPanel.OrientationProperty.AddOwner<NodeInput>();\n\n        /// <summary>\n        /// Gets of sets the data used for the control's header.\n        /// </summary>\n        public object Header\n        {\n            get => GetValue(HeaderProperty);\n            set => SetValue(HeaderProperty, value);\n        }\n\n        /// <summary>\n        /// Gets or sets the template used to display the content of the control's header.\n        /// </summary>\n        public IDataTemplate? HeaderTemplate\n        {\n            get => GetValue(HeaderTemplateProperty);\n            set => SetValue(HeaderTemplateProperty, value);\n        }\n\n        /// <summary>\n        /// Gets or sets the template used to display the connecting point of this <see cref=\"Connector\"/>.\n        /// </summary>\n        public ControlTemplate ConnectorTemplate\n        {\n            get => (ControlTemplate)GetValue(ConnectorTemplateProperty);\n            set => SetValue(ConnectorTemplateProperty, value);\n        }\n\n        /// <inheritdoc cref=\"StackPanel.Orientation\" />\n        public Orientation Orientation\n        {\n            get => (Orientation)GetValue(OrientationProperty);\n            set => SetValue(OrientationProperty, value);\n        }\n\n        #endregion\n\n        static NodeInput()\n        {\n            DefaultStyleKeyProperty.OverrideMetadata(typeof(NodeInput), new FrameworkPropertyMetadata(typeof(NodeInput)));\n            AffectsMeasure<NodeInput>(OrientationProperty);\n            OrientationProperty.OverrideDefaultValue<NodeInput>(Orientation.Horizontal);\n        }\n    }\n}\n"
  },
  {
    "path": "Nodify/Nodes/NodeOutput.Avalonia.cs",
    "content": "namespace Nodify;\n\npublic partial class NodeOutput\n{\n    /// <inheritdoc />\n    public NodeOutput()\n    {\n        UpdatePseudoClasses();\n    }\n\n    /// <inheritdoc />\n    protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)\n    {\n        base.OnPropertyChanged(change);\n        if (change.Property == OrientationProperty)\n            UpdatePseudoClasses();\n    }\n\n    private void UpdatePseudoClasses()\n    {\n        PseudoClasses.Set(\"vertial\", Orientation == Orientation.Vertical);\n    }\n}"
  },
  {
    "path": "Nodify/Nodes/NodeOutput.cs",
    "content": "﻿using System.Windows;\nusing System.Windows.Controls;\n\nnamespace Nodify\n{\n    /// <summary>\n    /// Represents the default control for the <see cref=\"Node.OutputConnectorTemplate\"/>.\n    /// </summary>\n    public partial class NodeOutput : Connector\n    {\n        #region Dependency Properties\n        \n        public static readonly StyledProperty<object?> HeaderProperty = HeaderedContentControl.HeaderProperty.AddOwner<NodeOutput>();\n        public static readonly StyledProperty<IDataTemplate?> HeaderTemplateProperty = HeaderedContentControl.HeaderTemplateProperty.AddOwner<NodeOutput>();\n        public static readonly StyledProperty<ControlTemplate> ConnectorTemplateProperty = NodeInput.ConnectorTemplateProperty.AddOwner<NodeOutput>();\n        public static readonly StyledProperty<Orientation> OrientationProperty = StackPanel.OrientationProperty.AddOwner<NodeOutput>();\n\n        /// <summary>\n        /// Gets of sets the data used for the control's header.\n        /// </summary>\n        public object Header\n        {\n            get => GetValue(HeaderProperty);\n            set => SetValue(HeaderProperty, value);\n        }\n        \n        /// <summary>\n        /// Gets or sets the template used to display the content of the control's header.\n        /// </summary>\n        public IDataTemplate? HeaderTemplate\n        {\n            get => GetValue(HeaderTemplateProperty);\n            set => SetValue(HeaderTemplateProperty, value);\n        }\n        \n        /// <summary>\n        /// Gets or sets the template used to display the connecting point of this <see cref=\"Connector\"/>.\n        /// </summary>\n        public ControlTemplate ConnectorTemplate\n        {\n            get => (ControlTemplate)GetValue(ConnectorTemplateProperty);\n            set => SetValue(ConnectorTemplateProperty, value);\n        }\n\n        /// <inheritdoc cref=\"StackPanel.Orientation\" />\n        public Orientation Orientation\n        {\n            get => (Orientation)GetValue(OrientationProperty);\n            set => SetValue(OrientationProperty, value);\n        }\n\n        #endregion\n\n        static NodeOutput()\n        {\n            DefaultStyleKeyProperty.OverrideMetadata(typeof(NodeOutput), new FrameworkPropertyMetadata(typeof(NodeOutput)));\n            AffectsMeasure<NodeOutput>(OrientationProperty);\n            OrientationProperty.OverrideDefaultValue<NodeOutput>(Orientation.Horizontal);\n        }\n    }\n}\n"
  },
  {
    "path": "Nodify/Nodes/StateNode.cs",
    "content": "﻿using System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Input;\nusing System.Windows.Media;\n\nnamespace Nodify\n{\n    /// <summary>\n    /// Represents a control that acts as a <see cref=\"Connector\"/>.\n    /// </summary>\n    [TemplatePart(Name = ElementContent, Type = typeof(UIElement))]\n    public class StateNode : Connector\n    {\n        protected const string ElementContent = \"PART_Content\";\n\n        #region Dependency Properties\n\n        public static readonly StyledProperty<IBrush> HighlightBrushProperty = ItemContainer.HighlightBrushProperty.AddOwner<StateNode>();\n        public static readonly StyledProperty<object?> ContentProperty = ContentPresenter.ContentProperty.AddOwner<StateNode>();\n        public static readonly StyledProperty<IDataTemplate?> ContentTemplateProperty = ContentPresenter.ContentTemplateProperty.AddOwner<StateNode>();\n\n        /// <summary>\n        /// Gets or sets the brush used when the <see cref=\"PendingConnection.IsOverElementProperty\"/> attached property is true for this <see cref=\"StateNode\"/>.\n        /// </summary>\n        public IBrush HighlightBrush\n        {\n            get => (IBrush)GetValue(HighlightBrushProperty);\n            set => SetValue(HighlightBrushProperty, value);\n        }\n\n        /// <summary>\n        /// Gets or sets the data for the control's content.\n        /// </summary>\n        public object Content\n        {\n            get => GetValue(ContentProperty);\n            set => SetValue(ContentProperty, value);\n        }\n\n        /// <summary>\n        /// Gets or sets the template used to display the content of the control's header.\n        /// </summary>\n        public IDataTemplate? ContentTemplate\n        {\n            get => GetValue(ContentTemplateProperty);\n            set => SetValue(ContentTemplateProperty, value);\n        }\n        \n        #endregion\n        \n        /// <summary>\n        /// Gets the <see cref=\"ContentControl\"/> control of this <see cref=\"StateNode\"/>.\n        /// </summary>\n        protected UIElement? ContentControl { get; private set; }\n\n        static StateNode()\n        {\n            DefaultStyleKeyProperty.OverrideMetadata(typeof(StateNode), new FrameworkPropertyMetadata(typeof(StateNode)));\n        }\n\n        /// <inheritdoc />\n        protected override void OnApplyTemplate(TemplateAppliedEventArgs e)\n        {\n            base.OnApplyTemplate(e);\n\n            ContentControl = e.NameScope.Find<Control>(ElementContent);\n        }\n\n        /// <inheritdoc />\n        protected override void OnMouseDown(MouseButtonEventArgs e)\n        {\n            // Do not raise PendingConnection events if clicked on content\n            if (e.Source is Visual visual && (!ContentControl?.IsAncestorOf(visual) ?? true))\n            {\n                base.OnMouseDown(e);\n            }\n        }\n\n        /// <inheritdoc />\n        protected override void OnMouseUp(MouseButtonEventArgs e)\n        {\n            // Do not raise PendingConnection events if clicked on content\n            if (e.Source is Visual visual && (!ContentControl?.IsAncestorOf(visual) ?? true))\n            {\n                base.OnMouseUp(e);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "Nodify/Nodify.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <TargetFrameworks>netstandard2.0</TargetFrameworks>\n    <Nullable>enable</Nullable>\n    <PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance>\n    <Authors>miroiu, bandysc</Authors>\n    <Company />\n    <PackageId>NodifyAvalonia</PackageId>\n    <Product>NodifyAvalonia</Product>\n    <Description>The core controls for a node based editor (designed for MVVM) for Avalonia</Description>\n    <Copyright>Miroiu Emanuel</Copyright>\n    <PackageLicenseExpression>MIT</PackageLicenseExpression>\n    <PackageProjectUrl>https://github.com/BAndysc/nodify-avalonia</PackageProjectUrl>\n    <PackageIcon>icon.png</PackageIcon>\n    <RepositoryUrl>https://github.com/miroiu/nodify</RepositoryUrl>\n    <PackageTags>wpf mvvm node network node-editor graph controls</PackageTags>\n    <Version>6.6.0</Version>\n    <PackageReleaseNotes>\n      > - Features:\n      >\t  - Added InputGroupStyle and OutputGroupStyle to Node\n      >\t  - Added PanWithMouseWheel, PanHorizontalModifierKey and PanVerticalModifierKey to EditorGestures.Editor\n      >\t  - Added CornerRadius dependency property to LineConnection, CircuitConnection and StepConnection\n      >\t  - Added EditorGestures.Editor.PushItems gesture used to start pushing ItemContainers vertically or horizontally\n      >\t  - Added PushedAreaStyle, PushedAreaOrientation and IsPushingItems dependency properties to NodifyEditor\n      >\t  - Added NodifyEditor.SnapToGrid utility function\n      > - Bugfixes:\n      >\t  - Fixed ItemContainer.BorderBrush and ItemContainer.SelectedBrush not reacting to theme changes\n    </PackageReleaseNotes>\n    <AssemblyOriginatorKeyFile>..\\build\\Nodify.snk</AssemblyOriginatorKeyFile>\n    <PackageReadmeFile>README.md</PackageReadmeFile>\n    <AvaloniaUseCompiledBindingsByDefault>true</AvaloniaUseCompiledBindingsByDefault>\n    <WarningsAsErrors>$(WarningsAsErrors),AVP1000, AVP1001, AVP1002, AVP1010, AVP1011, AVP1012, AVP1013, AVP1020, AVP1021, AVP1022, AVP1030, AVP1031, AVP1032, AVP1040, AVA2001</WarningsAsErrors>\n  </PropertyGroup>\n  <PropertyGroup>\n    <LangVersion>12.0</LangVersion>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)'=='Release'\">\n    <GeneratePackageOnBuild>true</GeneratePackageOnBuild>\n    <SignAssembly>true</SignAssembly>\n    <GenerateDocumentationFile>true</GenerateDocumentationFile>\n  </PropertyGroup>\n  <ItemGroup>\n    <None Include=\"..\\icon.png\">\n      <Pack>True</Pack>\n      <PackagePath></PackagePath>\n    </None>\n    <None Include=\"..\\README.md\">\n      <Pack>True</Pack>\n      <PackagePath>\\</PackagePath>\n    </None>\n  </ItemGroup>\n  <ItemGroup>\n    <PackageReference Include=\"Avalonia\" Version=\"$(AvaloniaVersion)\" />\n  </ItemGroup>\n  <ItemGroup>\n    <None Remove=\"Themes\\*.xaml\" />\n    <AvaloniaResource Include=\"Themes\\*.xaml\" />\n    <None Remove=\"Themes\\Styles\\*.xaml\" />\n    <AvaloniaResource Include=\"Themes\\Styles\\*.xaml\" />\n  </ItemGroup>\n  \n</Project>\n"
  },
  {
    "path": "Nodify/Nodify.csproj.DotSettings",
    "content": "﻿<wpf:ResourceDictionary xml:space=\"preserve\" xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" xmlns:s=\"clr-namespace:System;assembly=mscorlib\" xmlns:ss=\"urn:shemas-jetbrains-com:settings-storage-xaml\" xmlns:wpf=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\">\n\t<s:Boolean x:Key=\"/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=connections/@EntryIndexedValue\">True</s:Boolean>\n\t<s:Boolean x:Key=\"/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=converters/@EntryIndexedValue\">True</s:Boolean>\n\t<s:Boolean x:Key=\"/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=events/@EntryIndexedValue\">True</s:Boolean>\n\t<s:Boolean x:Key=\"/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=helpers/@EntryIndexedValue\">True</s:Boolean>\n\t<s:Boolean x:Key=\"/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=nodes/@EntryIndexedValue\">True</s:Boolean></wpf:ResourceDictionary>"
  },
  {
    "path": "Nodify/NodifyCanvas.cs",
    "content": "﻿using System.Windows;\nusing System.Windows.Controls;\n\nnamespace Nodify\n{\n    /// <summary>Interface for items inside a <see cref=\"NodifyCanvas\"/>.</summary>\n    public interface INodifyCanvasItem\n    {\n        /// <summary>The location of the item.</summary>\n        Point Location { get; }\n\n        /// <summary>The desired size of the item.</summary>\n        Size DesiredSize { get; }\n\n        /// <inheritdoc cref=\"UIElement.Arrange(Rect)\" />\n        void Arrange(Rect rect);\n    }\n\n    /// <summary>A canvas like panel that works with <see cref=\"INodifyCanvasItem\"/>s.</summary>\n    public class NodifyCanvas : Panel\n    {\n        public static readonly StyledProperty<Rect> ExtentProperty = AvaloniaProperty.Register<NodifyCanvas, Rect>(nameof(Extent), BoxValue.Rect);\n\n        /// <summary>The area covered by the children of this panel.</summary>\n        public Rect Extent\n        {\n            get => (Rect)GetValue(ExtentProperty);\n            set => SetValue(ExtentProperty, value);\n        }\n\n        /// <inheritdoc />\n        protected override Size ArrangeOverride(Size arrangeSize)\n        {\n            double minX = double.MaxValue;\n            double minY = double.MaxValue;\n\n            double maxX = double.MinValue;\n            double maxY = double.MinValue;\n\n            UIElementCollection children = Children;\n            for (int i = 0; i < children.Count; i++)\n            {\n                var item = (INodifyCanvasItem)children[i];\n                item.Arrange(new Rect(item.Location, item.DesiredSize));\n\n                Size size = children[i].DesiredSize;\n\n                if (item.Location.X < minX)\n                {\n                    minX = item.Location.X;\n                }\n\n                if (item.Location.Y < minY)\n                {\n                    minY = item.Location.Y;\n                }\n\n                double sizeX = item.Location.X + size.Width;\n                if (sizeX > maxX)\n                {\n                    maxX = sizeX;\n                }\n\n                double sizeY = item.Location.Y + size.Height;\n                if (sizeY > maxY)\n                {\n                    maxY = sizeY;\n                }\n            }\n\n            SetCurrentValue(ExtentProperty, minX == double.MaxValue\n                ? new Rect(0, 0, 0, 0)\n                : new Rect(minX, minY, maxX - minX, maxY - minY));\n\n            return arrangeSize;\n        }\n\n        /// <inheritdoc />\n        protected override Size MeasureOverride(Size constraint)\n        {\n            var availableSize = new Size(double.PositiveInfinity, double.PositiveInfinity);\n            UIElementCollection children = Children;\n\n            for (int i = 0; i < children.Count; i++)\n            {\n                children[i].Measure(availableSize);\n            }\n\n            return default;\n        }\n\n        static NodifyCanvas()\n        {\n            AffectsParentArrange<NodifyCanvas>(DecoratorContainer.LocationProperty);\n            AffectsParentArrange<NodifyCanvas>(DecoratorContainer.ActualSizeProperty);\n        }\n    }\n}\n"
  },
  {
    "path": "Nodify/NodifyEditor.Avalonia.cs",
    "content": "namespace Nodify;\n\npublic partial class NodifyEditor\n{\n    private Point viewportLocation;\n\n    private CancellationTokenSource? bringToViewToken;\n\n    protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)\n    {\n        base.OnPropertyChanged(change);\n        if (change.Property == DisplayConnectionsOnTopProperty)\n            PseudoClasses.Set(\":connections-on-top\", DisplayConnectionsOnTop);\n    }\n\n    private bool inOnSelectedItemsChanged;\n\n    /// <summary>\n    /// On WPF single mouse wheel delta is 120, on Avalonia it is 0.6 (at least on macOS)\n    /// </summary>\n    private static double MouseWheelAvaloniaToWpfScale => 120 / 0.6;\n    \n    private double PointerTouchGestureMagnifyScale => 4;\n\n    private void OnPointerTouchPadGestureMagnify(object? sender, PointerDeltaEventArgs e)\n    {\n        State.HandleMouseWheel(new MouseWheelEventArgs());\n\n        if (!e.Handled && EditorGestures.Mappings.Editor.ZoomModifierKey == e.KeyModifiers)\n        {\n            double zoom = Math.Pow(2.0, e.Delta.Y / 3.0 / MouseWheelDeltaForOneLine * MouseWheelAvaloniaToWpfScale * PointerTouchGestureMagnifyScale);\n            ZoomAtPosition(zoom, e.GetPosition(ItemsHost));\n\n            // Handle it for nested editors\n            if (e.Source is NodifyEditor)\n            {\n                e.Handled = true;\n            }\n        }\n    }\n\n    private void OnSourceReset(object sender, EventArgs e)\n    {\n        SelectedItems?.Clear();\n    }\n\n    private void OnPreviewPointerPressed(object sender, PointerPressedEventArgs e)\n    {\n        // Avalonia, contrary to WPF, automatically captures the pointer when pressed\n        // this interferes with Nodify behaviour, so here's the workaround:\n        // OnPreviewPointerPressed is a tunneled handler, meaning it is called BEFORE any other mouse handler\n        // basically we release the pointer capture as soon as it is captured, so that later it can be manually captured\n        // in correct places\n\n        // However, the above interferes with Thumb behaviour, which does not capture the pointer in OnPointerPressed,\n        // because it assumes the pointer is captured automatically. RIP\n        if (e.Source is Thumb || e.Source is Control sourceControl && sourceControl.FindAncestorOfType<Thumb>() != null)\n            return;\n\n        // this check is required for StickyConnections to work\n        // when IsMouseCaptureWithin is true, then the pointer is actually captured by an explicit call to Capture\n        // so we don't wanna interrupt it\n        if (!IsMouseCaptureWithin)\n            e.Pointer.Capture(null);\n    }\n\n    protected override void OnDataContextEndUpdate()\n    {\n        // Required to synchronize SelectedItems with the DataContext\n        base.OnDataContextEndUpdate();\n        Selection.Clear();\n        if (SelectedItems != null && SelectedItems.Count > 0)\n        {\n            for (var i = 0; i < SelectedItems.Count; i++)\n            {\n                Selection.Select(Items.IndexOf(SelectedItems[i]));\n            }\n        }\n    }\n\n    #region Scrollable\n\n    public Size Extent => new Size(_extentWidth, _extentHeight);\n\n    public Size Viewport => ViewportSize;\n\n    public Vector Offset\n    {\n        get => new Vector(_horizontalOffset, _verticalOffset);\n        set\n        {\n            _horizontalOffset = value.X;\n            _verticalOffset = value.Y;\n            UpdateViewportLocationOnScroll();\n            // merged SetHorizontalOffset and SetVerticalOffset\n        }\n    }\n\n    #endregion\n\n    protected override Type StyleKeyOverride => typeof(NodifyEditor);\n\n    public bool BringIntoView(Control target, Rect targetRect) => false;\n\n    public Control? GetControlInDirection(NavigationDirection direction, Control? from) => null;\n\n    public void RaiseScrollInvalidated(EventArgs e) => ScrollInvalidated?.Invoke(this, e);\n\n    public bool IsLogicalScrollEnabled => true;\n    public Size ScrollSize => new Size(ScrollIncrement / ViewportZoom, ScrollIncrement / ViewportZoom);\n    public Size PageScrollSize => ViewportSize;\n    public event EventHandler? ScrollInvalidated;\n}"
  },
  {
    "path": "Nodify/NodifyEditor.PushingItems.cs",
    "content": "﻿using System.Diagnostics;\nusing System.Windows;\nusing System;\nusing System.Windows.Controls;\nusing System.Windows.Shapes;\n\nnamespace Nodify\n{\n    [StyleTypedProperty(Property = nameof(PushedAreaStyle), StyleTargetType = typeof(Rectangle))]\n    public partial class NodifyEditor\n    {\n        public static readonly StyledProperty<ControlTheme> PushedAreaStyleProperty = AvaloniaProperty.Register<NodifyEditor, ControlTheme>(nameof(PushedAreaStyle));\n\n        public static readonly DirectProperty<NodifyEditor, Rect> PushedAreaProperty = AvaloniaProperty.RegisterDirect<NodifyEditor, Rect>(nameof(PushedArea), x => x.PushedArea);\n\n        public static readonly DirectProperty<NodifyEditor, bool> IsPushingItemsProperty = AvaloniaProperty.RegisterDirect<NodifyEditor, bool>(nameof(IsPushingItems), x => x.IsPushingItems);\n\n        public static readonly DirectProperty<NodifyEditor, Orientation> PushedAreaOrientationProperty = AvaloniaProperty.RegisterDirect<NodifyEditor, Orientation>(nameof(PushedAreaOrientation), x => x.PushedAreaOrientation);\n\n        private static void OnIsPushingItemsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)\n        {\n            var editor = (NodifyEditor)d;\n\n            if ((bool)e.NewValue == true)\n            {\n                editor.OnItemsPushStarted();\n            }\n            else\n            {\n                editor.OnItemsPushCompleted();\n            }\n        }\n\n        private void OnItemsPushCompleted()\n        {\n            if (ItemsDragCompletedCommand?.CanExecute(DataContext) ?? false)\n                ItemsDragCompletedCommand.Execute(DataContext);\n        }\n\n        private void OnItemsPushStarted()\n        {\n            if (ItemsDragStartedCommand?.CanExecute(DataContext) ?? false)\n                ItemsDragStartedCommand.Execute(DataContext);\n        }\n\n        private Rect pushedArea;\n        /// <summary>\n        /// Gets the currently pushed area while <see cref=\"IsPushingItems\"/> is true.\n        /// </summary>\n        public Rect PushedArea\n        {\n            get => pushedArea;\n            private set => SetAndRaise(PushedAreaProperty, ref pushedArea, value);\n        }\n\n        private bool isPushingItems;\n        /// <summary>\n        /// Gets a value that indicates whether a pushing operation is in progress.\n        /// </summary>\n        public bool IsPushingItems\n        {\n            get => isPushingItems;\n            private set => SetAndRaise(IsPushingItemsProperty, ref isPushingItems, value);\n        }\n\n        private Orientation pushedAreaOrientation;\n        /// <summary>\n        /// Gets the orientation of the <see cref=\"PushedArea\"/>.\n        /// </summary>\n        public Orientation PushedAreaOrientation\n        {\n            get => pushedAreaOrientation;\n            private set => SetAndRaise(PushedAreaOrientationProperty, ref pushedAreaOrientation, value);\n        }\n\n        /// <summary>\n        /// Gets or sets the style to use for the pushed area.\n        /// </summary>\n        public ControlTheme PushedAreaStyle\n        {\n            get => GetValue(PushedAreaStyleProperty);\n            set => SetValue(PushedAreaStyleProperty, value);\n        }\n\n        /// <summary>\n        /// Gets or sets whether push items cancellation is allowed (see <see cref=\"EditorGestures.NodifyEditorGestures.CancelAction\"/>).\n        /// </summary>\n        public static bool AllowPushItemsCancellation { get; set; } = true;\n\n        private IPushStrategy? _pushStrategy;\n\n        /// <summary>\n        /// Starts the pushing items operation at the specified location with the specified orientation.\n        /// </summary>\n        /// <param name=\"location\">The starting location for pushing items, in graph space coordinates.</param>\n        /// <param name=\"orientation\">The orientation of the <see cref=\"PushedArea\"/>.</param>\n        protected internal void StartPushingItems(Point location, Orientation orientation)\n        {\n            Debug.Assert(!IsPushingItems);\n\n            IsPushingItems = true;\n            PushedAreaOrientation = orientation;\n\n            _pushStrategy = CreatePushStrategy(orientation);\n\n            PushedArea = _pushStrategy.Start(location);\n        }\n\n        /// <summary>\n        /// Cancels the current pushing operation and reverts the <see cref=\"PushedArea\"/> to its initial state.\n        /// </summary>\n        /// <exception cref=\"InvalidOperationException\">Thrown if pushing item cancellation is not allowed (see <see cref=\"AllowPushItemsCancellation\"/>).</exception>\n        protected internal void CancelPushingItems()\n        {\n            if (!AllowPushItemsCancellation)\n                throw new InvalidOperationException(\"Push items cancellation is not allowed\");\n\n            Debug.Assert(IsPushingItems);\n            if (IsPushingItems)\n            {\n                PushedArea = _pushStrategy!.Cancel();\n                IsPushingItems = false;\n            }\n        }\n\n        /// <summary>\n        /// Updates the pushed area based on the specified amount.\n        /// </summary>\n        /// <param name=\"offset\">The amount to adjust the pushed area by.</param>\n        /// <remarks>\n        /// This method adjusts the pushed area incrementally. It should only be called while a pushing operation is active (see <see cref=\"StartPushingItems(Point, Orientation)\"/>).\n        /// </remarks>\n        protected internal void PushItems(Vector amount)\n        {\n            Debug.Assert(IsPushingItems);\n            PushedArea = _pushStrategy!.Push(amount);\n        }\n\n        /// <summary>\n        /// Ends the current pushing operation and finalizes the pushed area state.\n        /// </summary>\n        protected internal void EndPushingItems()\n        {\n            Debug.Assert(IsPushingItems);\n            if (IsPushingItems)\n            {\n                PushedArea = _pushStrategy!.End();\n                _pushStrategy = null;\n                IsPushingItems = false;\n            }\n        }\n\n        private void UpdatePushedArea()\n        {\n            if (IsPushingItems)\n            {\n                PushedArea = _pushStrategy!.OnViewportChanged();\n            }\n        }\n\n        private IPushStrategy CreatePushStrategy(Orientation orientation)\n        {\n            if (orientation == Orientation.Horizontal)\n            {\n                return new HorizontalPushStrategy(this);\n            }\n\n            return new VerticalPushStrategy(this);\n        }\n    }\n}\n"
  },
  {
    "path": "Nodify/NodifyEditor.Scrolling.cs",
    "content": "﻿using System.Windows.Controls;\nusing System.Windows;\nusing System;\nusing System.Windows.Controls.Primitives;\nusing System.Windows.Media;\nusing System.Windows.Input;\n\nnamespace Nodify\n{\n    public partial class NodifyEditor : IScrollInfo\n    {\n        /// <summary>\n        /// The number of units the mouse wheel is rotated to scroll one line.\n        /// </summary>\n        public static double ScrollIncrement { get; set; } = MouseWheelDeltaForOneLine / 2;\n\n        bool ILogicalScrollable.CanHorizontallyScroll { get; set; }\n        bool ILogicalScrollable.CanVerticallyScroll { get; set; }\n\n        private double _extentWidth;\n        double IScrollInfo.ExtentWidth => _extentWidth;\n\n        private double _extentHeight;\n        double IScrollInfo.ExtentHeight => _extentHeight;\n\n        private double _horizontalOffset;\n        double IScrollInfo.HorizontalOffset => _horizontalOffset;\n\n        private double _verticalOffset;\n        double IScrollInfo.VerticalOffset => _verticalOffset;\n\n        double IScrollInfo.ViewportWidth => ViewportSize.Width;\n        double IScrollInfo.ViewportHeight => ViewportSize.Height;\n\n        //ScrollViewer? IScrollInfo.ScrollOwner { get; set; }\n\n        private IScrollInfo ScrollInfo => this;\n        private Point? _viewportLocationBeforeScrolling;\n        private bool _isScrolling;\n\n        void IScrollInfo.LineUp()\n            => ViewportLocation -= new Vector(0, ScrollIncrement / ViewportZoom);\n\n        void IScrollInfo.LineDown()\n            => ViewportLocation += new Vector(0, ScrollIncrement / ViewportZoom);\n\n        void IScrollInfo.LineLeft()\n            => ViewportLocation -= new Vector(ScrollIncrement / ViewportZoom, 0);\n\n        void IScrollInfo.LineRight()\n            => ViewportLocation += new Vector(ScrollIncrement / ViewportZoom, 0);\n\n        void IScrollInfo.MouseWheelUp() => ScrollInfo.LineUp();\n        void IScrollInfo.MouseWheelDown() => ScrollInfo.LineDown();\n        void IScrollInfo.MouseWheelLeft() => ScrollInfo.LineLeft();\n        void IScrollInfo.MouseWheelRight() => ScrollInfo.LineRight();\n\n        void IScrollInfo.PageUp()\n            => ViewportLocation = new Point(ViewportLocation.X, ViewportLocation.Y - ViewportSize.Height);\n\n        void IScrollInfo.PageDown()\n            => ViewportLocation = new Point(ViewportLocation.X, ViewportLocation.Y + ViewportSize.Height);\n\n        void IScrollInfo.PageLeft()\n            => ViewportLocation = new Point(ViewportLocation.X - ViewportSize.Width, ViewportLocation.Y);\n\n        void IScrollInfo.PageRight()\n            => ViewportLocation = new Point(ViewportLocation.X + ViewportSize.Width, ViewportLocation.Y);\n\n        Rect IScrollInfo.MakeVisible(Visual visual, Rect rectangle)\n        {\n            // This is called when clicking on an item container. Uncomment to automatically scroll to the selected item container.\n            //if (visual is ItemContainer container)\n            //{\n            //    var containerBounds = new Rect(container.Location, container.RenderSize);\n            //    if (!new Rect(ViewportLocation, ViewportSize).Contains(containerBounds))\n            //    {\n            //        BringIntoView(containerBounds);\n            //        return containerBounds;\n            //    }\n            //}\n\n            return rectangle;\n        }\n\n        void IScrollInfo.SetHorizontalOffset(double offset)\n        {\n            _horizontalOffset = offset;\n            UpdateViewportLocationOnScroll();\n        }\n\n        void IScrollInfo.SetVerticalOffset(double offset)\n        {\n            _verticalOffset = offset;\n            UpdateViewportLocationOnScroll();\n        }\n\n        private void UpdateViewportLocationOnScroll()\n        {\n            if (!_viewportLocationBeforeScrolling.HasValue)\n            {\n                _viewportLocationBeforeScrolling = ViewportLocation;\n            }\n\n            _isScrolling = true;\n\n            double locationX = Math.Min(ItemsExtent.Left, _viewportLocationBeforeScrolling.Value.X) + ScrollInfo.HorizontalOffset;\n            double locationY = Math.Min(ItemsExtent.Top, _viewportLocationBeforeScrolling.Value.Y) + ScrollInfo.VerticalOffset;\n            ViewportLocation = new Point(locationX, locationY);\n\n            ScrollInfo.RaiseScrollInvalidated(EventArgs.Empty);\n            _isScrolling = false;\n        }\n\n        private void UpdateScrollbars()\n        {\n            // setting the ViewportLocation when manually scrolling triggers the ViewportUpdatedEvent which in turn calls this method, hence the !_isScrolling check\n            if (!_isScrolling)\n            {\n                _viewportLocationBeforeScrolling = null;\n\n                var extent = ItemsExtent;\n                extent = extent.Union(new Rect(ViewportLocation, ViewportSize));\n\n                _extentHeight = extent.Height;\n                _extentWidth = extent.Width;\n\n                var scrollOffset = ViewportLocation - ItemsExtent.Position;\n\n                _horizontalOffset = Math.Max(0, scrollOffset.X);\n                _verticalOffset = Math.Max(0, scrollOffset.Y);\n\n                ScrollInfo.RaiseScrollInvalidated(EventArgs.Empty);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "Nodify/NodifyEditor.cs",
    "content": "﻿using System;\nusing System.Collections;\nusing System.Collections.Generic;\nusing System.Collections.Specialized;\nusing System.ComponentModel;\nusing System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Controls.Primitives;\nusing System.Windows.Input;\nusing System.Windows.Markup;\nusing System.Windows.Media;\nusing System.Windows.Shapes;\nusing System.Windows.Threading;\n\nnamespace Nodify\n{\n    /// <summary>\n    /// Groups <see cref=\"ItemContainer\"/>s and <see cref=\"Connection\"/>s in an area that you can drag, zoom and select.\n    /// </summary>\n    [TemplatePart(Name = ElementItemsHost, Type = typeof(Panel))]\n    [TemplatePart(Name = ElementConnectionsHost, Type = typeof(FrameworkElement))]\n    [StyleTypedProperty(Property = nameof(ItemContainerTheme), StyleTargetType = typeof(ItemContainer))]\n    [StyleTypedProperty(Property = nameof(DecoratorContainerStyle), StyleTargetType = typeof(DecoratorContainer))]\n    [StyleTypedProperty(Property = nameof(SelectionRectangleStyle), StyleTargetType = typeof(Rectangle))]\n    [StyleTypedProperty(Property = nameof(CuttingLineStyle), StyleTargetType = typeof(CuttingLine))]\n    [ContentProperty(nameof(Decorators))]\n    [DefaultProperty(nameof(Decorators))]\n    public partial class NodifyEditor : MultiSelector\n    {\n        protected const string ElementItemsHost = \"PART_ItemsHost\";\n        protected const string ElementConnectionsHost = \"PART_ConnectionsHost\";\n\n        #region Viewport\n\n        public static readonly StyledProperty<double> ViewportZoomProperty = AvaloniaProperty.Register<NodifyEditor, double>(nameof(ViewportZoom), BoxValue.Double1, defaultBindingMode: BindingMode.TwoWay, coerce: ConstrainViewportZoomToRange);\n        public static readonly StyledProperty<double> MinViewportZoomProperty = AvaloniaProperty.Register<NodifyEditor, double>(nameof(MinViewportZoom), 0.1d, coerce: CoerceMinViewportZoom);\n        public static readonly StyledProperty<double> MaxViewportZoomProperty = AvaloniaProperty.Register<NodifyEditor, double>(nameof(MaxViewportZoom), BoxValue.Double2, coerce: CoerceMaxViewportZoom);\n        public static readonly DirectProperty<NodifyEditor, Point> ViewportLocationProperty = AvaloniaProperty.RegisterDirect<NodifyEditor, Point>(nameof(ViewportLocation), e => e.ViewportLocation, (e, v) => e.ViewportLocation = v, BoxValue.Point, defaultBindingMode: BindingMode.TwoWay);\n        public static readonly StyledProperty<Size> ViewportSizeProperty = AvaloniaProperty.Register<NodifyEditor, Size>(nameof(ViewportSize), BoxValue.Size);\n        public static readonly StyledProperty<Rect> ItemsExtentProperty = AvaloniaProperty.Register<NodifyEditor, Rect>(nameof(ItemsExtent), BoxValue.Rect);\n        public static readonly StyledProperty<Rect> DecoratorsExtentProperty = AvaloniaProperty.Register<NodifyEditor, Rect>(nameof(DecoratorsExtent), BoxValue.Rect);\n        public static readonly DirectProperty<NodifyEditor, bool> IsMouseCaptureWithinProperty = AvaloniaProperty.RegisterDirect<NodifyEditor, bool>(nameof(IsMouseCaptureWithin), x => x.IsMouseCaptureWithin);\n        \n        public static readonly StyledProperty<Transform> ViewportTransformProperty = AvaloniaProperty.Register<NodifyEditor, Transform>(nameof(ViewportTransform), new TransformGroup());\n        /// https://github.com/AvaloniaUI/Avalonia/issues/11959 workaround\n        public static readonly StyledProperty<Transform> DpiScaledViewportTransformProperty = AvaloniaProperty.Register<NodifyEditor, Transform>(nameof(DpiScaledViewportTransform), new TransformGroup());\n \n        #region Callbacks\n\n        private static void UpdateViewportTransform(NodifyEditor editor)\n        {\n            var transform = new TransformGroup();\n            transform.Children.Add(editor.ScaleTransform);\n            transform.Children.Add(editor.TranslateTransform);\n\n            editor.SetCurrentValue(ViewportTransformProperty, transform);\n            \n            var dpiScaledTransform = new TransformGroup();\n            dpiScaledTransform.Children.Add(editor.ScaleTransform);\n            dpiScaledTransform.Children.Add(editor.DpiScaledTranslateTransform);\n            \n            editor.SetCurrentValue(DpiScaledViewportTransformProperty, dpiScaledTransform);\n        }\n\n        private static void OnItemsExtentChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)\n        {\n            var editor = (NodifyEditor)d;\n            editor.UpdateScrollbars();\n        }\n\n        private static void OnViewportLocationChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)\n        {\n            var editor = (NodifyEditor)d;\n            var translate = (Point)e.NewValue;\n\n            // once https://github.com/AvaloniaUI/Avalonia/issues/15097 is fixed, dont't create new instances\n            editor.TranslateTransform = new();\n            editor.DpiScaledTranslateTransform = new();\n\n            editor.TranslateTransform.X = -translate.X * editor.ViewportZoom;\n            editor.TranslateTransform.Y = -translate.Y * editor.ViewportZoom;\n\n            var renderScale = (editor.GetVisualRoot()?.RenderScaling ?? 1);\n            editor.DpiScaledTranslateTransform.X = editor.TranslateTransform.X * renderScale;\n            editor.DpiScaledTranslateTransform.Y = editor.TranslateTransform.Y * renderScale;\n\n            editor.OnViewportUpdated();\n            UpdateViewportTransform(editor); // won't be required once https://github.com/AvaloniaUI/Avalonia/issues/15097 if fixed\n        }\n\n        private static void OnViewportZoomChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)\n        {\n            var editor = (NodifyEditor)d;\n            double zoom = (double)e.NewValue;\n\n            // once https://github.com/AvaloniaUI/Avalonia/issues/15097 is fixed, dont't create new instance\n            editor.ScaleTransform = new();\n            \n            editor.ScaleTransform.ScaleX = zoom;\n            editor.ScaleTransform.ScaleY = zoom;\n\n            editor.ViewportSize = new Size(editor.Bounds.Width / zoom, editor.Bounds.Height / zoom);\n            UpdateViewportTransform(editor); // won't be required once https://github.com/AvaloniaUI/Avalonia/issues/15097 if fixed\n\n            editor.ApplyRenderingOptimizations();\n            editor.OnViewportUpdated();\n        }\n\n        private static void OnMinViewportZoomChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)\n        {\n            var zoom = (NodifyEditor)d;\n            zoom.CoerceValue(MaxViewportZoomProperty);\n            zoom.CoerceValue(ViewportZoomProperty);\n        }\n\n        private static double CoerceMinViewportZoom(DependencyObject d, double value)\n            => (double)value > 0.1d ? value : 0.1d;\n\n        private static void OnMaxViewportZoomChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)\n        {\n            var zoom = (NodifyEditor)d;\n            zoom.CoerceValue(ViewportZoomProperty);\n        }\n\n        private static double CoerceMaxViewportZoom(DependencyObject d, double value)\n        {\n            var editor = (NodifyEditor)d;\n            double min = editor.MinViewportZoom;\n\n            return (double)value < min ? min : value;\n        }\n\n        private static double ConstrainViewportZoomToRange(DependencyObject d, double value)\n        {\n            var editor = (NodifyEditor)d;\n\n            var num = (double)value;\n            double minimum = editor.MinViewportZoom;\n            if (num < minimum)\n            {\n                return minimum;\n            }\n\n            double maximum = editor.MaxViewportZoom;\n            return num > maximum ? maximum : value;\n        }\n        #endregion\n\n        #region Routed Events\n\n        public static readonly RoutedEvent<RoutedEventArgs> ViewportUpdatedEvent = RoutedEvent.Register<RoutedEventArgs>(nameof(ViewportUpdated), RoutingStrategies.Bubble, typeof(NodifyEditor));\n\n        /// <summary>\n        /// Occurs whenever the viewport updates.\n        /// </summary>\n        public event RoutedEventHandler ViewportUpdated\n        {\n            add => AddHandler(ViewportUpdatedEvent, value);\n            remove => RemoveHandler(ViewportUpdatedEvent, value);\n        }\n\n        /// <summary>\n        /// Updates the <see cref=\"ViewportSize\"/> and raises the <see cref=\"ViewportUpdatedEvent\"/>.\n        /// Called when the <see cref=\"UIElement.RenderSize\"/> or <see cref=\"ViewportZoom\"/> is changed.\n        /// </summary>\n        protected void OnViewportUpdated()\n        {\n            UpdateScrollbars();\n            UpdatePushedArea();\n            RaiseEvent(new RoutedEventArgs(ViewportUpdatedEvent, this));\n        }\n\n        #endregion\n\n        #region Properties\n\n        /// <summary>\n        /// Gets the transform used to offset the viewport.\n        /// </summary>\n        protected TranslateTransform TranslateTransform = new TranslateTransform();\n        \n        /// https://github.com/AvaloniaUI/Avalonia/issues/11959 workaround\n        protected TranslateTransform DpiScaledTranslateTransform = new TranslateTransform();\n\n        /// <summary>\n        /// Gets the transform used to zoom on the viewport.\n        /// </summary>\n        protected ScaleTransform ScaleTransform = new ScaleTransform();\n\n        /// <summary>\n        /// Gets the transform that is applied to all child controls.\n        /// </summary>\n        public Transform ViewportTransform\n        {\n            get { return (Transform)GetValue(ViewportTransformProperty); }\n            set { SetValue(ViewportTransformProperty, value); }\n        }\n        \n        /// https://github.com/AvaloniaUI/Avalonia/issues/11959 workaround\n        /// <summary>\n        /// Gets the transform that is applied to all child controls.\n        /// </summary>\n        public Transform DpiScaledViewportTransform\n        {\n            get { return (Transform)GetValue(DpiScaledViewportTransformProperty); }\n            set { SetValue(DpiScaledViewportTransformProperty, value); }\n        }\n\n        /// <summary>\n        /// Gets the size of the viewport in graph space (scaled by the <see cref=\"ViewportZoom\"/>).\n        /// </summary>\n        public Size ViewportSize\n        {\n            get => (Size)GetValue(ViewportSizeProperty);\n            set => SetValue(ViewportSizeProperty, value);\n        }\n\n        /// <summary>\n        /// Gets or sets the viewport's top-left coordinates in graph space coordinates.\n        /// </summary>\n        public Point ViewportLocation\n        {\n            get => viewportLocation;\n            set => SetAndRaise(ViewportLocationProperty, ref viewportLocation, value);\n        }\n\n        /// <summary>\n        /// Gets or sets the zoom factor of the viewport.\n        /// </summary>\n        public double ViewportZoom\n        {\n            get => (double)GetValue(ViewportZoomProperty);\n            set => SetValue(ViewportZoomProperty, value);\n        }\n\n        /// <summary>\n        /// Gets or sets the minimum zoom factor of the viewport\n        /// </summary>\n        public double MinViewportZoom\n        {\n            get => (double)GetValue(MinViewportZoomProperty);\n            set => SetValue(MinViewportZoomProperty, value);\n        }\n\n        /// <summary>\n        /// Gets or sets the maximum zoom factor of the viewport\n        /// </summary>\n        public double MaxViewportZoom\n        {\n            get => (double)GetValue(MaxViewportZoomProperty);\n            set => SetValue(MaxViewportZoomProperty, value);\n        }\n\n        /// <summary>\n        /// The area covered by the <see cref=\"ItemContainer\"/>s.\n        /// </summary>\n        public Rect ItemsExtent\n        {\n            get => (Rect)GetValue(ItemsExtentProperty);\n            set => SetValue(ItemsExtentProperty, value);\n        }\n\n        /// <summary>\n        /// The area covered by the <see cref=\"DecoratorContainer\"/>s.\n        /// </summary>\n        public Rect DecoratorsExtent\n        {\n            get => (Rect)GetValue(DecoratorsExtentProperty);\n            set => SetValue(DecoratorsExtentProperty, value);\n        }\n\n        private bool isMouseCaptureWithin;\n        /// <summary>\n        /// Gets a value that indicates whether the mouse is captured to the <see cref=\"NodifyEditor\"/>.\n        /// </summary>\n        public bool IsMouseCaptureWithin\n        {\n            get => isMouseCaptureWithin;\n            internal set => SetAndRaise(IsMouseCaptureWithinProperty, ref isMouseCaptureWithin, value);\n        }\n\n        #endregion\n\n        private void ApplyRenderingOptimizations()\n        {\n            if (ItemsHost != null)\n            {\n                if (EnableRenderingContainersOptimizations && Items.Count >= OptimizeRenderingMinimumContainers)\n                {\n                    double zoom = ViewportZoom;\n                    double availableZoomIn = 1.0 - MinViewportZoom;\n                    bool shouldCache = zoom / availableZoomIn <= OptimizeRenderingZoomOutPercent;\n                    //ItemsHost.CacheMode = shouldCache ? new BitmapCache(1.0 / zoom) : null;\n                }\n                else\n                {\n                    //ItemsHost.CacheMode = null;\n                }\n            }\n        }\n\n        #endregion\n\n        #region Cosmetic Dependency Properties\n\n        public static readonly StyledProperty<double> BringIntoViewSpeedProperty = AvaloniaProperty.Register<NodifyEditor, double>(nameof(BringIntoViewSpeed), BoxValue.Double1000);\n        public static readonly StyledProperty<double> BringIntoViewMaxDurationProperty = AvaloniaProperty.Register<NodifyEditor, double>(nameof(BringIntoViewMaxDuration), BoxValue.Double1);\n        public static readonly StyledProperty<bool> DisplayConnectionsOnTopProperty = AvaloniaProperty.Register<NodifyEditor, bool>(nameof(DisplayConnectionsOnTop), BoxValue.False);\n        public static readonly StyledProperty<bool> DisableAutoPanningProperty = AvaloniaProperty.Register<NodifyEditor, bool>(nameof(DisableAutoPanning), BoxValue.False);\n        public static readonly StyledProperty<double> AutoPanSpeedProperty = AvaloniaProperty.Register<NodifyEditor, double>(nameof(AutoPanSpeed), 15d);\n        public static readonly StyledProperty<double> AutoPanEdgeDistanceProperty = AvaloniaProperty.Register<NodifyEditor, double>(nameof(AutoPanEdgeDistance), 15d);\n        public static readonly StyledProperty<DataTemplate> ConnectionTemplateProperty = AvaloniaProperty.Register<NodifyEditor, DataTemplate>(nameof(ConnectionTemplate));\n        public static readonly StyledProperty<DataTemplate> DecoratorTemplateProperty = AvaloniaProperty.Register<NodifyEditor, DataTemplate>(nameof(DecoratorTemplate));\n        public static readonly StyledProperty<DataTemplate> PendingConnectionTemplateProperty = AvaloniaProperty.Register<NodifyEditor, DataTemplate>(nameof(PendingConnectionTemplate));\n        public static readonly StyledProperty<ControlTheme> SelectionRectangleStyleProperty = AvaloniaProperty.Register<NodifyEditor, ControlTheme>(nameof(SelectionRectangleStyle));\n        public static readonly StyledProperty<ControlTheme> CuttingLineStyleProperty = AvaloniaProperty.Register<NodifyEditor, ControlTheme>(nameof(CuttingLineStyle));\n        public static readonly StyledProperty<ControlTheme> DecoratorContainerStyleProperty = AvaloniaProperty.Register<NodifyEditor, ControlTheme>(nameof(DecoratorContainerStyle));\n\n        private static void OnDisableAutoPanningChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)\n            => ((NodifyEditor)d).OnDisableAutoPanningChanged((bool)e.NewValue);\n\n        /// <summary>\n        /// Gets or sets the maximum animation duration in seconds for bringing a location into view.\n        /// </summary>\n        public double BringIntoViewMaxDuration\n        {\n            get => (double)GetValue(BringIntoViewMaxDurationProperty);\n            set => SetValue(BringIntoViewMaxDurationProperty, value);\n        }\n\n        /// <summary>\n        /// Gets or sets the animation speed in pixels per second for bringing a location into view.\n        /// </summary>\n        /// <remarks>Total animation duration is calculated based on distance and clamped between 0.1 and <see cref=\"BringIntoViewMaxDuration\"/>.</remarks>\n        public double BringIntoViewSpeed\n        {\n            get => (double)GetValue(BringIntoViewSpeedProperty);\n            set => SetValue(BringIntoViewSpeedProperty, value);\n        }\n\n        /// <summary>\n        /// Gets or sets whether to display connections on top of <see cref=\"ItemContainer\"/>s or not.\n        /// </summary>\n        public bool DisplayConnectionsOnTop\n        {\n            get => (bool)GetValue(DisplayConnectionsOnTopProperty);\n            set => SetValue(DisplayConnectionsOnTopProperty, value);\n        }\n\n        /// <summary>\n        /// Gets or sets whether to disable the auto panning when selecting or dragging near the edge of the editor configured by <see cref=\"AutoPanEdgeDistance\"/>.\n        /// </summary>\n        public bool DisableAutoPanning\n        {\n            get => (bool)GetValue(DisableAutoPanningProperty);\n            set => SetValue(DisableAutoPanningProperty, value);\n        }\n\n        /// <summary>\n        /// Gets or sets the speed used when auto-panning scaled by <see cref=\"AutoPanningTickRate\"/>\n        /// </summary>\n        public double AutoPanSpeed\n        {\n            get => (double)GetValue(AutoPanSpeedProperty);\n            set => SetValue(AutoPanSpeedProperty, value);\n        }\n\n        /// <summary>\n        /// Gets or sets the maximum distance in pixels from the edge of the editor that will trigger auto-panning.\n        /// </summary>\n        public double AutoPanEdgeDistance\n        {\n            get => (double)GetValue(AutoPanEdgeDistanceProperty);\n            set => SetValue(AutoPanEdgeDistanceProperty, value);\n        }\n\n        /// <summary>\n        /// Gets or sets the <see cref=\"DataTemplate\"/> to use when generating a new <see cref=\"BaseConnection\"/>.\n        /// </summary>\n        public DataTemplate ConnectionTemplate\n        {\n            get => (DataTemplate)GetValue(ConnectionTemplateProperty);\n            set => SetValue(ConnectionTemplateProperty, value);\n        }\n\n        /// <summary>\n        /// Gets or sets the <see cref=\"DataTemplate\"/> to use when generating a new <see cref=\"DecoratorContainer\"/>.\n        /// </summary>\n        public DataTemplate DecoratorTemplate\n        {\n            get => (DataTemplate)GetValue(DecoratorTemplateProperty);\n            set => SetValue(DecoratorTemplateProperty, value);\n        }\n\n        /// <summary>\n        /// Gets or sets the <see cref=\"DataTemplate\"/> to use for the <see cref=\"PendingConnection\"/>.\n        /// </summary>\n        public DataTemplate PendingConnectionTemplate\n        {\n            get => (DataTemplate)GetValue(PendingConnectionTemplateProperty);\n            set => SetValue(PendingConnectionTemplateProperty, value);\n        }\n\n        /// <summary>\n        /// Gets or sets the style to use for the selection rectangle.\n        /// </summary>\n        public ControlTheme SelectionRectangleStyle\n        {\n            get => (ControlTheme)GetValue(SelectionRectangleStyleProperty);\n            set => SetValue(SelectionRectangleStyleProperty, value);\n        }\n\n        /// <summary>\n        /// Gets or sets the style to use for the cutting line.\n        /// </summary>\n        public ControlTheme CuttingLineStyle\n        {\n            get => (ControlTheme)GetValue(CuttingLineStyleProperty);\n            set => SetValue(CuttingLineStyleProperty, value);\n        }\n\n        /// <summary>\n        /// Gets or sets the style to use for the <see cref=\"DecoratorContainer\"/>.\n        /// </summary>\n        public ControlTheme DecoratorContainerStyle\n        {\n            get => (ControlTheme)GetValue(DecoratorContainerStyleProperty);\n            set => SetValue(DecoratorContainerStyleProperty, value);\n        }\n\n        #endregion\n\n        #region Readonly Dependency Properties\n\n        public static readonly DirectProperty<NodifyEditor, Rect> SelectedAreaProperty = AvaloniaProperty.RegisterDirect<NodifyEditor, Rect>(nameof(SelectedArea), x => x.SelectedArea);\n\n        public static readonly DirectProperty<NodifyEditor, bool> IsSelectingProperty = AvaloniaProperty.RegisterDirect<NodifyEditor, bool>(nameof(IsSelecting), x => x.IsSelecting);\n\n        public static readonly DirectProperty<NodifyEditor, Point> CuttingLineStartProperty = AvaloniaProperty.RegisterDirect<NodifyEditor, Point>(nameof(CuttingLineStart), x => x.CuttingLineStart);\n\n        public static readonly DirectProperty<NodifyEditor, Point> CuttingLineEndProperty = AvaloniaProperty.RegisterDirect<NodifyEditor, Point>(nameof(CuttingLineEnd), x => x.CuttingLineEnd);\n\n        public static readonly DirectProperty<NodifyEditor, bool> IsCuttingProperty = AvaloniaProperty.RegisterDirect<NodifyEditor, bool>(nameof(IsCutting), x => x.IsCutting);\n\n        public static readonly DirectProperty<NodifyEditor, bool> IsPanningProperty = AvaloniaProperty.RegisterDirect<NodifyEditor, bool>(nameof(IsPanning), x => x.IsPanning);\n\n        public static readonly DirectProperty<NodifyEditor, Point> MouseLocationProperty = AvaloniaProperty.RegisterDirect<NodifyEditor, Point>(nameof(MouseLocation), x => x.MouseLocation);\n\n        private static void OnIsSelectingChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)\n        {\n            var editor = (NodifyEditor)d;\n            if ((bool)e.NewValue == true)\n                editor.OnItemsSelectStarted();\n            else\n                editor.OnItemsSelectCompleted();\n        }\n\n        private void OnItemsSelectCompleted()\n        {\n            if (ItemsSelectCompletedCommand?.CanExecute(DataContext) ?? false)\n                ItemsSelectCompletedCommand.Execute(DataContext);\n        }\n\n        private void OnItemsSelectStarted()\n        {\n            if (ItemsSelectStartedCommand?.CanExecute(DataContext) ?? false)\n                ItemsSelectStartedCommand.Execute(DataContext);\n        }\n\n        private static void OnIsCuttingChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)\n        {\n            var editor = (NodifyEditor)d;\n            if ((bool)e.NewValue == true)\n                editor.OnCuttingStarted();\n            else\n                editor.OnCuttingCompleted();\n        }\n\n        private void OnCuttingCompleted()\n        {\n            if (CuttingCompletedCommand?.CanExecute(DataContext) ?? false)\n                CuttingCompletedCommand.Execute(DataContext);\n        }\n\n        private void OnCuttingStarted()\n        {\n            if (CuttingStartedCommand?.CanExecute(DataContext) ?? false)\n                CuttingStartedCommand.Execute(DataContext);\n        }\n\n        private Rect selectedArea;\n        /// <summary>\n        /// Gets the currently selected area while <see cref=\"IsSelecting\"/> is true.\n        /// </summary>\n        public Rect SelectedArea\n        {\n            get => selectedArea;\n            internal set => SetAndRaise(SelectedAreaProperty, ref selectedArea, value);\n        }\n\n        private bool isSelecting;\n        /// <summary>\n        /// Gets a value that indicates whether a selection operation is in progress.\n        /// </summary>\n        public bool IsSelecting\n        {\n            get => isSelecting;\n            internal set => SetAndRaise(IsSelectingProperty, ref isSelecting, value);\n        }\n\n        private Point cuttingLineStart;\n        /// <summary>\n        /// Gets the start point of the <see cref=\"CuttingLine\"/> while <see cref=\"IsCutting\"/> is true.\n        /// </summary>\n        public Point CuttingLineStart\n        {\n            get => cuttingLineStart;\n            internal set => SetAndRaise(CuttingLineStartProperty, ref cuttingLineStart, value);\n        }\n\n        private Point cuttingLineEnd;\n        /// <summary>\n        /// Gets the end point of the <see cref=\"CuttingLine\"/> while <see cref=\"IsCutting\"/> is true.\n        /// </summary>\n        public Point CuttingLineEnd\n        {\n            get => cuttingLineEnd;\n            internal set => SetAndRaise(CuttingLineEndProperty, ref cuttingLineEnd, value);\n        }\n\n        private bool isCutting;\n        /// <summary>\n        /// Gets a value that indicates whether a cutting operation is in progress.\n        /// </summary>\n        public bool IsCutting\n        {\n            get => isCutting;\n            internal set => SetAndRaise(IsCuttingProperty, ref isCutting, value);\n        }\n\n        private bool isPanning;\n        /// <summary>\n        /// Gets a value that indicates whether a panning operation is in progress.\n        /// </summary>\n        public bool IsPanning\n        {\n            get => isPanning;\n            protected internal set => SetAndRaise(IsPanningProperty, ref isPanning, value);\n        }\n\n        private Point mouseLocation;\n        /// <summary>\n        /// Gets the current mouse location in graph space coordinates (relative to the <see cref=\"ItemsHost\" />).\n        /// </summary>\n        public Point MouseLocation\n        {\n            get => mouseLocation;\n            protected set => SetAndRaise(MouseLocationProperty, ref mouseLocation, value);\n        }\n\n        /// <summary>\n        /// Gets the current mouse location relative to NotifyEditor\n        /// </summary>\n        private Point RelativeMouseLocation;\n\n        #endregion\n\n        #region Dependency Properties\n\n        public static readonly StyledProperty<IEnumerable> ConnectionsProperty = AvaloniaProperty.Register<NodifyEditor, IEnumerable>(nameof(Connections));\n        public new static readonly StyledProperty<IList> SelectedItemsProperty = AvaloniaProperty.Register<NodifyEditor, IList>(nameof(SelectedItems));\n        public static readonly StyledProperty<IList> SelectedConnectionsProperty = AvaloniaProperty.Register<NodifyEditor, IList>(nameof(SelectedConnections));\n        public static readonly StyledProperty<object> SelectedConnectionProperty = AvaloniaProperty.Register<NodifyEditor, object>(nameof(SelectedConnection), defaultBindingMode: BindingMode.TwoWay);\n        public static readonly StyledProperty<object> PendingConnectionProperty = AvaloniaProperty.Register<NodifyEditor, object>(nameof(PendingConnection));\n        public static readonly StyledProperty<uint> GridCellSizeProperty = AvaloniaProperty.Register<NodifyEditor, uint>(nameof(GridCellSize), BoxValue.UInt1, coerce: OnCoerceGridCellSize);\n        public static readonly StyledProperty<bool> DisableZoomingProperty = AvaloniaProperty.Register<NodifyEditor, bool>(nameof(DisableZooming));\n        public static readonly StyledProperty<bool> DisablePanningProperty = AvaloniaProperty.Register<NodifyEditor, bool>(nameof(DisablePanning));\n        public static readonly StyledProperty<bool> EnableRealtimeSelectionProperty = AvaloniaProperty.Register<NodifyEditor, bool>(nameof(EnableRealtimeSelection));\n        public static readonly StyledProperty<IEnumerable> DecoratorsProperty = AvaloniaProperty.Register<NodifyEditor, IEnumerable>(nameof(Decorators));\n        public static readonly StyledProperty<bool> CanSelectMultipleConnectionsProperty = AvaloniaProperty.Register<NodifyEditor, bool>(nameof(CanSelectMultipleConnections), BoxValue.True);\n        public static readonly StyledProperty<bool> CanSelectMultipleItemsProperty = AvaloniaProperty.Register<NodifyEditor, bool>(nameof(CanSelectMultipleItems), BoxValue.True, coerce: CoerceCanSelectMultipleItems);\n\n        private static void OnCanSelectMultipleItemsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)\n            => ((NodifyEditor)d).CanSelectMultipleItemsBase = (bool)e.NewValue;\n\n        private static bool CoerceCanSelectMultipleItems(DependencyObject d, bool baseValue)\n            => ((NodifyEditor)d).CanSelectMultipleItemsBase = (bool)baseValue;\n\n        private static void OnSelectedItemsSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)\n            => ((NodifyEditor)d).OnSelectedItemsSourceChanged((IList)e.OldValue, (IList)e.NewValue);\n\n        private static uint OnCoerceGridCellSize(DependencyObject d, uint value)\n            => (uint)value > 0u ? value : BoxValue.UInt1;\n\n        private static void OnGridCellSizeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { }\n\n        private static void OnDisablePanningChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)\n        {\n            var editor = (NodifyEditor)d;\n            editor.OnDisableAutoPanningChanged(editor.DisableAutoPanning || editor.DisablePanning);\n        }\n\n        /// <summary>\n        /// Gets or sets the items that will be rendered in the decorators layer via <see cref=\"DecoratorContainer\"/>s.\n        /// </summary>\n        [Content]\n        public IEnumerable Decorators\n        {\n            get => (IEnumerable)GetValue(DecoratorsProperty);\n            set => SetValue(DecoratorsProperty, value);\n        }\n\n        /// <summary>\n        /// Gets or sets the value of an invisible grid used to adjust locations (snapping) of <see cref=\"ItemContainer\"/>s.\n        /// </summary>\n        public uint GridCellSize\n        {\n            get => (uint)GetValue(GridCellSizeProperty);\n            set => SetValue(GridCellSizeProperty, value);\n        }\n\n        /// <summary>\n        /// Gets or sets the data source that <see cref=\"BaseConnection\"/>s will be generated for.\n        /// </summary>\n        public IEnumerable Connections\n        {\n            get => (IEnumerable)GetValue(ConnectionsProperty);\n            set => SetValue(ConnectionsProperty, value);\n        }\n\n        /// <summary>\n        /// Gets of sets the <see cref=\"FrameworkElement.DataContext\"/> of the <see cref=\"Nodify.PendingConnection\"/>.\n        /// </summary>\n        public object PendingConnection\n        {\n            get => GetValue(PendingConnectionProperty);\n            set => SetValue(PendingConnectionProperty, value);\n        }\n\n        /// <summary>\n        /// Gets or sets the selected connection.\n        /// </summary>\n        public object? SelectedConnection\n        {\n            get => GetValue(SelectedConnectionProperty);\n            set => SetValue(SelectedConnectionProperty, value);\n        }\n\n        /// <summary>\n        /// Gets or sets the selected connections in the <see cref=\"NodifyEditor\"/>.\n        /// </summary>\n        public IList? SelectedConnections\n        {\n            get => (IList?)GetValue(SelectedConnectionsProperty);\n            set => SetValue(SelectedConnectionsProperty, value);\n        }\n\n        /// <summary>\n        /// Gets or sets the selected items in the <see cref=\"NodifyEditor\"/>.\n        /// </summary>\n        public new IList? SelectedItems\n        {\n            get => (IList?)GetValue(SelectedItemsProperty);\n            set => SetValue(SelectedItemsProperty, value);\n        }\n\n        /// <summary>\n        /// Gets or sets whether zooming should be disabled.\n        /// </summary>\n        public bool DisableZooming\n        {\n            get => (bool)GetValue(DisableZoomingProperty);\n            set => SetValue(DisableZoomingProperty, value);\n        }\n\n        /// <summary>\n        /// Gets or sets whether panning should be disabled.\n        /// </summary>\n        public bool DisablePanning\n        {\n            get => (bool)GetValue(DisablePanningProperty);\n            set => SetValue(DisablePanningProperty, value);\n        }\n\n        /// <summary>\n        /// Enables selecting and deselecting items while the <see cref=\"SelectedArea\"/> changes.\n        /// Disable for maximum performance when hundreds of items are generated.\n        /// </summary>\n        public bool EnableRealtimeSelection\n        {\n            get => (bool)GetValue(EnableRealtimeSelectionProperty);\n            set => SetValue(EnableRealtimeSelectionProperty, value);\n        }\n\n        /// <summary>\n        /// Gets or sets whether multiple connections can be selected.\n        /// </summary>\n        public bool CanSelectMultipleConnections\n        {\n            get => (bool)GetValue(CanSelectMultipleConnectionsProperty);\n            set => SetValue(CanSelectMultipleConnectionsProperty, value);\n        }\n\n        /// <summary>\n        /// Gets or sets whether multiple <see cref=\"ItemContainer\" />s can be selected.\n        /// </summary>\n        public new bool CanSelectMultipleItems\n        {\n            get => (bool)GetValue(CanSelectMultipleItemsProperty);\n            set => SetValue(CanSelectMultipleItemsProperty, value);\n        }\n\n        private bool CanSelectMultipleItemsBase\n        {\n            get => base.CanSelectMultipleItems;\n            set => base.CanSelectMultipleItems = value;\n        }\n\n        #endregion\n\n        #region Command Dependency Properties\n\n        public static readonly StyledProperty<ICommand> ConnectionCompletedCommandProperty = AvaloniaProperty.Register<NodifyEditor, ICommand>(nameof(ConnectionCompletedCommand));\n        public static readonly StyledProperty<ICommand> ConnectionStartedCommandProperty = AvaloniaProperty.Register<NodifyEditor, ICommand>(nameof(ConnectionStartedCommand));\n        public static readonly StyledProperty<ICommand> DisconnectConnectorCommandProperty = AvaloniaProperty.Register<NodifyEditor, ICommand>(nameof(DisconnectConnectorCommand));\n        public static readonly StyledProperty<ICommand> RemoveConnectionCommandProperty = AvaloniaProperty.Register<NodifyEditor, ICommand>(nameof(RemoveConnectionCommand));\n        public static readonly StyledProperty<ICommand> ItemsDragStartedCommandProperty = AvaloniaProperty.Register<NodifyEditor, ICommand>(nameof(ItemsDragStartedCommand));\n        public static readonly StyledProperty<ICommand> ItemsDragCompletedCommandProperty = AvaloniaProperty.Register<NodifyEditor, ICommand>(nameof(ItemsDragCompletedCommand));\n        public static readonly StyledProperty<ICommand> ItemsSelectStartedCommandProperty = AvaloniaProperty.Register<NodifyEditor, ICommand>(nameof(ItemsSelectStartedCommand));\n        public static readonly StyledProperty<ICommand> ItemsSelectCompletedCommandProperty = AvaloniaProperty.Register<NodifyEditor, ICommand>(nameof(ItemsSelectCompletedCommand));\n        public static readonly StyledProperty<ICommand> CuttingStartedCommandProperty = AvaloniaProperty.Register<NodifyEditor, ICommand>(nameof(CuttingStartedCommand));\n        public static readonly StyledProperty<ICommand> CuttingCompletedCommandProperty = AvaloniaProperty.Register<NodifyEditor, ICommand>(nameof(CuttingCompletedCommand));\n\n        /// <summary>\n        /// Invoked when the <see cref=\"Nodify.PendingConnection\"/> is completed. <br />\n        /// Use <see cref=\"PendingConnection.StartedCommand\"/> if you want to control the visibility of the connection from the viewmodel. <br />\n        /// Parameter is <see cref=\"PendingConnection.Source\"/>.\n        /// </summary>\n        public ICommand? ConnectionStartedCommand\n        {\n            get => (ICommand?)GetValue(ConnectionStartedCommandProperty);\n            set => SetValue(ConnectionStartedCommandProperty, value);\n        }\n\n        /// <summary>\n        /// Invoked when the <see cref=\"Nodify.PendingConnection\"/> is completed. <br />\n        /// Use <see cref=\"PendingConnection.CompletedCommand\"/> if you want to control the visibility of the connection from the viewmodel. <br />\n        /// Parameter is <see cref=\"Tuple{T, U}\"/> where <see cref=\"Tuple{T, U}.Item1\"/> is the <see cref=\"PendingConnection.Source\"/> and <see cref=\"Tuple{T, U}.Item2\"/> is <see cref=\"PendingConnection.Target\"/>.\n        /// </summary>\n        public ICommand? ConnectionCompletedCommand\n        {\n            get => (ICommand?)GetValue(ConnectionCompletedCommandProperty);\n            set => SetValue(ConnectionCompletedCommandProperty, value);\n        }\n\n        /// <summary>\n        /// Invoked when the <see cref=\"Connector.Disconnect\"/> event is raised. <br />\n        /// Can also be handled at the <see cref=\"Connector\"/> level using the <see cref=\"Connector.DisconnectCommand\"/> command. <br />\n        /// Parameter is the <see cref=\"Connector\"/>'s <see cref=\"FrameworkElement.DataContext\"/>.\n        /// </summary>\n        public ICommand? DisconnectConnectorCommand\n        {\n            get => (ICommand?)GetValue(DisconnectConnectorCommandProperty);\n            set => SetValue(DisconnectConnectorCommandProperty, value);\n        }\n\n        /// <summary>\n        /// Invoked when the <see cref=\"BaseConnection.Disconnect\"/> event is raised. <br />\n        /// Can also be handled at the <see cref=\"BaseConnection\"/> level using the <see cref=\"BaseConnection.DisconnectCommand\"/> command. <br />\n        /// Parameter is the <see cref=\"BaseConnection\"/>'s <see cref=\"FrameworkElement.DataContext\"/>.\n        /// </summary>\n        public ICommand? RemoveConnectionCommand\n        {\n            get => (ICommand?)GetValue(RemoveConnectionCommandProperty);\n            set => SetValue(RemoveConnectionCommandProperty, value);\n        }\n\n        /// <summary>\n        /// Invoked when a drag operation starts for the <see cref=\"SelectedItems\"/>.\n        /// </summary>\n        public ICommand? ItemsDragStartedCommand\n        {\n            get => (ICommand?)GetValue(ItemsDragStartedCommandProperty);\n            set => SetValue(ItemsDragStartedCommandProperty, value);\n        }\n\n        /// <summary>\n        /// Invoked when a drag operation is completed for the <see cref=\"SelectedItems\"/>.\n        /// </summary>\n        public ICommand? ItemsDragCompletedCommand\n        {\n            get => (ICommand?)GetValue(ItemsDragCompletedCommandProperty);\n            set => SetValue(ItemsDragCompletedCommandProperty, value);\n        }\n\n        /// <summary>Invoked when a selection operation is started.</summary>\n        public ICommand? ItemsSelectStartedCommand\n        {\n            get => (ICommand?)GetValue(ItemsSelectStartedCommandProperty);\n            set => SetValue(ItemsSelectStartedCommandProperty, value);\n        }\n\n        /// <summary>Invoked when a selection operation is completed.</summary>\n        public ICommand? ItemsSelectCompletedCommand\n        {\n            get => (ICommand?)GetValue(ItemsSelectCompletedCommandProperty);\n            set => SetValue(ItemsSelectCompletedCommandProperty, value);\n        }\n\n        /// <summary>Invoked when a cutting operation is started.</summary>\n        public ICommand? CuttingStartedCommand\n        {\n            get => (ICommand?)GetValue(CuttingStartedCommandProperty);\n            set => SetValue(CuttingStartedCommandProperty, value);\n        }\n\n        /// <summary>Invoked when a cutting operation is completed.</summary>\n        public ICommand? CuttingCompletedCommand\n        {\n            get => (ICommand?)GetValue(CuttingCompletedCommandProperty);\n            set => SetValue(CuttingCompletedCommandProperty, value);\n        }\n\n        #endregion\n\n        #region Fields\n\n        /// <summary>\n        /// Gets or sets the maximum number of pixels allowed to move the mouse before cancelling the mouse event.\n        /// Useful for <see cref=\"ContextMenu\"/>s to appear if mouse only moved a bit or not at all.\n        /// </summary>\n        public static double HandleRightClickAfterPanningThreshold { get; set; } = 12d;\n\n        /// <summary>\n        /// Correct <see cref=\"ItemContainer\"/>'s position after moving if starting position is not snapped to grid.\n        /// </summary>\n        public static bool EnableSnappingCorrection { get; set; } = true;\n\n        /// <summary>\n        /// Gets or sets how often the new <see cref=\"ViewportLocation\"/> is calculated in milliseconds when <see cref=\"DisableAutoPanning\"/> is false.\n        /// </summary>\n        public static double AutoPanningTickRate { get; set; } = 16;\n\n        /// <summary>\n        /// Gets or sets if <see cref=\"NodifyEditor\"/>s should enable optimizations based on <see cref=\"OptimizeRenderingMinimumContainers\"/> and <see cref=\"OptimizeRenderingZoomOutPercent\"/>.\n        /// </summary>\n        public static bool EnableRenderingContainersOptimizations { get; set; } = true;\n\n        /// <summary>\n        /// Gets or sets the minimum number of <see cref=\"ItemContainer\"/>s needed to trigger optimizations when reaching the <see cref=\"OptimizeRenderingZoomOutPercent\"/>.\n        /// </summary>\n        public static uint OptimizeRenderingMinimumContainers { get; set; } = 700;\n\n        /// <summary>\n        /// Gets or sets the minimum zoom out percent needed to start optimizing the rendering for <see cref=\"ItemContainer\"/>s.\n        /// Value is between 0 and 1.\n        /// </summary>\n        public static double OptimizeRenderingZoomOutPercent { get; set; } = 0.3;\n\n        /// <summary>\n        /// Gets or sets the margin to add in all directions to the <see cref=\"ItemsExtent\"/> or area parameter when using <see cref=\"FitToScreen(Rect?)\"/>.\n        /// </summary>\n        public static double FitToScreenExtentMargin { get; set; } = 30;\n\n        /// <summary>\n        /// Gets or sets if the current position of containers that are being dragged should not be committed until the end of the dragging operation.\n        /// </summary>\n        public static bool EnableDraggingContainersOptimizations { get; set; } = true;\n\n        /// <summary>\n        /// Gets or sets whether the cutting line should apply the preview style to the interesected elements.\n        /// </summary>\n        /// <remarks>\n        /// This may hurt performance because intersection must be calculated on mouse move.\n        /// </remarks>\n        public static bool EnableCuttingLinePreview { get; set; } = false;\n\n        /// <summary>\n        /// The list of supported connection types for cutting. Type must be derived from <see cref=\"FrameworkElement\" />.\n        /// </summary>\n        public static readonly HashSet<Type> CuttingConnectionTypes = new HashSet<Type>();\n\n        /// <summary>\n        /// Tells if the <see cref=\"NodifyEditor\"/> is doing operations on multiple items at once.\n        /// </summary>\n        public bool IsBulkUpdatingItems { get; protected set; }\n\n        /// <summary>\n        /// Gets the panel that holds all the <see cref=\"ItemContainer\"/>s.\n        /// </summary>\n        protected internal ItemsPresenter ItemsHost { get; private set; } = default!;\n\n        /// <summary>\n        /// Gets the element that holds all the <see cref=\"BaseConnection\"/>s and custom connections.\n        /// </summary>\n        protected internal UIElement ConnectionsHost { get; private set; } = default!;\n\n        private IDraggingStrategy? _draggingStrategy;\n        private DispatcherTimer? _autoPanningTimer;\n\n        /// <summary>\n        /// Gets a list of <see cref=\"ItemContainer\"/>s that are selected.\n        /// </summary>\n        /// <remarks>Cache the result before using it to avoid extra allocations.</remarks>\n        protected internal IReadOnlyList<ItemContainer> SelectedContainers\n        {\n            get\n            {\n                var selectedItems = Selection.SelectedItems;\n                var selectedContainers = new List<ItemContainer>(selectedItems.Count);\n\n                for (var i = 0; i < selectedItems.Count; i++)\n                {\n                    var container = (ItemContainer)ContainerFromItem(selectedItems[i]);\n                    selectedContainers.Add(container);\n                }\n\n                return selectedContainers;\n            }\n        }\n\n        /// <summary>\n        /// Gets a list of all <see cref=\"ItemContainer\"/>s.\n        /// </summary>\n        /// <remarks>Cache the result before using it to avoid extra allocations.</remarks>\n        protected internal IReadOnlyCollection<ItemContainer> ItemContainers\n        {\n            get\n            {\n                ItemCollection items = Items;\n                var containers = new List<ItemContainer>(items.Count);\n\n                for (var i = 0; i < items.Count; i++)\n                {\n                    containers.Add((ItemContainer)ItemContainerGenerator.ContainerFromIndex(i));\n                }\n\n                return containers;\n            }\n        }\n\n        #endregion\n\n        #region Construction\n\n        static NodifyEditor()\n        {\n            DefaultStyleKeyProperty.OverrideMetadata(typeof(NodifyEditor), new FrameworkPropertyMetadata(typeof(NodifyEditor)));\n            FocusableProperty.OverrideDefaultValue<NodifyEditor>(true);\n            IsSelectingProperty.Changed.AddClassHandler<NodifyEditor>(OnIsSelectingChanged);\n            DisableAutoPanningProperty.Changed.AddClassHandler<NodifyEditor>(OnDisableAutoPanningChanged);\n            GridCellSizeProperty.Changed.AddClassHandler<NodifyEditor>(OnGridCellSizeChanged);\n            DisablePanningProperty.Changed.AddClassHandler<NodifyEditor>(OnDisablePanningChanged);\n            ViewportLocationProperty.Changed.AddClassHandler<NodifyEditor>(OnViewportLocationChanged);\n            ViewportZoomProperty.Changed.AddClassHandler<NodifyEditor>(OnViewportZoomChanged);\n            MinViewportZoomProperty.Changed.AddClassHandler<NodifyEditor>(OnMinViewportZoomChanged);\n            MaxViewportZoomProperty.Changed.AddClassHandler<NodifyEditor>(OnMaxViewportZoomChanged);\n            SelectedItemsProperty.Changed.AddClassHandler<NodifyEditor>(OnSelectedItemsSourceChanged);\n            IsCuttingProperty.Changed.AddClassHandler<NodifyEditor>(OnIsCuttingChanged);\n            CanSelectMultipleItemsProperty.Changed.AddClassHandler<NodifyEditor>(OnCanSelectMultipleItemsChanged);\n            ItemsExtentProperty.Changed.AddClassHandler<NodifyEditor>(OnItemsExtentChanged);\n            IsPushingItemsProperty.Changed.AddClassHandler<NodifyEditor>(OnIsPushingItemsChanged);\n\n            EditorCommands.Register(typeof(NodifyEditor));\n        }\n\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"NodifyEditor\"/> class.\n        /// </summary>\n        public NodifyEditor()\n        {\n            AddHandler(Gestures.PointerTouchPadGestureMagnifyEvent, OnPointerTouchPadGestureMagnify);\n            AddHandler(Connector.DisconnectEvent, new ConnectorEventHandler(OnConnectorDisconnected));\n            AddHandler(Connector.PendingConnectionStartedEvent, new PendingConnectionEventHandler(OnConnectionStarted));\n            AddHandler(Connector.PendingConnectionCompletedEvent, new PendingConnectionEventHandler(OnConnectionCompleted));\n\n            AddHandler(BaseConnection.DisconnectEvent, new ConnectionEventHandler(OnRemoveConnection));\n\n            AddHandler(ItemContainer.DragStartedEvent, OnItemsDragStarted);\n            AddHandler(ItemContainer.DragCompletedEvent, OnItemsDragCompleted);\n            AddHandler(ItemContainer.DragDeltaEvent, OnItemsDragDelta);\n\n            AddHandler(PointerPressedEvent, OnPreviewPointerPressed, RoutingStrategies.Tunnel);\n\n            UpdateViewportTransform(this);\n            \n            _states.Push(GetInitialState());\n\n            Selection.SourceReset += OnSourceReset;\n        }\n\n        /// <inheritdoc />\n        protected override void OnApplyTemplate(TemplateAppliedEventArgs e)\n        {\n            base.OnApplyTemplate(e);\n\n            ItemsHost = e.NameScope.Find<ItemsPresenter>(\"PART_ItemsPresenter\") ?? throw new InvalidOperationException($\"{ElementItemsHost} is missing or is not of type Panel.\");\n            ConnectionsHost = e.NameScope.Find<ItemsControl>(ElementConnectionsHost) ?? throw new InvalidOperationException($\"{ElementConnectionsHost} is missing or is not of type UIElement.\");\n\n            OnDisableAutoPanningChanged(DisableAutoPanning);\n\n            State.Enter(null, null);\n        }\n        \n        /// <inheritdoc />\n        protected override DependencyObject GetContainerForItemOverride()\n            => new ItemContainer(this)\n            {\n                RenderTransform = new TranslateTransform(),\n                RenderTransformOrigin = new RelativePoint(0, 0, RelativeUnit.Relative)\n            };\n\n        /// <inheritdoc />\n        protected override bool IsItemItsOwnContainerOverride(object item)\n            => item is ItemContainer;\n\n        #endregion\n\n        #region Methods\n\n        internal const int MouseWheelDeltaForOneLine = 120;\n        \n        /// <summary>\n        /// Zoom in at the viewports center\n        /// </summary>\n        public void ZoomIn() => ZoomAtPosition(Math.Pow(2.0, 120.0 / 3.0 / MouseWheelDeltaForOneLine), (Point)((Vector)ViewportLocation + ViewportSize.ToVector() / 2));\n\n        /// <summary>\n        /// Zoom out at the viewports center\n        /// </summary>\n        public void ZoomOut() => ZoomAtPosition(Math.Pow(2.0, -120.0 / 3.0 / MouseWheelDeltaForOneLine), (Point)((Vector)ViewportLocation + ViewportSize.ToVector() / 2));\n\n        /// <summary>\n        /// Zoom at the specified location in graph space coordinates.\n        /// </summary>\n        /// <param name=\"zoom\">The zoom factor.</param>\n        /// <param name=\"location\">The location to focus when zooming.</param>\n        public void ZoomAtPosition(double zoom, Point location)\n        {\n            if (!DisableZooming)\n            {\n                double prevZoom = ViewportZoom;\n                ViewportZoom *= zoom;\n\n                if (Math.Abs(prevZoom - ViewportZoom) > 0.0000001)\n                {\n                    // get the actual zoom value because Zoom might have been coerced\n                    zoom = ViewportZoom / prevZoom;\n                    Vector position = (Vector)location;\n\n                    var dist = position - (Vector)ViewportLocation;\n                    var zoomedDist = dist * zoom;\n                    var diff = zoomedDist - dist;\n                    ViewportLocation += diff / zoom;\n                }\n            }\n        }\n\n        /// <summary>\n        /// Moves the viewport center at the specified location.\n        /// </summary>\n        /// <param name=\"point\">The location in graph space coordinates.</param>\n        /// <param name=\"animated\">True to animate the movement.</param>\n        /// <param name=\"onFinish\">The callback invoked when movement is finished.</param>\n        /// <remarks>Temporarily disables editor controls when animated.</remarks>\n        public void BringIntoView(Point point, bool animated = true, Action? onFinish = null)\n        {\n            bringToViewToken?.Cancel();\n\n            Point newLocation = (Point)((Vector)point - ViewportSize.ToVector() / 2);\n\n            if (animated && newLocation != ViewportLocation)\n            {\n                IsPanning = true;\n                SetCurrentValue(DisablePanningProperty, true);\n                SetCurrentValue(DisableZoomingProperty, true);\n\n                double distance = (newLocation - ViewportLocation).Length();\n                double duration = distance / (BringIntoViewSpeed + (distance / 10)) * ViewportZoom;\n                duration = Math.Max(0.1, Math.Min(duration, BringIntoViewMaxDuration));\n\n                bringToViewToken = new CancellationTokenSource();\n                this.StartAnimation(ViewportLocationProperty, newLocation, duration, bringToViewToken.Token, (s, e) =>\n                {\n                    IsPanning = false;\n                    SetCurrentValue(DisablePanningProperty, false);\n                    SetCurrentValue(DisableZoomingProperty, false);\n\n                    onFinish?.Invoke();\n                });\n            }\n            else\n            {\n                SetCurrentValue(ViewportLocationProperty, newLocation);\n                onFinish?.Invoke();\n            }\n        }\n\n        /// <summary>\n        /// Moves the viewport center at the center of the specified area.\n        /// </summary>\n        /// <param name=\"area\">The location in graph space coordinates.</param>\n        public new void BringIntoView(Rect area)\n            => BringIntoView(new Point(area.X + area.Width / 2, area.Y + area.Height / 2));\n\n        /// <summary>\n        /// Scales the viewport to fit the specified <paramref name=\"area\"/> or all the <see cref=\"ItemContainer\"/>s if that's possible.\n        /// </summary>\n        /// <remarks>Does nothing if <paramref name=\"area\"/> is null and there's no items.</remarks>\n        public void FitToScreen(Rect? area = null)\n        {\n            Rect extent = area ?? ItemsExtent;\n            extent = extent.Inflate(FitToScreenExtentMargin);\n\n            if (extent.Width > 0 && extent.Height > 0)\n            {\n                double widthRatio = ViewportSize.Width / extent.Width;\n                double heightRatio = ViewportSize.Height / extent.Height;\n\n                double zoom = Math.Min(widthRatio, heightRatio);\n                var center = new Point(extent.X + extent.Width / 2, extent.Y + extent.Height / 2);\n\n                ZoomAtPosition(zoom, center);\n                BringIntoView(center, animated: false);\n            }\n        }\n\n        #endregion\n\n        #region Auto panning\n\n        private void HandleAutoPanning(object? sender, EventArgs e)\n        {\n            if (!IsPanning && IsMouseCaptureWithin)\n            {\n                Point mousePosition = RelativeMouseLocation;\n                double edgeDistance = AutoPanEdgeDistance;\n                double autoPanSpeed = Math.Min(AutoPanSpeed, AutoPanSpeed * AutoPanningTickRate) / (ViewportZoom * 2);\n                double x = ViewportLocation.X;\n                double y = ViewportLocation.Y;\n\n                if (mousePosition.X <= edgeDistance)\n                {\n                    x -= autoPanSpeed;\n                    mouseLocation -= new Vector(autoPanSpeed, 0);\n                }\n                else if (mousePosition.X >= Bounds.Width - edgeDistance)\n                {\n                    x += autoPanSpeed;\n                    mouseLocation += new Vector(autoPanSpeed, 0);\n                }\n\n                if (mousePosition.Y <= edgeDistance)\n                {\n                    y -= autoPanSpeed;\n                    mouseLocation -= new Vector(0, autoPanSpeed);\n                }\n                else if (mousePosition.Y >= Bounds.Height - edgeDistance)\n                {\n                    y += autoPanSpeed;\n                    mouseLocation += new Vector(0, autoPanSpeed);\n                }\n\n                SetCurrentValue(ViewportLocationProperty, new Point(x, y));\n\n                State.HandleAutoPanning(null);\n            }\n        }\n\n        /// <summary>\n        /// Called when the <see cref=\"DisableAutoPanning\"/> changes.\n        /// </summary>\n        /// <param name=\"shouldDisable\">Whether to enable or disable auto panning.</param>\n        protected virtual void OnDisableAutoPanningChanged(bool shouldDisable)\n        {\n            if (shouldDisable)\n            {\n                _autoPanningTimer?.Stop();\n            }\n            else if (_autoPanningTimer == null)\n            {\n                _autoPanningTimer = new DispatcherTimer(TimeSpan.FromMilliseconds(AutoPanningTickRate),\n                    DispatcherPriority.Background, HandleAutoPanning);\n            }\n            else\n            {\n                _autoPanningTimer.Interval = TimeSpan.FromMilliseconds(AutoPanningTickRate);\n                _autoPanningTimer.Start();\n            }\n        }\n\n        #endregion\n\n        #region Connector handling\n\n        private void OnConnectorDisconnected(object? sender, ConnectorEventArgs e)\n        {\n            if (!e.Handled && (DisconnectConnectorCommand?.CanExecute(e.Connector) ?? false))\n            {\n                DisconnectConnectorCommand.Execute(e.Connector);\n                e.Handled = true;\n            }\n        }\n\n        private void OnConnectionStarted(object? sender, PendingConnectionEventArgs e)\n        {\n            if (!e.Canceled && ConnectionStartedCommand != null)\n            {\n                e.Canceled = !ConnectionStartedCommand.CanExecute(e.SourceConnector);\n                if (!e.Canceled)\n                {\n                    ConnectionStartedCommand.Execute(e.SourceConnector);\n                }\n            }\n        }\n\n        private void OnConnectionCompleted(object? sender, PendingConnectionEventArgs e)\n        {\n            if (!e.Canceled)\n            {\n                (object SourceConnector, object? TargetConnector) result = (e.SourceConnector, e.TargetConnector);\n                if (ConnectionCompletedCommand?.CanExecute(result) ?? false)\n                {\n                    ConnectionCompletedCommand.Execute(result);\n                }\n            }\n        }\n\n        private void OnRemoveConnection(object? sender, ConnectionEventArgs e)\n        {\n            OnRemoveConnection(e.Connection);\n        }\n\n        protected void OnRemoveConnection(object? dataContext)\n        {\n            if (RemoveConnectionCommand?.CanExecute(dataContext) ?? false)\n            {\n                RemoveConnectionCommand.Execute(dataContext);\n            }\n        }\n\n        #endregion\n\n        #region State Handling\n\n        private readonly Stack<EditorState> _states = new Stack<EditorState>();\n\n        /// <summary>The current state of the editor.</summary>\n        public EditorState State => _states.Peek();\n\n        /// <summary>Creates the initial state of the editor.</summary>\n        /// <returns>The initial state.</returns>\n        protected virtual EditorState GetInitialState()\n            => new EditorDefaultState(this);\n\n        /// <summary>Pushes the given state to the stack.</summary>\n        /// <param name=\"state\">The new state of the editor.</param>\n        /// <remarks>Calls <see cref=\"EditorState.Enter\"/> on the new state.</remarks>\n        public void PushState(EditorState state, MouseEventArgs e)\n        {\n            var prev = State;\n            _states.Push(state);\n            state.Enter(prev, e);\n        }\n\n        /// <summary>Pops the current <see cref=\"State\"/> from the stack.</summary>\n        /// <remarks>It doesn't pop the initial state. (see <see cref=\"GetInitialState\"/>)\n        /// <br />Calls <see cref=\"EditorState.Exit\"/> on the current state.\n        /// <br />Calls <see cref=\"EditorState.ReEnter\"/> on the previous state.\n        /// </remarks>\n        public void PopState()\n        {\n            // Never remove the default state\n            if (_states.Count > 1)\n            {\n                EditorState prev = _states.Pop();\n                prev.Exit();\n                State.ReEnter(prev);\n            }\n        }\n\n        /// <summary>Pops all states from the editor.</summary>\n        /// <remarks>It doesn't pop the initial state. (see <see cref=\"GetInitialState\"/>)</remarks>\n        public void PopAllStates()\n        {\n            while (_states.Count > 1)\n            {\n                PopState();\n            }\n        }\n\n        /// <inheritdoc />\n        protected override void OnPointerPressed(PointerPressedEventArgs e)\n        {\n            // Needed to not steal mouse capture from children\n            if (e.Pointer.Captured == null)\n            {\n                Focus();\n                e.Pointer.Capture(this);\n                this.PropagateMouseCapturedWithin(true);\n\n                MouseLocation = e.GetPosition(ItemsHost);\n                State.HandleMouseDown(new MouseButtonEventArgs(e));\n            }\n        }\n\n        /// <inheritdoc />\n        protected override void OnPointerReleased(PointerReleasedEventArgs e)\n        {\n            MouseLocation = e.GetPosition(ItemsHost);\n            State.HandleMouseUp(new MouseButtonEventArgs(e));\n\n            // Release the mouse capture if all the mouse buttons are released\n            if (ReferenceEquals(e.Pointer.Captured, this) && e.GetCurrentPoint(this) is { Properties: { IsLeftButtonPressed: false, IsRightButtonPressed: false, IsMiddleButtonPressed: false} })\n            {\n                e.Pointer.Capture(null);\n                this.PropagateMouseCapturedWithin(false);\n            }\n\n            // Disable context menu if selecting\n            if (IsSelecting)\n            {\n                e.Handled = true;\n            }\n\n            if (e.Handled)\n                return;\n\n            base.OnPointerReleased(e);\n        }\n\n        /// <inheritdoc />\n        protected override void OnPointerMoved(PointerEventArgs e)\n        {\n            MouseLocation = e.GetPosition(ItemsHost);\n            RelativeMouseLocation = e.GetPosition(this);\n            State.HandleMouseMove(new MouseMoveEventArgs(e));\n\n            // Avalonia here works like UWP rather than WPF\n            // https://github.com/AvaloniaUI/Avalonia/issues/14777\n            // that means when a mouse button is pressed, other pointer pressed events are not raised.\n            // Instead, only the mouse move event is raised with appropriate PointerUpdateKind\n            // So here I am transforming the pointer move event to a pointer pressed event to mimic WPF\n            var pointerUpdateKind = e.GetCurrentPoint(this).Properties.PointerUpdateKind;\n            if (pointerUpdateKind is PointerUpdateKind.RightButtonPressed or PointerUpdateKind.LeftButtonPressed or PointerUpdateKind.MiddleButtonPressed)\n            {\n                State.HandleMouseDown(new MouseButtonEventArgs(e));\n            }\n            else if (pointerUpdateKind is PointerUpdateKind.RightButtonReleased or PointerUpdateKind.LeftButtonReleased or PointerUpdateKind.MiddleButtonReleased)\n            {\n                State.HandleMouseUp(new MouseButtonEventArgs(e));\n            }\n        }\n\n        /// <inheritdoc />\n        protected override void OnPointerCaptureLost(PointerCaptureLostEventArgs e)\n            => PopAllStates();\n\n        /// <inheritdoc />\n        protected override void OnPointerWheelChanged(PointerWheelEventArgs e)\n        {\n            State.HandleMouseWheel(new MouseWheelEventArgs(e));\n\n            if (!e.Handled && EditorGestures.Mappings.Editor.ZoomModifierKey == e.KeyModifiers)\n            {\n                double zoom = Math.Pow(2.0, e.Delta.Y / 3.0 / MouseWheelDeltaForOneLine * MouseWheelAvaloniaToWpfScale);\n                ZoomAtPosition(zoom, e.GetPosition(ItemsHost));\n\n                // Handle it for nested editors\n                // in Avalonia e.Source is equivalent of e.OriginalSource in WPF\n                // in this case we can safely ignore the source and always handle the event\n                // if (e.Source is NodifyEditor)\n                {\n                    e.Handled = true;\n                }\n            }\n        }\n\n        protected override void OnKeyUp(KeyEventArgs e)\n            => State.HandleKeyUp(e);\n\n        protected override void OnKeyDown(KeyEventArgs e)\n            => State.HandleKeyDown(e);\n\n        #endregion\n\n        #region Selection Handlers\n\n        private void OnSelectedItemsSourceChanged(IList oldValue, IList newValue)\n        {\n            if (oldValue is INotifyCollectionChanged oc)\n            {\n                oc.CollectionChanged -= OnSelectedItemsChanged;\n            }\n\n            if (newValue is INotifyCollectionChanged nc)\n            {\n                nc.CollectionChanged += OnSelectedItemsChanged;\n            }\n\n            BeginUpdateSelectedItems();\n            Selection.Clear();\n            if (newValue != null)\n            {\n                for (var i = 0; i < newValue.Count; i++)\n                {\n                    Selection.Select(Items.IndexOf(newValue[i]));\n                }\n            }\n            EndUpdateSelectedItems();\n        }\n\n        private void OnSelectedItemsChanged(object? sender, NotifyCollectionChangedEventArgs e)\n        {\n            if (inOnSelectedItemsChanged)\n                return;\n\n            if (!CanSelectMultipleItems)\n                return;\n\n            switch (e.Action)\n            {\n                case NotifyCollectionChangedAction.Reset:\n                    UnselectAll();\n                    break;\n\n                case NotifyCollectionChangedAction.Add:\n                    IList? newItems = e.NewItems;\n                    if (newItems != null)\n                    {\n                        for (var i = 0; i < newItems.Count; i++)\n                        {\n                            Selection.Select(Items.IndexOf(newItems[i]));\n                        }\n                    }\n                    break;\n\n                case NotifyCollectionChangedAction.Remove:\n                    IList? oldItems = e.OldItems;\n                    if (oldItems != null)\n                    {\n                        for (var i = 0; i < oldItems.Count; i++)\n                        {\n                            Selection.Deselect(Items.IndexOf(oldItems[i]));\n                        }\n                    }\n                    break;\n            }\n        }\n\n        /// <inheritdoc />\n        protected override void OnSelectionChanged(SelectionModelSelectionChangedEventArgs e)\n        {\n            inOnSelectedItemsChanged = true;\n            IList? selected = SelectedItems;\n            if (selected != null)\n            {\n                IReadOnlyList<object?> added = e.SelectedItems;\n                for (var i = 0; i < added.Count; i++)\n                {\n                    // Ensure no duplicates are added\n                    if (!selected.Contains(added[i]))\n                    {\n                        selected.Add(added[i]);\n                    }\n                }\n\n                IReadOnlyList<object?> removed = e.DeselectedItems;\n                for (var i = 0; i < removed.Count; i++)\n                {\n                    selected.Remove(removed[i]);\n                }\n            }\n\n            inOnSelectedItemsChanged = false;\n        }\n\n        #endregion\n\n        #region Selection\n\n        internal void ApplyPreviewingSelection()\n        {\n            ItemCollection items = Items;\n\n            IsSelecting = true;\n            BeginUpdateSelectedItems();\n            for (var i = 0; i < items.Count; i++)\n            {\n                var container = (ItemContainer)ItemContainerGenerator.ContainerFromIndex(i);\n                if (container.IsPreviewingSelection == true && container.IsSelectable)\n                {\n                    Selection.Select(i);\n                }\n                else if (container.IsPreviewingSelection == false)\n                {\n                    Selection.Deselect(i);\n                }\n                container.IsPreviewingSelection = null;\n            }\n            EndUpdateSelectedItems();\n            IsSelecting = false;\n        }\n\n        internal void ClearPreviewingSelection()\n        {\n            ItemCollection items = Items;\n            for (var i = 0; i < items.Count; i++)\n            {\n                var container = (ItemContainer)ItemContainerGenerator.ContainerFromIndex(i);\n                container.IsPreviewingSelection = null;\n            }\n        }\n\n        /// <summary>\n        /// Inverts the <see cref=\"ItemContainer\"/>s selection in the specified <paramref name=\"area\"/>.\n        /// </summary>\n        /// <param name=\"area\">The area to look for <see cref=\"ItemContainer\"/>s.</param>\n        /// <param name=\"fit\">True to check if the <paramref name=\"area\"/> contains the <see cref=\"ItemContainer\"/>. <br />False to check if <paramref name=\"area\"/> intersects the <see cref=\"ItemContainer\"/>.</param>\n        public void InvertSelection(Rect area, bool fit = false)\n        {\n            ItemCollection items = Items;\n\n            IsSelecting = true;\n            BeginUpdateSelectedItems();\n            for (var i = 0; i < items.Count; i++)\n            {\n                var container = (ItemContainer)ItemContainerGenerator.ContainerFromIndex(i);\n\n                if (container.IsSelectableInArea(area, fit))\n                {\n                    object? item = items[i];\n                    if (container.IsSelected)\n                    {\n                        Selection.Deselect(i);\n                    }\n                    else\n                    {\n                        Selection.Select(i);\n                    }\n                }\n            }\n            EndUpdateSelectedItems();\n            IsSelecting = false;\n        }\n\n        /// <summary>\n        /// Selects the <see cref=\"ItemContainer\"/>s in the specified <paramref name=\"area\"/>.\n        /// </summary>\n        /// <param name=\"area\">The area to look for <see cref=\"ItemContainer\"/>s.</param>\n        /// <param name=\"append\">If true, it will add to the existing selection.</param>\n        /// <param name=\"fit\">True to check if the <paramref name=\"area\"/> contains the <see cref=\"ItemContainer\"/>. <br />False to check if <paramref name=\"area\"/> intersects the <see cref=\"ItemContainer\"/>.</param>\n        public void SelectArea(Rect area, bool append = false, bool fit = false)\n        {\n            if (!append)\n            {\n                Selection.Clear();\n            }\n\n            ItemCollection items = Items;\n\n            IsSelecting = true;\n            BeginUpdateSelectedItems();\n            for (var i = 0; i < items.Count; i++)\n            {\n                var container = (ItemContainer)ItemContainerGenerator.ContainerFromIndex(i);\n                if (container.IsSelectableInArea(area, fit))\n                {\n                    Selection.Select(i);\n                }\n            }\n            EndUpdateSelectedItems();\n            IsSelecting = false;\n        }\n\n        /// <summary>\n        /// Unselect the <see cref=\"ItemContainer\"/>s in the specified <paramref name=\"area\"/>.\n        /// </summary>\n        /// <param name=\"area\">The area to look for <see cref=\"ItemContainer\"/>s.</param>\n        /// <param name=\"fit\">True to check if the <paramref name=\"area\"/> contains the <see cref=\"ItemContainer\"/>. <br />False to check if <paramref name=\"area\"/> intersects the <see cref=\"ItemContainer\"/>.</param>\n        public void UnselectArea(Rect area, bool fit = false)\n        {\n            IReadOnlyList<object?> items = Selection.SelectedItems;\n\n            IsSelecting = true;\n            BeginUpdateSelectedItems();\n            for (var i = 0; i < items.Count; i++)\n            {\n                var container = (ItemContainer)ContainerFromItem(items[i]);\n                if (container.IsSelectableInArea(area, fit))\n                {\n                    Selection.Deselect(Items.IndexOf(items[i]));\n                }\n            }\n            EndUpdateSelectedItems();\n            IsSelecting = false;\n        }\n\n        /// <summary>\n        /// Unselect all <see cref=\"Connections\"/>.\n        /// </summary>\n        public void UnselectAllConnection()\n        {\n            if (ConnectionsHost is MultiSelector selector)\n            {\n                selector.UnselectAll();\n            }\n        }\n\n        /// <summary>\n        /// Select all <see cref=\"Connections\"/>.\n        /// </summary>\n        public void SelectAllConnections()\n        {\n            if (ConnectionsHost is MultiSelector selector)\n            {\n                selector.SelectAll();\n            }\n        }\n\n        #endregion\n\n        #region Dragging\n\n        private void OnItemsDragDelta(object? sender, DragDeltaEventArgs e)\n        {\n            _draggingStrategy?.Update(new Vector(e.HorizontalChange, e.VerticalChange));\n        }\n\n        private void OnItemsDragCompleted(object? sender, DragCompletedEventArgs e)\n        {\n            if (e.Canceled && ItemContainer.AllowDraggingCancellation)\n            {\n                _draggingStrategy?.Abort();\n            }\n            else\n            {\n                IsBulkUpdatingItems = true;\n\n                _draggingStrategy?.End();\n\n                IsBulkUpdatingItems = false;\n\n                // Draw the containers at the new position.\n                ItemsHost.Panel?.InvalidateArrange();\n            }\n\n            if (ItemsDragCompletedCommand?.CanExecute(DataContext) ?? false)\n            {\n                ItemsDragCompletedCommand.Execute(DataContext);\n            }\n        }\n\n        private void OnItemsDragStarted(object? sender, DragStartedEventArgs e)\n        {\n            _draggingStrategy = CreateDraggingStrategy(SelectedContainers);\n\n            if (Selection.Count > 0)\n            {\n                if (ItemsDragStartedCommand?.CanExecute(DataContext) ?? false)\n                {\n                    ItemsDragStartedCommand.Execute(DataContext);\n                }\n\n                e.Handled = true;\n            }\n        }\n\n        internal IDraggingStrategy CreateDraggingStrategy(IEnumerable<ItemContainer> containers)\n        {\n            if (EnableDraggingContainersOptimizations)\n            {\n                return new DraggingOptimized(containers, GridCellSize);\n            }\n\n            return new DraggingSimple(containers, GridCellSize);\n        }\n\n        #endregion\n\n        #region Cutting\n\n        /// <summary>\n        /// Starts the cutting operation at the specified location. Call <see cref=\"EndCutting\"/> to finish cutting.\n        /// </summary>\n        protected internal void StartCutting(Point location)\n        {\n            CuttingLineStart = location;\n            CuttingLineEnd = location;\n            IsCutting = true;\n        }\n\n        /// <summary>\n        /// Cancels the cutting operation.\n        /// </summary>\n        protected internal void CancelCutting()\n        {\n            if (IsCutting)\n            {\n                IsCutting = false;\n            }\n        }\n\n        /// <summary>\n        /// Ends the cutting operation at the specified location.\n        /// </summary>\n        protected internal void EndCutting(Point location)\n        {\n            CuttingLineEnd = location;\n\n            var lineGeometry = new LineGeometry(CuttingLineStart, CuttingLineEnd);\n            var connections = ConnectionsHost.GetIntersectingElements(lineGeometry, CuttingConnectionTypes);\n\n            if (RemoveConnectionCommand != null)\n            {\n                foreach (var connection in connections)\n                {\n                    OnRemoveConnection(connection.DataContext);\n                }\n            }\n            else\n            {\n                foreach (var connection in connections)\n                {\n                    if (connection is BaseConnection bc)\n                    {\n                        bc.OnDisconnect();\n                    }\n                }\n            }\n\n            IsCutting = false;\n        }\n\n        #endregion\n\n        /// <inheritdoc />\n        protected override void OnSizeChanged(SizeChangedInfo sizeInfo)\n        {\n            base.OnSizeChanged(sizeInfo);\n\n            double zoom = ViewportZoom;\n            SetCurrentValue(ViewportSizeProperty, new Size(Bounds.Width / zoom, Bounds.Height / zoom));\n\n            OnViewportUpdated();\n        }\n\n        #region Utilities\n\n        /// <summary>\n        /// Translates the specified location to graph space coordinates (relative to the <see cref=\"ItemsHost\" />).\n        /// </summary>\n        /// <param name=\"location\">The location coordinates relative to <paramref name=\"relativeTo\"/></param>\n        /// <param name=\"relativeTo\">The element where the <paramref name=\"location\"/> was calculated from.</param>\n        /// <returns>A location inside the graph.</returns>\n        public Point GetLocationInsideEditor(Point location, UIElement relativeTo)\n            => relativeTo.TranslatePoint(location, ItemsHost) ?? default;\n\n        /// <summary>\n        /// Translates the event location to graph space coordinates (relative to the <see cref=\"ItemsHost\" />).\n        /// </summary>\n        /// <param name=\"args\">The drag event.</param>\n        /// <returns>A location inside the graph</returns>\n        public Point GetLocationInsideEditor(DragEventArgs args)\n            => args.GetPosition(ItemsHost);\n\n        /// <summary>\n        /// Translates the event location to graph space coordinates (relative to the <see cref=\"ItemsHost\" />).\n        /// </summary>\n        /// <param name=\"args\">The mouse event.</param>\n        /// <returns>A location inside the graph</returns>\n        public Point GetLocationInsideEditor(PointerEventArgs args)\n            => args.GetPosition(ItemsHost);\n\n        /// <summary>\n        /// Snaps the given value down to the nearest multiple of the grid cell size.\n        /// </summary>\n        /// <param name=\"value\">The value to be snapped to the grid.</param>\n        /// <returns>The largest multiple of the grid cell size less than or equal to the value.</returns>\n        public double SnapToGrid(double value)\n        {\n            return (int)value / GridCellSize * GridCellSize;\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "Nodify/Properties/AssemblyInfo.cs",
    "content": "using System.Reflection;\nusing System.Runtime.InteropServices;\nusing System.Windows;\nusing System.Windows.Markup;\n\n[assembly: ThemeInfo(ResourceDictionaryLocation.ExternalAssembly, ResourceDictionaryLocation.SourceAssembly)]\n[assembly: XmlnsDefinition(\"https://miroiu.github.io/nodify\", \"Nodify\")]\n[assembly: XmlnsPrefix(\"https://miroiu.github.io/nodify\", \"nodify\")]\n\n[assembly: ComVisible(false)]\n[assembly: Guid(\"f70fca9c-3224-4b0b-a97a-bde5a92cbf08\")]\n\n[assembly: InternalsVisibleTo(\"Nodify.Shared, PublicKey=0024000004800000940000000602000000240000525341310004000001000100dd4a9985a64bac9b6ff775d13d7b4a0ed7cd2826239ac3ed2a5117bc2ca38128a0b204567e3bb9ef1ff99d9e8b676a5edfd713c23285c12757dd52272233fbb9bd48bfd4d4f4554dabd09243931195249cd7f006e92c5edbab4c4c1205d2edfd77cdc0d2b8bef6d26b1803989399d22b5d25ce4141a9d6a4f708fcc011c6e1cf\")]\n[assembly: InternalsVisibleTo(\"Nodify.Calculator, PublicKey=0024000004800000940000000602000000240000525341310004000001000100dd4a9985a64bac9b6ff775d13d7b4a0ed7cd2826239ac3ed2a5117bc2ca38128a0b204567e3bb9ef1ff99d9e8b676a5edfd713c23285c12757dd52272233fbb9bd48bfd4d4f4554dabd09243931195249cd7f006e92c5edbab4c4c1205d2edfd77cdc0d2b8bef6d26b1803989399d22b5d25ce4141a9d6a4f708fcc011c6e1cf\")]\n[assembly: InternalsVisibleTo(\"Nodify.Playground, PublicKey=0024000004800000940000000602000000240000525341310004000001000100dd4a9985a64bac9b6ff775d13d7b4a0ed7cd2826239ac3ed2a5117bc2ca38128a0b204567e3bb9ef1ff99d9e8b676a5edfd713c23285c12757dd52272233fbb9bd48bfd4d4f4554dabd09243931195249cd7f006e92c5edbab4c4c1205d2edfd77cdc0d2b8bef6d26b1803989399d22b5d25ce4141a9d6a4f708fcc011c6e1cf\")]\n[assembly: InternalsVisibleTo(\"Nodify.StateMachine, PublicKey=0024000004800000940000000602000000240000525341310004000001000100dd4a9985a64bac9b6ff775d13d7b4a0ed7cd2826239ac3ed2a5117bc2ca38128a0b204567e3bb9ef1ff99d9e8b676a5edfd713c23285c12757dd52272233fbb9bd48bfd4d4f4554dabd09243931195249cd7f006e92c5edbab4c4c1205d2edfd77cdc0d2b8bef6d26b1803989399d22b5d25ce4141a9d6a4f708fcc011c6e1cf\")]\n[assembly: InternalsVisibleTo(\"Nodify.Shapes, PublicKey=0024000004800000940000000602000000240000525341310004000001000100dd4a9985a64bac9b6ff775d13d7b4a0ed7cd2826239ac3ed2a5117bc2ca38128a0b204567e3bb9ef1ff99d9e8b676a5edfd713c23285c12757dd52272233fbb9bd48bfd4d4f4554dabd09243931195249cd7f006e92c5edbab4c4c1205d2edfd77cdc0d2b8bef6d26b1803989399d22b5d25ce4141a9d6a4f708fcc011c6e1cf\")]"
  },
  {
    "path": "Nodify/Theme.axaml",
    "content": "<ResourceDictionary xmlns=\"https://github.com/avaloniaui\"\n                    xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\">\n    <ResourceDictionary.MergedDictionaries>\n        <ResourceInclude Source=\"avares://Nodify/Themes/Generic.xaml\" />\n    </ResourceDictionary.MergedDictionaries>\n    <ResourceDictionary.ThemeDictionaries>\n        <ResourceInclude x:Key=\"Default\" Source=\"avares://Nodify/Themes/Light.xaml\" />\n        <ResourceInclude x:Key=\"Dark\" Source=\"avares://Nodify/Themes/Dark.xaml\" />\n    </ResourceDictionary.ThemeDictionaries>\n</ResourceDictionary>"
  },
  {
    "path": "Nodify/Themes/Brushes.xaml",
    "content": "﻿<ResourceDictionary xmlns=\"https://github.com/avaloniaui\"\n                    xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n                    xmlns:o=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation/options\">\n\n    <!--NODIFY EDITOR-->\n\n    <SolidColorBrush x:Key=\"NodifyEditor.BackgroundBrush\"\n                     Color=\"{DynamicResource NodifyEditor.BackgroundColor}\" />\n\n    <SolidColorBrush x:Key=\"NodifyEditor.ForegroundBrush\"\n                     Color=\"{DynamicResource NodifyEditor.ForegroundColor}\" />\n\n    <SolidColorBrush x:Key=\"NodifyEditor.SelectionRectangleStrokeBrush\"\n                     Color=\"{DynamicResource NodifyEditor.SelectionRectangleColor}\" />\n\n    <SolidColorBrush x:Key=\"NodifyEditor.SelectionRectangleBackgroundBrush\"\n                     Opacity=\"0.1\"\n                     Color=\"{DynamicResource NodifyEditor.SelectionRectangleColor}\" />\n\n    <SolidColorBrush x:Key=\"NodifyEditor.PushedAreaStrokeBrush\"\n                     Color=\"{DynamicResource NodifyEditor.PushedAreaColor}\" />\n\n    <SolidColorBrush x:Key=\"NodifyEditor.PushedAreaBackgroundBrush\"\n                     Opacity=\"0.2\"\n                     Color=\"{DynamicResource NodifyEditor.PushedAreaColor}\" />\n\n    <SolidColorBrush x:Key=\"NodifyEditor.CuttingLineStrokeBrush\"\n                     Color=\"{DynamicResource NodifyEditor.CuttingLineColor}\" />\n\n    <SolidColorBrush x:Key=\"NodifyEditor.CuttingLineBackgroundBrush\"\n                     Color=\"{DynamicResource NodifyEditor.CuttingLineColor}\" />\n\n    <!--ITEM CONTAINER-->\n\n    <SolidColorBrush x:Key=\"ItemContainer.BorderBrush\"\n                     Color=\"{DynamicResource ItemContainer.BorderColor}\" />\n\n    <DrawingBrush x:Key=\"ItemContainer.HighlightBrush\"\n                  DestinationRect=\"0 0 24 24\"\n                  TileMode=\"Tile\">\n        <DrawingBrush.Drawing>\n            <DrawingGroup>\n                <GeometryDrawing Brush=\"{StaticResource ItemContainer.BorderBrush}\">\n                    <GeometryDrawing.Geometry>\n                        <GeometryGroup>\n                            <RectangleGeometry Rect=\"0 0 50 50\" />\n                            <RectangleGeometry Rect=\"50 50 50 50\" />\n                        </GeometryGroup>\n                    </GeometryDrawing.Geometry>\n                </GeometryDrawing>\n            </DrawingGroup>\n        </DrawingBrush.Drawing>\n    </DrawingBrush>\n\n    <SolidColorBrush x:Key=\"ItemContainer.SelectedBrush\"\n                     Color=\"{DynamicResource ItemContainer.SelectedColor}\" />\n\n    <!--NODE-->\n\n    <SolidColorBrush x:Key=\"Node.BackgroundBrush\"\n                     Color=\"{DynamicResource Node.BackgroundColor}\" />\n\n    <SolidColorBrush x:Key=\"Node.ContentBrush\"\n                     Color=\"{DynamicResource Node.ContentColor}\" />\n\n    <SolidColorBrush x:Key=\"Node.ForegroundBrush\"\n                     Color=\"{DynamicResource Node.ForegroundColor}\" />\n    \n    <SolidColorBrush x:Key=\"Node.HeaderForegroundBrush\"\n                     Color=\"{DynamicResource Node.HeaderForegroundColor}\" />\n\n    <SolidColorBrush x:Key=\"Node.BorderBrush\"\n                     Color=\"{DynamicResource Node.BorderColor}\" />\n\n    <SolidColorBrush x:Key=\"Node.HeaderBrush\"\n                     Color=\"{DynamicResource Node.HeaderColor}\" />\n\n    <SolidColorBrush x:Key=\"Node.FooterBrush\"\n                     Color=\"{DynamicResource Node.FooterColor}\" />\n\n    <!--STATE NODE-->\n\n    <SolidColorBrush x:Key=\"StateNode.BackgroundBrush\"\n                     Color=\"{DynamicResource StateNode.BackgroundColor}\" />\n\n    <SolidColorBrush x:Key=\"StateNode.ForegroundBrush\"\n                     Color=\"{DynamicResource StateNode.ForegroundColor}\" />\n\n    <SolidColorBrush x:Key=\"StateNode.BorderBrush\"\n                     Color=\"{DynamicResource StateNode.BorderColor}\" />\n\n    <SolidColorBrush x:Key=\"StateNode.HighlightBrush\"\n                     Color=\"{DynamicResource StateNode.HighlightColor}\" />\n\n    <!--GROUPING NODE-->\n\n    <SolidColorBrush x:Key=\"GroupingNode.BackgroundBrush\"\n                     Opacity=\".5\" \n                     Color=\"{DynamicResource GroupingNode.BackgroundColor}\" />\n\n    <SolidColorBrush x:Key=\"GroupingNode.ForegroundBrush\"\n                     Color=\"{DynamicResource GroupingNode.ForegroundColor}\" />\n\n    <SolidColorBrush x:Key=\"GroupingNode.HeaderBrush\"\n                     Color=\"{DynamicResource GroupingNode.HeaderColor}\" />\n\n    <SolidColorBrush x:Key=\"GroupingNode.BorderBrush\"\n                     Color=\"{DynamicResource GroupingNode.BorderColor}\" />\n\n    <!--KNOT NODE-->\n\n    <SolidColorBrush x:Key=\"KnotNode.BackgroundBrush\"\n                     Opacity=\".5\" \n                     Color=\"{DynamicResource KnotNode.BackgroundColor}\" />\n\n    <SolidColorBrush x:Key=\"KnotNode.ForegroundBrush\"\n                     Color=\"{DynamicResource KnotNode.ForegroundColor}\" />\n\n    <SolidColorBrush x:Key=\"KnotNode.BorderBrush\"\n                     Color=\"{DynamicResource KnotNode.BorderColor}\" />\n\n    <!--CONNECTOR-->\n\n    <SolidColorBrush x:Key=\"Connector.BackgroundBrush\"\n                     Color=\"{DynamicResource Connector.BackgroundColor}\" />\n\n    <SolidColorBrush x:Key=\"Connector.ForegroundBrush\"\n                     Color=\"{DynamicResource Connector.ForegroundColor}\" />\n\n    <SolidColorBrush x:Key=\"Connector.BorderBrush\"\n                     Color=\"{DynamicResource Connector.BorderColor}\" />\n\n    <!--NODE INPUT-->\n\n    <SolidColorBrush x:Key=\"NodeInput.BackgroundBrush\"\n                     Color=\"{DynamicResource NodeInput.BackgroundColor}\" />\n\n    <SolidColorBrush x:Key=\"NodeInput.ForegroundBrush\"\n                     Color=\"{DynamicResource NodeInput.ForegroundColor}\" />\n\n    <SolidColorBrush x:Key=\"NodeInput.BorderBrush\"\n                     Color=\"{DynamicResource NodeInput.BorderColor}\" />\n\n    <!--NODE OUTPUT-->\n\n    <SolidColorBrush x:Key=\"NodeOutput.BackgroundBrush\"\n                     Color=\"{DynamicResource NodeOutput.BackgroundColor}\" />\n\n    <SolidColorBrush x:Key=\"NodeOutput.ForegroundBrush\"\n                     Color=\"{DynamicResource NodeOutput.ForegroundColor}\" />\n\n    <SolidColorBrush x:Key=\"NodeOutput.BorderBrush\"\n                     Color=\"{DynamicResource NodeOutput.BorderColor}\" />\n\n    <!--CONNECTION-->\n\n    <SolidColorBrush x:Key=\"Connection.StrokeBrush\"\n                     Color=\"{DynamicResource Connection.StrokeColor}\" />\n\n    <!--LINE CONNECTION-->\n\n    <SolidColorBrush x:Key=\"LineConnection.StrokeBrush\"\n                     Color=\"{DynamicResource LineConnection.StrokeColor}\" />\n\n    <!--CIRCUIT CONNECTION-->\n\n    <SolidColorBrush x:Key=\"CircuitConnection.StrokeBrush\"\n                     Color=\"{DynamicResource CircuitConnection.StrokeColor}\" />\n\n    <!--STEP CONNECTION-->\n\n    <SolidColorBrush x:Key=\"StepConnection.StrokeBrush\"\n                     Color=\"{DynamicResource StepConnection.StrokeColor}\" />\n\n    <!--PENDING CONNECTION-->\n    \n    <SolidColorBrush x:Key=\"PendingConnection.StrokeBrush\"\n                     Color=\"{DynamicResource PendingConnection.StrokeColor}\" />\n    \n    <SolidColorBrush x:Key=\"PendingConnection.BorderBrush\"\n                     Color=\"{DynamicResource PendingConnection.BorderColor}\" />\n    \n    <SolidColorBrush x:Key=\"PendingConnection.ForegroundBrush\"\n                     Color=\"{DynamicResource PendingConnection.ForegroundColor}\" />\n    \n    <SolidColorBrush x:Key=\"PendingConnection.BackgroundBrush\"\n                     Color=\"{DynamicResource PendingConnection.BackgroundColor}\" />\n\n    <!--MINIMAP-->\n\n    <SolidColorBrush x:Key=\"Minimap.BackgroundBrush\"\n                     Color=\"{DynamicResource Minimap.BackgroundColor}\"\n                     Opacity=\"0.7\" />\n\n    <SolidColorBrush x:Key=\"Minimap.ViewportStrokeBrush\"\n                     Color=\"{DynamicResource Minimap.ViewportStrokeColor}\" />\n\n    <SolidColorBrush x:Key=\"Minimap.ViewportBackgroundBrush\"\n                     Color=\"{DynamicResource Minimap.ViewportBackgroundColor}\"\n                     Opacity=\"0.2\" />\n\n    <SolidColorBrush x:Key=\"MinimapItem.BackgroundBrush\"\n                     Color=\"{DynamicResource MinimapItem.BackgroundColor}\"\n                     Opacity=\"0.8\" />\n\n</ResourceDictionary>"
  },
  {
    "path": "Nodify/Themes/Controls.xaml",
    "content": "﻿<ResourceDictionary xmlns=\"https://github.com/avaloniaui\"\n                    xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n                    xmlns:local=\"clr-namespace:Nodify\">\n\n    <ResourceDictionary.MergedDictionaries>\n        <ResourceInclude Source=\"Brushes.xaml\" />\n    </ResourceDictionary.MergedDictionaries>\n\n    <!--NODIFY EDITOR-->\n\n    <ControlTheme x:Key=\"NodifyEditor.SelectionRectangleStyle\"\n           TargetType=\"Rectangle\">\n        <Setter Property=\"StrokeThickness\"\n                Value=\"1\" />\n        <Setter Property=\"Stroke\"\n                Value=\"{StaticResource NodifyEditor.SelectionRectangleStrokeBrush}\" />\n        <Setter Property=\"Fill\"\n                Value=\"{StaticResource NodifyEditor.SelectionRectangleBackgroundBrush}\" />\n    </ControlTheme>\n\n    <ControlTheme x:Key=\"NodifyEditor.PushedAreaStyle\"\n           TargetType=\"Rectangle\">\n        <Setter Property=\"StrokeThickness\"\n                Value=\"1\" />\n        <Setter Property=\"Stroke\"\n                Value=\"{StaticResource NodifyEditor.PushedAreaStrokeBrush}\" />\n        <Setter Property=\"Fill\"\n                Value=\"{StaticResource NodifyEditor.PushedAreaBackgroundBrush}\" />\n    </ControlTheme>\n\n    <ControlTheme TargetType=\"{x:Type local:NodifyEditor}\" x:Key=\"{x:Type local:NodifyEditor}\"\n                  BasedOn=\"{StaticResource {x:Type local:NodifyEditor}}\">\n        <Setter Property=\"Background\"\n                Value=\"{StaticResource NodifyEditor.BackgroundBrush}\" />\n        <Setter Property=\"Foreground\"\n                Value=\"{StaticResource NodifyEditor.ForegroundBrush}\" />\n        <Setter Property=\"SelectionRectangleStyle\"\n                Value=\"{StaticResource NodifyEditor.SelectionRectangleStyle}\" />\n        <Setter Property=\"PushedAreaStyle\"\n                Value=\"{StaticResource NodifyEditor.PushedAreaStyle}\" />\n    </ControlTheme>\n\n    <!--ITEM CONTAINER-->\n\n    <ControlTheme TargetType=\"{x:Type local:ItemContainer}\" x:Key=\"{x:Type local:ItemContainer}\"\n                  BasedOn=\"{StaticResource {x:Type local:ItemContainer}}\">\n        <Setter Property=\"HighlightBrush\"\n                Value=\"{StaticResource ItemContainer.HighlightBrush}\" />\n        <Setter Property=\"BorderBrush\"\n                Value=\"{DynamicResource ItemContainer.BorderBrush}\" />\n        <Setter Property=\"SelectedBrush\"\n                Value=\"{DynamicResource ItemContainer.SelectedBrush}\" />\n    </ControlTheme>\n    \n    <!--DECORATOR CONTAINER-->\n\n    <ControlTheme TargetType=\"{x:Type local:DecoratorContainer}\" x:Key=\"{x:Type local:DecoratorContainer}\"\n                  BasedOn=\"{StaticResource {x:Type local:DecoratorContainer}}\">\n        <Setter Property=\"RenderTransformOrigin\"\n                Value=\"0,0\" />\n    </ControlTheme>\n\n    <!--NODE-->\n\n    <ControlTheme TargetType=\"{x:Type local:Node}\" x:Key=\"{x:Type local:Node}\"\n           BasedOn=\"{StaticResource {x:Type local:Node}}\">\n        <Setter Property=\"Background\"\n                Value=\"{StaticResource Node.BackgroundBrush}\" />\n        <Setter Property=\"Foreground\"\n                Value=\"{StaticResource Node.ForegroundBrush}\" />\n        <Setter Property=\"ContentBrush\"\n                Value=\"{StaticResource Node.ContentBrush}\" />\n        <Setter Property=\"HeaderBrush\"\n                Value=\"{StaticResource Node.HeaderBrush}\" />\n        <Setter Property=\"FooterBrush\"\n                Value=\"{StaticResource Node.FooterBrush}\" />\n        <Setter Property=\"BorderBrush\"\n                Value=\"{StaticResource Node.BorderBrush}\" />\n        <Style Selector=\"^:has-header\">\n            <Setter Property=\"Foreground\"\n                    Value=\"{StaticResource Node.HeaderForegroundBrush}\" />\n        </Style>\n    </ControlTheme>\n    \n    <!--STATE NODE-->\n\n    <ControlTheme TargetType=\"{x:Type local:StateNode}\" x:Key=\"{x:Type local:StateNode}\"\n           BasedOn=\"{StaticResource {x:Type local:StateNode}}\">\n        <Setter Property=\"Background\"\n                Value=\"{StaticResource StateNode.BackgroundBrush}\" />\n        <Setter Property=\"Foreground\"\n                Value=\"{StaticResource StateNode.ForegroundBrush}\" />\n        <Setter Property=\"BorderBrush\"\n                Value=\"{StaticResource StateNode.BorderBrush}\" />\n        <Setter Property=\"HighlightBrush\"\n                Value=\"{StaticResource StateNode.HighlightBrush}\" />\n    </ControlTheme>\n\n    <!--GROUPING NODE-->\n\n    <ControlTheme TargetType=\"{x:Type local:GroupingNode}\" x:Key=\"{x:Type local:GroupingNode}\"\n           BasedOn=\"{StaticResource {x:Type local:GroupingNode}}\">\n        <Setter Property=\"Background\"\n                Value=\"{StaticResource GroupingNode.BackgroundBrush}\" />\n        <Setter Property=\"Foreground\"\n                Value=\"{StaticResource GroupingNode.ForegroundBrush}\" />\n        <Setter Property=\"HeaderBrush\"\n                Value=\"{StaticResource GroupingNode.HeaderBrush}\" />\n        <Setter Property=\"BorderBrush\"\n                Value=\"{StaticResource GroupingNode.BorderBrush}\" />\n    </ControlTheme>\n\n    <!--KNOT NODE-->\n\n    <ControlTheme TargetType=\"{x:Type local:KnotNode}\" x:Key=\"{x:Type local:KnotNode}\"\n           BasedOn=\"{StaticResource {x:Type local:KnotNode}}\">\n        <Setter Property=\"Background\"\n                Value=\"{StaticResource KnotNode.BackgroundBrush}\" />\n        <Setter Property=\"Foreground\"\n                Value=\"{StaticResource KnotNode.ForegroundBrush}\" />\n        <Setter Property=\"BorderBrush\"\n                Value=\"{StaticResource KnotNode.BorderBrush}\" />\n    </ControlTheme>\n\n    <!--CONNECTOR-->\n\n    <ControlTheme TargetType=\"{x:Type local:Connector}\" x:Key=\"{x:Type local:Connector}\"\n           BasedOn=\"{StaticResource {x:Type local:Connector}}\">\n        <Setter Property=\"Background\"\n                Value=\"{StaticResource Connector.BackgroundBrush}\" />\n        <Setter Property=\"Foreground\"\n                Value=\"{StaticResource Connector.ForegroundBrush}\" />\n        <Setter Property=\"BorderBrush\"\n                Value=\"{StaticResource Connector.BorderBrush}\" />\n    </ControlTheme>\n\n    <!--NODE INPUT-->\n\n    <ControlTheme TargetType=\"{x:Type local:NodeInput}\" x:Key=\"{x:Type local:NodeInput}\"\n           BasedOn=\"{StaticResource {x:Type local:NodeInput}}\">\n        <Setter Property=\"Background\"\n                Value=\"{StaticResource NodeInput.BackgroundBrush}\" />\n        <Setter Property=\"Foreground\"\n                Value=\"{StaticResource NodeInput.ForegroundBrush}\" />\n        <Setter Property=\"BorderBrush\"\n                Value=\"{StaticResource NodeInput.BorderBrush}\" />\n    </ControlTheme>\n\n    <!--NODE OUTPUT-->\n\n    <ControlTheme TargetType=\"{x:Type local:NodeOutput}\" x:Key=\"{x:Type local:NodeOutput}\"\n           BasedOn=\"{StaticResource {x:Type local:NodeOutput}}\">\n        <Setter Property=\"Background\"\n                Value=\"{StaticResource NodeOutput.BackgroundBrush}\" />\n        <Setter Property=\"Foreground\"\n                Value=\"{StaticResource NodeOutput.ForegroundBrush}\" />\n        <Setter Property=\"BorderBrush\"\n                Value=\"{StaticResource NodeOutput.BorderBrush}\" />\n    </ControlTheme>\n\n    <!--CONNECTION-->\n\n    <ControlTheme TargetType=\"{x:Type local:Connection}\" x:Key=\"{x:Type local:Connection}\"\n           BasedOn=\"{StaticResource {x:Type local:Connection}}\">\n        <Setter Property=\"Stroke\"\n                Value=\"{StaticResource Connection.StrokeBrush}\" />\n        <Setter Property=\"Fill\"\n                Value=\"{StaticResource Connection.StrokeBrush}\" />\n    </ControlTheme>\n\n    <!--LINE CONNECTION-->\n\n    <ControlTheme TargetType=\"{x:Type local:LineConnection}\" x:Key=\"{x:Type local:LineConnection}\"\n           BasedOn=\"{StaticResource {x:Type local:LineConnection}}\">\n        <Setter Property=\"Stroke\"\n                Value=\"{StaticResource LineConnection.StrokeBrush}\" />\n        <Setter Property=\"Fill\"\n                Value=\"{StaticResource LineConnection.StrokeBrush}\" />\n    </ControlTheme>\n\n    <!--CIRCUIT CONNECTION-->\n\n    <ControlTheme TargetType=\"{x:Type local:CircuitConnection}\" x:Key=\"{x:Type local:CircuitConnection}\"\n           BasedOn=\"{StaticResource {x:Type local:CircuitConnection}}\">\n        <Setter Property=\"Stroke\"\n                Value=\"{StaticResource CircuitConnection.StrokeBrush}\" />\n        <Setter Property=\"Fill\"\n                Value=\"{StaticResource CircuitConnection.StrokeBrush}\" />\n    </ControlTheme>\n\n    <!--STEP CONNECTION-->\n\n    <ControlTheme TargetType=\"{x:Type local:StepConnection}\" x:Key=\"{x:Type local:StepConnection}\"\n                  BasedOn=\"{StaticResource {x:Type local:StepConnection}}\">\n        <Setter Property=\"Stroke\"\n                Value=\"{StaticResource StepConnection.StrokeBrush}\" />\n        <Setter Property=\"Fill\"\n                Value=\"{StaticResource StepConnection.StrokeBrush}\" />\n    </ControlTheme>\n\n    <!--PENDING CONNECTION-->\n\n    <ControlTheme TargetType=\"{x:Type local:PendingConnection}\" x:Key=\"{x:Type local:PendingConnection}\"\n           BasedOn=\"{StaticResource {x:Type local:PendingConnection}}\">\n        <Setter Property=\"Stroke\"\n                Value=\"{StaticResource PendingConnection.StrokeBrush}\" />\n        <Setter Property=\"BorderBrush\"\n                Value=\"{StaticResource PendingConnection.BorderBrush}\" />\n        <Setter Property=\"Foreground\"\n                Value=\"{StaticResource PendingConnection.ForegroundBrush}\" />\n        <Setter Property=\"Background\"\n                Value=\"{StaticResource PendingConnection.BackgroundBrush}\" />\n    </ControlTheme>\n\n    <!--CUTTING LINE-->\n\n    <ControlTheme TargetType=\"{x:Type local:CuttingLine}\" x:Key=\"{x:Type local:CuttingLine}\"\n           BasedOn=\"{StaticResource {x:Type local:CuttingLine}}\">\n        <Setter Property=\"Fill\"\n                Value=\"{StaticResource NodifyEditor.CuttingLineBackgroundBrush}\" />\n        <Setter Property=\"Stroke\"\n                Value=\"{StaticResource NodifyEditor.CuttingLineStrokeBrush}\" />\n    </ControlTheme>\n\n    <!--MINIMAP-->\n\n    <ControlTheme x:Key=\"Minimap.ViewportStyle\"\n           TargetType=\"Rectangle\">\n        <Setter Property=\"Stroke\"\n                Value=\"{StaticResource Minimap.ViewportStrokeBrush}\" />\n        <Setter Property=\"StrokeThickness\"\n                Value=\"3\" />\n        <Setter Property=\"Fill\"\n                Value=\"{StaticResource Minimap.ViewportBackgroundBrush}\" />\n        <Setter Property=\"StrokeJoin\"\n                Value=\"Round\" />\n        <Setter Property=\"StrokeLineCap\"\n                Value=\"Round\" />\n    </ControlTheme>\n\n    <ControlTheme TargetType=\"{x:Type local:Minimap}\" x:Key=\"{x:Type local:Minimap}\"\n           BasedOn=\"{StaticResource {x:Type local:Minimap}}\">\n        <Setter Property=\"Background\"\n                Value=\"{StaticResource Minimap.BackgroundBrush}\" />\n        <Setter Property=\"ViewportStyle\"\n                Value=\"{StaticResource Minimap.ViewportStyle}\" />\n    </ControlTheme>\n\n    <ControlTheme TargetType=\"{x:Type local:MinimapItem}\" x:Key=\"{x:Type local:MinimapItem}\"\n           BasedOn=\"{StaticResource {x:Type local:MinimapItem}}\">\n        <Setter Property=\"Background\"\n                Value=\"{StaticResource MinimapItem.BackgroundBrush}\" />\n    </ControlTheme>\n\n</ResourceDictionary>\n"
  },
  {
    "path": "Nodify/Themes/Dark.xaml",
    "content": "﻿<ResourceDictionary xmlns=\"https://github.com/avaloniaui\"\n                    xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\">\n\n    <!--NODIFY EDITOR-->\n\n    <Color x:Key=\"NodifyEditor.BackgroundColor\">#1E1E1E</Color>\n    <Color x:Key=\"NodifyEditor.ForegroundColor\">White</Color>\n    <Color x:Key=\"NodifyEditor.SelectionRectangleColor\">DodgerBlue</Color>\n    <Color x:Key=\"NodifyEditor.PushedAreaColor\">#74747c</Color>\n    <Color x:Key=\"NodifyEditor.CuttingLineColor\">Red</Color>\n\n    <!--ITEM CONTAINER-->\n\n    <Color x:Key=\"ItemContainer.BorderColor\">DodgerBlue</Color>\n    <Color x:Key=\"ItemContainer.SelectedColor\">Orange</Color>\n\n    <!--NODE-->\n    <Color x:Key=\"Node.BackgroundColor\">#2D2D30</Color>\n    <Color x:Key=\"Node.ContentColor\">#2D2D30</Color>\n    <Color x:Key=\"Node.ForegroundColor\">White</Color>\n    <Color x:Key=\"Node.HeaderForegroundColor\">White</Color>\n    <Color x:Key=\"Node.HeaderColor\">#1E1E1E</Color>\n    <Color x:Key=\"Node.FooterColor\">#1E1E1E</Color>\n    <Color x:Key=\"Node.BorderColor\">Transparent</Color>\n\n    <!--STATE NODE-->\n    <Color x:Key=\"StateNode.BackgroundColor\">#171717</Color>\n    <Color x:Key=\"StateNode.ForegroundColor\">White</Color>\n    <Color x:Key=\"StateNode.BorderColor\">#484848</Color>\n    <Color x:Key=\"StateNode.HighlightColor\">#D6D3D6</Color>\n\n    <!--GROUPING NODE-->\n    <Color x:Key=\"GroupingNode.BackgroundColor\">#3E3E42</Color>\n    <Color x:Key=\"GroupingNode.ForegroundColor\">White</Color>\n    <Color x:Key=\"GroupingNode.HeaderColor\">#1E1E1E</Color>\n    <Color x:Key=\"GroupingNode.BorderColor\">Transparent</Color>\n\n    <!--KNOT NODE-->\n    <Color x:Key=\"KnotNode.BackgroundColor\">Transparent</Color>\n    <Color x:Key=\"KnotNode.ForegroundColor\">DodgerBlue</Color>\n    <Color x:Key=\"KnotNode.BorderColor\">Transparent</Color>\n\n    <!--CONNECTOR-->\n    <Color x:Key=\"Connector.BackgroundColor\">Transparent</Color>\n    <Color x:Key=\"Connector.ForegroundColor\">White</Color>\n    <Color x:Key=\"Connector.BorderColor\">DodgerBlue</Color>\n\n    <!--NODE INPUT-->\n    <Color x:Key=\"NodeInput.BackgroundColor\">#2D2D30</Color>\n    <Color x:Key=\"NodeInput.ForegroundColor\">White</Color>\n    <Color x:Key=\"NodeInput.BorderColor\">DodgerBlue</Color>\n\n    <!--NODE OUTPUT-->\n    <Color x:Key=\"NodeOutput.BackgroundColor\">#2D2D30</Color>\n    <Color x:Key=\"NodeOutput.ForegroundColor\">White</Color>\n    <Color x:Key=\"NodeOutput.BorderColor\">DodgerBlue</Color>\n\n    <!--CONNECTION-->\n    <Color x:Key=\"Connection.StrokeColor\">DodgerBlue</Color>\n\n    <!--LINE CONNECTION-->\n    <Color x:Key=\"LineConnection.StrokeColor\">DodgerBlue</Color>\n\n    <!--CIRCUIT CONNECTION-->\n    <Color x:Key=\"CircuitConnection.StrokeColor\">DodgerBlue</Color>\n\n    <!--STEP CONNECTION-->\n    <Color x:Key=\"StepConnection.StrokeColor\">DodgerBlue</Color>\n\n    <!--PENDING CONNECTION-->\n    <Color x:Key=\"PendingConnection.StrokeColor\">DodgerBlue</Color>\n    <Color x:Key=\"PendingConnection.BorderColor\">Black</Color>\n    <Color x:Key=\"PendingConnection.ForegroundColor\">White</Color>\n    <Color x:Key=\"PendingConnection.BackgroundColor\">#121212</Color>\n\n    <!--MINIMAP-->\n    <Color x:Key=\"Minimap.BackgroundColor\">#121212</Color>\n    <Color x:Key=\"Minimap.ViewportStrokeColor\">#74747c</Color>\n    <Color x:Key=\"Minimap.ViewportBackgroundColor\">#74747c</Color>\n    <Color x:Key=\"MinimapItem.BackgroundColor\">DodgerBlue</Color>\n    \n</ResourceDictionary>"
  },
  {
    "path": "Nodify/Themes/Generic.xaml",
    "content": "<ResourceDictionary xmlns=\"https://github.com/avaloniaui\"\n                    xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\">\n    \n    <ResourceDictionary.MergedDictionaries>\n        <ResourceInclude Source=\"avares://Nodify/Themes/Styles/Controls.xaml\" />\n        <ResourceInclude Source=\"avares://Nodify/Themes/Controls.xaml\" />\n    </ResourceDictionary.MergedDictionaries>\n\n</ResourceDictionary>\n"
  },
  {
    "path": "Nodify/Themes/Light.xaml",
    "content": "﻿<ResourceDictionary xmlns=\"https://github.com/avaloniaui\"\n                    xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\">\n\n    <!--NODIFY EDITOR-->\n\n    <Color x:Key=\"NodifyEditor.BackgroundColor\">White</Color>\n    <Color x:Key=\"NodifyEditor.ForegroundColor\">Black</Color>\n    <Color x:Key=\"NodifyEditor.SelectionRectangleColor\">DodgerBlue</Color>\n    <Color x:Key=\"NodifyEditor.PushedAreaColor\">#5c6a98</Color>\n    <Color x:Key=\"NodifyEditor.CuttingLineColor\">Red</Color>\n\n    <!--ITEM CONTAINER-->\n\n    <Color x:Key=\"ItemContainer.BorderColor\">#7EB4EA</Color>\n    <Color x:Key=\"ItemContainer.SelectedColor\">Orange</Color>\n\n    <!--NODE-->\n    <Color x:Key=\"Node.BackgroundColor\">#CBCCDF</Color>\n    <Color x:Key=\"Node.ContentColor\">#CBCCDF</Color>\n    <Color x:Key=\"Node.ForegroundColor\">Black</Color>\n    <Color x:Key=\"Node.HeaderForegroundColor\">White</Color>\n    <Color x:Key=\"Node.HeaderColor\">#5C6A98</Color>\n    <Color x:Key=\"Node.FooterColor\">#5C6A98</Color>\n    <Color x:Key=\"Node.BorderColor\">Transparent</Color>\n\n    <!--STATE NODE-->\n    <Color x:Key=\"StateNode.BackgroundColor\">#5C6A98</Color>\n    <Color x:Key=\"StateNode.ForegroundColor\">White</Color>\n    <Color x:Key=\"StateNode.BorderColor\">#CBCCDF</Color>\n    <Color x:Key=\"StateNode.HighlightColor\">#B1C5FF</Color>\n\n    <!--GROUPING NODE-->\n    <Color x:Key=\"GroupingNode.BackgroundColor\">#CBCCDF</Color>\n    <Color x:Key=\"GroupingNode.ForegroundColor\">White</Color>\n    <Color x:Key=\"GroupingNode.HeaderColor\">#5C6A98</Color>\n    <Color x:Key=\"GroupingNode.BorderColor\">Transparent</Color>\n\n    <!--KNOT NODE-->\n    <Color x:Key=\"KnotNode.BackgroundColor\">#CBCCDF</Color>\n    <Color x:Key=\"KnotNode.ForegroundColor\">White</Color>\n    <Color x:Key=\"KnotNode.BorderColor\">Transparent</Color>\n\n    <!--CONNECTOR-->\n    <Color x:Key=\"Connector.BackgroundColor\">#B1C5FF</Color>\n    <Color x:Key=\"Connector.ForegroundColor\">White</Color>\n    <Color x:Key=\"Connector.BorderColor\">#7EB4EA</Color>\n\n    <!--NODE INPUT-->\n    <Color x:Key=\"NodeInput.BackgroundColor\">Transparent</Color>\n    <Color x:Key=\"NodeInput.ForegroundColor\">Black</Color>\n    <Color x:Key=\"NodeInput.BorderColor\">#4B91B8</Color>\n\n    <!--NODE OUTPUT-->\n    <Color x:Key=\"NodeOutput.BackgroundColor\">Transparent</Color>\n    <Color x:Key=\"NodeOutput.ForegroundColor\">Black</Color>\n    <Color x:Key=\"NodeOutput.BorderColor\">#4B91B8</Color>\n\n    <!--CONNECTION-->\n    <Color x:Key=\"Connection.StrokeColor\">#5CA2C9</Color>\n\n    <!--LINE CONNECTION-->\n    <Color x:Key=\"LineConnection.StrokeColor\">#5CA2C9</Color>\n\n    <!--CIRCUIT CONNECTION-->\n    <Color x:Key=\"CircuitConnection.StrokeColor\">#5CA2C9</Color>\n\n    <!--STEP CONNECTION-->\n    <Color x:Key=\"StepConnection.StrokeColor\">#5CA2C9</Color>\n\n    <!--PENDING CONNECTION-->\n    <Color x:Key=\"PendingConnection.StrokeColor\">#5CA2C9</Color>\n    <Color x:Key=\"PendingConnection.BorderColor\">Black</Color>\n    <Color x:Key=\"PendingConnection.ForegroundColor\">White</Color>\n    <Color x:Key=\"PendingConnection.BackgroundColor\">#5C6A98</Color>\n\n    <!--MINIMAP-->\n    <Color x:Key=\"Minimap.BackgroundColor\">#f2f3f5</Color>\n    <Color x:Key=\"Minimap.ViewportStrokeColor\">DodgerBlue</Color>\n    <Color x:Key=\"Minimap.ViewportBackgroundColor\">DodgerBlue</Color>\n    <Color x:Key=\"MinimapItem.BackgroundColor\">#5c6a98</Color>\n    \n</ResourceDictionary>"
  },
  {
    "path": "Nodify/Themes/Nodify.xaml",
    "content": "﻿<ResourceDictionary xmlns=\"https://github.com/avaloniaui\"\n                    xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\">\n\n    <ResourceDictionary.MergedDictionaries>\n        <ResourceInclude Source=\"Controls.xaml\" />\n    </ResourceDictionary.MergedDictionaries>\n\n    <!--NODIFY EDITOR-->\n\n    <Color x:Key=\"NodifyEditor.BackgroundColor\">#332155</Color>\n    <Color x:Key=\"NodifyEditor.ForegroundColor\">#E8E1F3</Color>\n    <Color x:Key=\"NodifyEditor.SelectionRectangleColor\">#FD5618</Color>\n    <Color x:Key=\"NodifyEditor.PushedAreaColor\">#9b44dd</Color>\n    <Color x:Key=\"NodifyEditor.CuttingLineColor\">Red</Color>\n\n    <!--ITEM CONTAINER-->\n\n    <Color x:Key=\"ItemContainer.BorderColor\">#662D91</Color>\n    <Color x:Key=\"ItemContainer.SelectedColor\">#FD5618</Color>\n\n    <!--NODE-->\n    <Color x:Key=\"Node.BackgroundColor\">#662D91</Color>\n    <Color x:Key=\"Node.ContentColor\">#662D91</Color>\n    <Color x:Key=\"Node.ForegroundColor\">#E8E1F3</Color>\n    <Color x:Key=\"Node.HeaderForegroundColor\">#E8E1F3</Color>\n    <Color x:Key=\"Node.HeaderColor\">#451E63</Color>\n    <Color x:Key=\"Node.FooterColor\">#451E63</Color>\n    <Color x:Key=\"Node.BorderColor\">Transparent</Color>\n\n    <!--STATE NODE-->\n    <Color x:Key=\"StateNode.BackgroundColor\">#451E63</Color>\n    <Color x:Key=\"StateNode.ForegroundColor\">#E8E1F3</Color>\n    <Color x:Key=\"StateNode.BorderColor\">#662D91</Color>\n    <Color x:Key=\"StateNode.HighlightColor\">#D6D3D6</Color>\n\n    <!--GROUPING NODE-->\n    <Color x:Key=\"GroupingNode.BackgroundColor\">#2C1D4A</Color>\n    <Color x:Key=\"GroupingNode.ForegroundColor\">#E8E1F3</Color>\n    <Color x:Key=\"GroupingNode.HeaderColor\">#451E63</Color>\n    <Color x:Key=\"GroupingNode.BorderColor\">Transparent</Color>\n\n    <!--KNOT NODE-->\n    <Color x:Key=\"KnotNode.BackgroundColor\">Transparent</Color>\n    <Color x:Key=\"KnotNode.ForegroundColor\">#662D91</Color>\n    <Color x:Key=\"KnotNode.BorderColor\">Transparent</Color>\n\n    <!--CONNECTOR-->\n    <Color x:Key=\"Connector.BackgroundColor\">Transparent</Color>\n    <Color x:Key=\"Connector.ForegroundColor\">#E8E1F3</Color>\n    <Color x:Key=\"Connector.BorderColor\">#FD5618</Color>\n\n    <!--NODE INPUT-->\n    <Color x:Key=\"NodeInput.BackgroundColor\">#662D91</Color>\n    <Color x:Key=\"NodeInput.ForegroundColor\">#E8E1F3</Color>\n    <Color x:Key=\"NodeInput.BorderColor\">#FD5618</Color>\n\n    <!--NODE OUTPUT-->\n    <Color x:Key=\"NodeOutput.BackgroundColor\">#662D91</Color>\n    <Color x:Key=\"NodeOutput.ForegroundColor\">#E8E1F3</Color>\n    <Color x:Key=\"NodeOutput.BorderColor\">#FD5618</Color>\n\n    <!--CONNECTION-->\n    <Color x:Key=\"Connection.StrokeColor\">#FD5618</Color>\n\n    <!--LINE CONNECTION-->\n    <Color x:Key=\"LineConnection.StrokeColor\">#FD5618</Color>\n\n    <!--CIRCUIT CONNECTION-->\n    <Color x:Key=\"CircuitConnection.StrokeColor\">#FD5618</Color>\n\n    <!--STEP CONNECTION-->\n    <Color x:Key=\"StepConnection.StrokeColor\">#FD5618</Color>\n\n    <!--PENDING CONNECTION-->\n    <Color x:Key=\"PendingConnection.StrokeColor\">#FD5618</Color>\n    <Color x:Key=\"PendingConnection.BorderColor\">#451E63</Color>\n    <Color x:Key=\"PendingConnection.ForegroundColor\">#E8E1F3</Color>\n    <Color x:Key=\"PendingConnection.BackgroundColor\">#2A1B47</Color>\n\n    <!--MINIMAP-->\n    <Color x:Key=\"Minimap.BackgroundColor\">#2A1B47</Color>\n    <Color x:Key=\"Minimap.ViewportStrokeColor\">#9b44dd</Color>\n    <Color x:Key=\"Minimap.ViewportBackgroundColor\">#9b44dd</Color>\n    <Color x:Key=\"MinimapItem.BackgroundColor\">#FD5618</Color>\n\n</ResourceDictionary>"
  },
  {
    "path": "Nodify/Themes/NodifyStyle.axaml",
    "content": "<Styles xmlns=\"https://github.com/avaloniaui\"\n        xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\">\n    <Styles.Resources>\n        <ResourceDictionary>\n            <ResourceDictionary.MergedDictionaries>\n                <ResourceInclude Source=\"Styles/Controls.xaml\" />\n                <ResourceInclude Source=\"Nodify.xaml\" />\n            </ResourceDictionary.MergedDictionaries>\n        </ResourceDictionary>\n    </Styles.Resources>\n</Styles>\n"
  },
  {
    "path": "Nodify/Themes/Styles/Connection.xaml",
    "content": "﻿<ResourceDictionary xmlns=\"https://github.com/avaloniaui\"\n                    xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n                    xmlns:local=\"clr-namespace:Nodify\">\n\n    <ControlTheme TargetType=\"{x:Type local:BaseConnection}\" x:Key=\"{x:Type local:BaseConnection}\">\n        <Setter Property=\"Opacity\" Value=\"1\" />\n        <Style Selector=\"^[(local|CuttingLine.IsOverElement)=true]\">\n            <Setter Property=\"Opacity\" Value=\"0.4\" />\n        </Style>\n        <Style Selector=\"^[(local|BaseConnection.IsSelectable)=true]\">\n            <Setter Property=\"Cursor\" Value=\"Hand\" />\n        </Style>\n    </ControlTheme>\n\n    <ControlTheme TargetType=\"{x:Type local:Connection}\"\n           x:Key=\"{x:Type local:Connection}\"\n           BasedOn=\"{StaticResource {x:Type local:BaseConnection}}\">\n        <Setter Property=\"StrokeThickness\"\n                Value=\"3\" />\n        <Setter Property=\"Stroke\"\n                Value=\"DodgerBlue\" />\n        <Setter Property=\"Fill\"\n                Value=\"DodgerBlue\" />\n        <Setter Property=\"Spacing\"\n                Value=\"20\" />\n    </ControlTheme>\n\n    <ControlTheme TargetType=\"{x:Type local:LineConnection}\"\n           x:Key=\"{x:Type local:LineConnection}\"\n           BasedOn=\"{StaticResource {x:Type local:BaseConnection}}\">\n        <Setter Property=\"StrokeThickness\"\n                Value=\"3\" />\n        <Setter Property=\"Stroke\"\n                Value=\"DodgerBlue\" />\n        <Setter Property=\"Fill\"\n                Value=\"DodgerBlue\" />\n        <Setter Property=\"Spacing\"\n                Value=\"30\" />\n    </ControlTheme>\n\n    <ControlTheme TargetType=\"{x:Type local:CircuitConnection}\"\n           x:Key=\"{x:Type local:CircuitConnection}\"\n           BasedOn=\"{StaticResource {x:Type local:BaseConnection}}\">\n        <Setter Property=\"StrokeThickness\"\n                Value=\"3\" />\n        <Setter Property=\"Stroke\"\n                Value=\"DodgerBlue\" />\n        <Setter Property=\"Fill\"\n                Value=\"DodgerBlue\" />\n        <Setter Property=\"Spacing\"\n                Value=\"30\" />\n    </ControlTheme>\n\n    <ControlTheme TargetType=\"{x:Type local:StepConnection}\"\n           x:Key=\"{x:Type local:StepConnection}\"\n           BasedOn=\"{StaticResource {x:Type local:BaseConnection}}\">\n        <Setter Property=\"StrokeThickness\"\n                Value=\"3\" />\n        <Setter Property=\"Stroke\"\n                Value=\"DodgerBlue\" />\n        <Setter Property=\"Fill\"\n                Value=\"DodgerBlue\" />\n        <Setter Property=\"Spacing\"\n                Value=\"30\" />\n    </ControlTheme>\n\n</ResourceDictionary>\n"
  },
  {
    "path": "Nodify/Themes/Styles/Connector.xaml",
    "content": "﻿<ResourceDictionary xmlns=\"https://github.com/avaloniaui\"\n                    xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n                    xmlns:local=\"clr-namespace:Nodify\">\n\n    <ControlTheme TargetType=\"{x:Type local:Connector}\" x:Key=\"{x:Type local:Connector}\">\n        <Setter Property=\"BorderBrush\"\n                Value=\"DodgerBlue\" />\n        <Setter Property=\"Foreground\"\n                Value=\"White\" />\n        <Setter Property=\"Background\"\n                Value=\"Transparent\" />\n        <Setter Property=\"Width\"\n                Value=\"14\" />\n        <Setter Property=\"Height\"\n                Value=\"14\" />\n        <Setter Property=\"Template\">\n            <Setter.Value>\n                <ControlTemplate TargetType=\"{x:Type local:Connector}\">\n                    <Ellipse x:Name=\"Connector\"\n                             Width=\"{TemplateBinding Width}\"\n                             Height=\"{TemplateBinding Height}\"\n                             Stroke=\"{TemplateBinding BorderBrush}\"\n                             Fill=\"Transparent\"\n                             StrokeThickness=\"2\" />\n                </ControlTemplate>\n            </Setter.Value>\n        </Setter>\n        <Style Selector=\"^:connected /template/ Ellipse#Connector\">\n            <Setter Property=\"Ellipse.Fill\"\n                    Value=\"{Binding BorderBrush, RelativeSource={RelativeSource TemplatedParent}}\" />\n        </Style>\n        <Style Selector=\"^.isOverElement /template/ Ellipse#Connector\">\n            <Setter Property=\"Ellipse.Fill\"\n                    Value=\"{Binding BorderBrush, RelativeSource={RelativeSource TemplatedParent}}\" />\n        </Style>\n    </ControlTheme>\n\n</ResourceDictionary>"
  },
  {
    "path": "Nodify/Themes/Styles/Controls.xaml",
    "content": "﻿<ResourceDictionary xmlns=\"https://github.com/avaloniaui\">\n    <ResourceDictionary.MergedDictionaries>\n        <!--NODES-->\n        <MergeResourceInclude Source=\"/Themes/Styles/Node.xaml\" />\n        <MergeResourceInclude Source=\"/Themes/Styles/KnotNode.xaml\" />\n        <MergeResourceInclude Source=\"/Themes/Styles/GroupingNode.xaml\" />\n        <MergeResourceInclude Source=\"/Themes/Styles/StateNode.xaml\" />\n        <MergeResourceInclude Source=\"/Themes/Styles/Connector.xaml\" />\n        <MergeResourceInclude Source=\"/Themes/Styles/NodeInput.xaml\" />\n        <MergeResourceInclude Source=\"/Themes/Styles/NodeOutput.xaml\" />\n        <MergeResourceInclude Source=\"/Themes/Styles/Connection.xaml\" />\n        <MergeResourceInclude Source=\"/Themes/Styles/PendingConnection.xaml\" />\n        <MergeResourceInclude Source=\"/Themes/Styles/CuttingLine.xaml\" />\n\n        <!--CORE-->\n        <MergeResourceInclude Source=\"/Themes/Styles/ItemContainer.xaml\" />\n        <MergeResourceInclude Source=\"/Themes/Styles/DecoratorContainer.xaml\" />\n        <MergeResourceInclude Source=\"/Themes/Styles/NodifyEditor.xaml\" />\n        \n        <!--WIDGETS-->\n        <MergeResourceInclude Source=\"/Themes/Styles/Minimap.xaml\" />\n    </ResourceDictionary.MergedDictionaries>\n</ResourceDictionary>\n"
  },
  {
    "path": "Nodify/Themes/Styles/CuttingLine.xaml",
    "content": "﻿<ResourceDictionary xmlns=\"https://github.com/avaloniaui\"\n                    xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n                    xmlns:local=\"clr-namespace:Nodify\">\n\n    <ControlTheme TargetType=\"{x:Type local:CuttingLine}\" x:Key=\"{x:Type local:CuttingLine}\">\n        <Setter Property=\"StrokeThickness\"\n                Value=\"3\" />\n        <Setter Property=\"StrokeDashArray\"\n                Value=\"2,1\" />\n        <Setter Property=\"Stroke\"\n                Value=\"Red\" />\n        <Setter Property=\"Fill\"\n                Value=\"Red\" />\n    </ControlTheme>\n    \n</ResourceDictionary>"
  },
  {
    "path": "Nodify/Themes/Styles/DecoratorContainer.xaml",
    "content": "﻿<ResourceDictionary xmlns=\"https://github.com/avaloniaui\"\n                    xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n                    xmlns:local=\"clr-namespace:Nodify\">\n\n    <ControlTheme TargetType=\"{x:Type local:DecoratorContainer}\" x:Key=\"{x:Type local:DecoratorContainer}\">\n        <Setter Property=\"KeyboardNavigation.IsTabStop\"\n                Value=\"False\" />\n        <Setter Property=\"RenderTransform\"\n                Value=\"{Binding ViewportTransform, RelativeSource={RelativeSource AncestorType=local:NodifyEditor}, Converter={StaticResource UnscaleTransformConverter}}\" />\n        <Setter Property=\"Template\">\n            <Setter.Value>\n                <ControlTemplate TargetType=\"{x:Type local:DecoratorContainer}\">\n                    <Border Background=\"{TemplateBinding Background}\"\n                            BorderThickness=\"{TemplateBinding BorderThickness}\"\n                            BorderBrush=\"{TemplateBinding BorderBrush}\"\n                            Padding=\"{TemplateBinding Padding}\"\n                            CornerRadius=\"3\">\n                        <ContentPresenter Content=\"{TemplateBinding Content}\" ContentTemplate=\"{TemplateBinding ContentTemplate}\" />\n                    </Border>\n                </ControlTemplate>\n            </Setter.Value>\n        </Setter>\n    </ControlTheme>\n\n</ResourceDictionary>\n"
  },
  {
    "path": "Nodify/Themes/Styles/GroupingNode.xaml",
    "content": "﻿<ResourceDictionary xmlns=\"https://github.com/avaloniaui\"\n                    xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n                    xmlns:local=\"clr-namespace:Nodify\">\n\n    <ControlTheme TargetType=\"{x:Type local:GroupingNode}\" x:Key=\"{x:Type local:GroupingNode}\">\n        <Setter Property=\"Background\">\n            <Setter.Value>\n                <SolidColorBrush Color=\"#3E3E42\"\n                                 Opacity=\".5\" />\n            </Setter.Value>\n        </Setter>\n        <Setter Property=\"Foreground\"\n                Value=\"White\" />\n        <Setter Property=\"HeaderBrush\"\n                Value=\"#1E1E1E\" />\n        <Setter Property=\"BorderBrush\"\n                Value=\"Transparent\" />\n        <Setter Property=\"Padding\"\n                Value=\"7 3\" />\n        <Setter Property=\"VerticalAlignment\"\n                Value=\"Center\" />\n        <Setter Property=\"HorizontalAlignment\"\n                Value=\"Center\" />\n        <Setter Property=\"MinHeight\"\n                Value=\"30\" />\n        <Setter Property=\"MinWidth\"\n                Value=\"150\" />\n        <Setter Property=\"Template\">\n            <Setter.Value>\n                <ControlTemplate TargetType=\"{x:Type local:GroupingNode}\">\n                    <Border BorderBrush=\"{TemplateBinding BorderBrush}\"\n                            BorderThickness=\"{TemplateBinding BorderThickness}\"\n                            CornerRadius=\"3\">\n                        <Grid>\n                            <Grid.RowDefinitions>\n                                <RowDefinition Height=\"Auto\"\n                                               MinHeight=\"30\" />\n                                <RowDefinition Height=\"*\" />\n                            </Grid.RowDefinitions>\n\n                            <Border Background=\"{TemplateBinding HeaderBrush}\"\n                                    Cursor=\"SizeAll\"\n                                    CornerRadius=\"3 3 0 0\"\n                                    x:Name=\"PART_Header\">\n                                <ContentPresenter Content=\"{TemplateBinding Header}\"\n                                                  ContentTemplate=\"{TemplateBinding HeaderTemplate}\"\n                                                  Margin=\"{TemplateBinding Padding}\" />\n                            </Border>\n\n                            <Grid Grid.Row=\"1\"\n                                  Background=\"{TemplateBinding Background}\">\n                                <ContentPresenter x:Name=\"PART_Content\"\n                                                  ContentTemplate=\"{TemplateBinding ContentTemplate}\"\n                                                  Content=\"{TemplateBinding Content}\" />\n                                <Thumb x:Name=\"PART_ResizeThumb\"\n                                       HorizontalAlignment=\"Right\"\n                                       VerticalAlignment=\"Bottom\"\n                                       Margin=\"0 0 2 2\"\n                                       MinHeight=\"20\"\n                                       Cursor=\"SizeAll\"\n                                       Foreground=\"{TemplateBinding Foreground}\"\n                                       IsVisible=\"{TemplateBinding CanResize}\">\n                                    <Thumb.Template>\n                                        <ControlTemplate>\n                                            <TextBlock Text=\"p\"\n                                                       FontFamily=\"Marlett\"\n                                                       FontSize=\"18\" />\n                                        </ControlTemplate>\n                                    </Thumb.Template>\n                                </Thumb>\n                            </Grid>\n                        </Grid>\n                    </Border>\n                </ControlTemplate>\n            </Setter.Value>\n        </Setter>\n    </ControlTheme>\n\n</ResourceDictionary>"
  },
  {
    "path": "Nodify/Themes/Styles/ItemContainer.xaml",
    "content": "﻿<ResourceDictionary xmlns=\"https://github.com/avaloniaui\"\n                    xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n                    xmlns:local=\"clr-namespace:Nodify\">\n\n    <SolidColorBrush x:Key=\"ItemContainer.HighlightColor\"\n                     Color=\"DodgerBlue\" />\n\n    <DrawingBrush x:Key=\"ItemContainer.HighlightBrush\"\n                  DestinationRect=\"0 0 24 24\"\n                  TileMode=\"Tile\">\n        <DrawingBrush.Drawing>\n            <DrawingGroup>\n                <GeometryDrawing Brush=\"{StaticResource ItemContainer.HighlightColor}\">\n                    <GeometryDrawing.Geometry>\n                        <GeometryGroup>\n                            <RectangleGeometry Rect=\"0 0 50 50\" />\n                            <RectangleGeometry Rect=\"50 50 50 50\" />\n                        </GeometryGroup>\n                    </GeometryDrawing.Geometry>\n                </GeometryDrawing>\n            </DrawingGroup>\n        </DrawingBrush.Drawing>\n    </DrawingBrush>\n\n    <ControlTheme TargetType=\"{x:Type local:ItemContainer}\" x:Key=\"{x:Type local:ItemContainer}\">\n        <Setter Property=\"BorderThickness\"\n                Value=\"1\" />\n        <Setter Property=\"SelectedBorderThickness\"\n                Value=\"2\" />\n        <Setter Property=\"BorderBrush\"\n                Value=\"DodgerBlue\" />\n        <Setter Property=\"SelectedBrush\"\n                Value=\"Orange\" />\n        <Setter Property=\"HighlightBrush\"\n                Value=\"{StaticResource ItemContainer.HighlightBrush}\" />\n        <Setter Property=\"KeyboardNavigation.IsTabStop\"\n                Value=\"False\" />\n        <Setter Property=\"Template\">\n            <Setter.Value>\n                <ControlTemplate TargetType=\"{x:Type local:ItemContainer}\">\n                    <Border Background=\"{TemplateBinding Background}\"\n                            BorderThickness=\"{TemplateBinding BorderThickness}\"\n                            BorderBrush=\"{TemplateBinding BorderBrush}\"\n                            Padding=\"{TemplateBinding Padding}\"\n                            x:Name=\"Border\"\n                            CornerRadius=\"3\">\n                        <ContentPresenter Content=\"{TemplateBinding Content}\" ContentTemplate=\"{TemplateBinding ContentTemplate}\" />\n                    </Border>\n                </ControlTemplate>\n            </Setter.Value>\n        </Setter>\n        <Style Selector=\"^:selected:null-previewing-selection\">\n            <Setter Property=\"BorderBrush\"\n                    Value=\"{Binding SelectedBrush, RelativeSource={RelativeSource Self}}\" />\n            <Setter Property=\"Margin\"\n                    Value=\"{Binding SelectedMargin, RelativeSource={RelativeSource Self}}\" />\n            <Setter Property=\"BorderThickness\"\n                    Value=\"{Binding SelectedBorderThickness, RelativeSource={RelativeSource Self}}\" />\n        </Style>\n        <Style Selector=\"^:previewing-selection\">\n            <Setter Property=\"BorderBrush\"\n                    Value=\"{Binding SelectedBrush, RelativeSource={RelativeSource Self}}\" />\n            <Setter Property=\"Margin\"\n                    Value=\"{Binding SelectedMargin, RelativeSource={RelativeSource Self}}\" />\n            <Setter Property=\"BorderThickness\"\n                    Value=\"{Binding SelectedBorderThickness, RelativeSource={RelativeSource Self}}\" />\n        </Style>\n        <Style Selector=\"^.isOverElement\">\n            <Setter Property=\"BorderBrush\"\n                    Value=\"{Binding HighlightBrush, RelativeSource={RelativeSource Self}}\" />\n            <Setter Property=\"Margin\"\n                    Value=\"{Binding SelectedMargin, RelativeSource={RelativeSource Self}}\" />\n            <Setter Property=\"BorderThickness\"\n                    Value=\"{Binding SelectedBorderThickness, RelativeSource={RelativeSource Self}}\" />\n        </Style>\n    </ControlTheme>\n\n</ResourceDictionary>"
  },
  {
    "path": "Nodify/Themes/Styles/KnotNode.xaml",
    "content": "﻿<ResourceDictionary xmlns=\"https://github.com/avaloniaui\"\n                    xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n                    xmlns:local=\"clr-namespace:Nodify\">\n\n    <DataTemplate x:Key=\"DefaultConnectorTemplate\">\n        <local:Connector Focusable=\"False\"\n                         Cursor=\"Arrow\" />\n    </DataTemplate>\n\n    <ControlTheme TargetType=\"{x:Type local:KnotNode}\" x:Key=\"{x:Type local:KnotNode}\">\n        <Setter Property=\"Background\"\n                Value=\"Transparent\" />\n        <Setter Property=\"Foreground\"\n                Value=\"DodgerBlue\" />\n        <Setter Property=\"BorderBrush\"\n                Value=\"Transparent\" />\n        <Setter Property=\"Padding\"\n                Value=\"15 5\" />\n        <Setter Property=\"Cursor\"\n                Value=\"SizeAll\" />\n        <Setter Property=\"ContentTemplate\"\n                Value=\"{StaticResource DefaultConnectorTemplate}\" />\n        <Setter Property=\"Template\">\n            <Setter.Value>\n                <ControlTemplate TargetType=\"{x:Type local:KnotNode}\">\n                    <Border Background=\"{TemplateBinding Background}\"\n                            BorderBrush=\"{TemplateBinding BorderBrush}\"\n                            BorderThickness=\"{TemplateBinding BorderThickness}\"\n                            Padding=\"{TemplateBinding Padding}\"\n                            CornerRadius=\"3\">\n                        <ContentPresenter Content=\"{TemplateBinding Content}\" ContentTemplate=\"{TemplateBinding ContentTemplate}\" />\n                    </Border>\n                </ControlTemplate>\n            </Setter.Value>\n        </Setter>\n    </ControlTheme>\n\n</ResourceDictionary>"
  },
  {
    "path": "Nodify/Themes/Styles/Minimap.xaml",
    "content": "﻿<ResourceDictionary xmlns=\"https://github.com/avaloniaui\"\n                    xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n                    xmlns:local=\"clr-namespace:Nodify\">\n\n    <local:SubtractConverter x:Key=\"SubtractConverter\" />\n\n    <SolidColorBrush x:Key=\"MinimapBackground\"\n                     Color=\"#121212\"\n                     Opacity=\"0.7\" />\n    <SolidColorBrush x:Key=\"MinimapItemBackground\"\n                     Color=\"DodgerBlue\"\n                     Opacity=\"0.8\" />\n\n    <ControlTheme x:Key=\"ViewportRectStyle\"\n           TargetType=\"Rectangle\">\n        <Setter Property=\"Stroke\"\n                Value=\"#74747c\" />\n        <Setter Property=\"StrokeThickness\"\n                Value=\"3\" />\n        <Setter Property=\"Fill\">\n            <Setter.Value>\n                <SolidColorBrush Color=\"#74747c\"\n                                 Opacity=\"0.2\" />\n            </Setter.Value>\n        </Setter>\n    </ControlTheme>\n\n    <ControlTheme TargetType=\"{x:Type local:Minimap}\" x:Key=\"{x:Type local:Minimap}\">\n        <Setter Property=\"Padding\"\n                Value=\"10\" />\n        <Setter Property=\"Background\"\n                Value=\"{StaticResource MinimapBackground}\" />\n        <Setter Property=\"ViewportStyle\"\n                Value=\"{StaticResource ViewportRectStyle}\" />\n        <Setter Property=\"Template\">\n            <Setter.Value>\n                <ControlTemplate TargetType=\"{x:Type local:Minimap}\">\n                    <Border Background=\"{TemplateBinding Background}\"\n                            BorderThickness=\"{TemplateBinding BorderThickness}\"\n                            BorderBrush=\"{TemplateBinding BorderBrush}\"\n                            CornerRadius=\"3\">\n                        <Viewbox Stretch=\"Uniform\"\n                                 Margin=\"{TemplateBinding Padding}\">\n                            <Grid>\n                                <ItemsPresenter Name=\"PART_ItemsPresenter\">\n                                    <ItemsPresenter.ItemsPanel>\n                                        <ItemsPanelTemplate>\n                                            <local:MinimapPanel ViewportSize=\"{TemplateBinding ViewportSize}\"\n                                                                ViewportLocation=\"{TemplateBinding ViewportLocation}\"\n                                                                ResizeToViewport=\"{TemplateBinding ResizeToViewport}\"\n                                                                Extent=\"{Binding Extent, RelativeSource={RelativeSource TemplatedParent}, Mode=OneWayToSource}\"\n                                                                ItemsExtent=\"{Binding ItemsExtent, RelativeSource={RelativeSource TemplatedParent}, Mode=OneWayToSource}\" />\n                                        </ItemsPanelTemplate>\n                                    </ItemsPresenter.ItemsPanel>\n                                </ItemsPresenter>\n\n                                <Canvas>\n                                    <Rectangle  Theme=\"{TemplateBinding ViewportStyle}\"\n                                                Width=\"{Binding ViewportSize.Width, RelativeSource={RelativeSource TemplatedParent}}\"\n                                                Height=\"{Binding ViewportSize.Height, RelativeSource={RelativeSource TemplatedParent}}\">\n                                        <Canvas.Top>\n                                            <MultiBinding Converter=\"{StaticResource SubtractConverter}\">\n                                                <Binding Path=\"ViewportLocation.Y\"\n                                                         RelativeSource=\"{RelativeSource TemplatedParent}\" />\n                                                <Binding Path=\"Extent.Position.Y\"\n                                                         RelativeSource=\"{RelativeSource TemplatedParent}\" />\n                                            </MultiBinding>\n                                        </Canvas.Top>\n                                        <Canvas.Left>\n                                            <MultiBinding Converter=\"{StaticResource SubtractConverter}\">\n                                                <Binding Path=\"ViewportLocation.X\"\n                                                         RelativeSource=\"{RelativeSource TemplatedParent}\" />\n                                                <Binding Path=\"Extent.Position.X\"\n                                                         RelativeSource=\"{RelativeSource TemplatedParent}\" />\n                                            </MultiBinding>\n                                        </Canvas.Left>\n                                    </Rectangle>\n                                </Canvas>\n                            </Grid>\n                        </Viewbox>\n                    </Border>\n                </ControlTemplate>\n            </Setter.Value>\n        </Setter>\n    </ControlTheme>\n\n    <ControlTheme TargetType=\"{x:Type local:MinimapItem}\" x:Key=\"{x:Type local:MinimapItem}\">\n        <Setter Property=\"Background\"\n                Value=\"{StaticResource MinimapItemBackground}\" />\n        <Setter Property=\"Template\">\n            <Setter.Value>\n                <ControlTemplate TargetType=\"{x:Type local:MinimapItem}\">\n                    <Border Background=\"{TemplateBinding Background}\"\n                            BorderThickness=\"{TemplateBinding BorderThickness}\"\n                            BorderBrush=\"{TemplateBinding BorderBrush}\"\n                            Padding=\"{TemplateBinding Padding}\"\n                            CornerRadius=\"3\">\n                        <ContentPresenter Content=\"{TemplateBinding Content}\" ContentTemplate=\"{TemplateBinding ContentTemplate}\" />\n                    </Border>\n                </ControlTemplate>\n            </Setter.Value>\n        </Setter>\n    </ControlTheme>\n\n</ResourceDictionary>"
  },
  {
    "path": "Nodify/Themes/Styles/Node.xaml",
    "content": "﻿<ResourceDictionary xmlns=\"https://github.com/avaloniaui\"\n                    xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n                    xmlns:local=\"clr-namespace:Nodify\">\n\n    <DataTemplate x:Key=\"DefaultInputConnectorTemplate\">\n        <local:NodeInput />\n    </DataTemplate>\n\n    <DataTemplate x:Key=\"DefaultOutputConnectorTemplate\">\n        <local:NodeOutput />\n    </DataTemplate>\n\n    <ControlTheme x:Key=\"DefaultHeaderContainerStyle\"\n           TargetType=\"Border\">\n        <Setter Property=\"CornerRadius\"\n                Value=\"3 3 0 0\" />\n        <Setter Property=\"Padding\"\n                Value=\"6 4 6 4\" />\n    </ControlTheme>\n\n    <ControlTheme x:Key=\"DefaultFooterContainerStyle\"\n           TargetType=\"Border\">\n        <Setter Property=\"CornerRadius\"\n                Value=\"0 0 3 3\" />\n        <Setter Property=\"Padding\"\n                Value=\"6 4 6 4\" />\n    </ControlTheme>\n\n    <ControlTheme TargetType=\"{x:Type local:Node}\" x:Key=\"{x:Type local:Node}\">\n        <Setter Property=\"Background\"\n                Value=\"#2D2D30\" />\n        <Setter Property=\"ContentBrush\"\n                Value=\"#2D2D30\" />\n        <Setter Property=\"HeaderBrush\"\n                Value=\"#1E1E1E\" />\n        <Setter Property=\"FooterBrush\"\n                Value=\"#1E1E1E\" />\n        <Setter Property=\"Foreground\"\n                Value=\"White\" />\n        <Setter Property=\"BorderBrush\"\n                Value=\"Transparent\" />\n        <Setter Property=\"MinHeight\"\n                Value=\"15\" />\n        <Setter Property=\"MinWidth\"\n                Value=\"15\" />\n        <Setter Property=\"VerticalAlignment\"\n                Value=\"Top\" />\n        <Setter Property=\"HorizontalAlignment\"\n                Value=\"Center\" />\n        <Setter Property=\"VerticalContentAlignment\"\n                Value=\"Center\" />\n        <Setter Property=\"HorizontalContentAlignment\"\n                Value=\"Center\" />\n        <Setter Property=\"InputConnectorTemplate\"\n                Value=\"{StaticResource DefaultInputConnectorTemplate}\" />\n        <Setter Property=\"OutputConnectorTemplate\"\n                Value=\"{StaticResource DefaultOutputConnectorTemplate}\" />\n        <Setter Property=\"HeaderContainerStyle\"\n                Value=\"{StaticResource DefaultHeaderContainerStyle}\" />\n        <Setter Property=\"FooterContainerStyle\"\n                Value=\"{StaticResource DefaultFooterContainerStyle}\" />\n        <Setter Property=\"Template\">\n            <Setter.Value>\n                <ControlTemplate TargetType=\"{x:Type local:Node}\">\n                    <Border Background=\"{TemplateBinding Background}\"\n                            BorderBrush=\"{TemplateBinding BorderBrush}\"\n                            BorderThickness=\"{TemplateBinding BorderThickness}\"\n                            Padding=\"{TemplateBinding Padding}\"\n                            CornerRadius=\"3\">\n                        <Grid>\n                            <Grid.RowDefinitions>\n                                <RowDefinition Height=\"Auto\" />\n                                <RowDefinition Height=\"*\" />\n                                <RowDefinition Height=\"Auto\" />\n                            </Grid.RowDefinitions>\n\n                            <!--Header-->\n                            <Border IsVisible=\"{TemplateBinding Header, Converter={x:Static ObjectConverters.IsNotNull}}\"\n                                    Name=\"PART_Header\"\n                                    Background=\"{TemplateBinding HeaderBrush}\"\n                                    Theme=\"{TemplateBinding HeaderContainerStyle}\"\n                                    Grid.ColumnSpan=\"3\">\n                                <ContentPresenter Content=\"{TemplateBinding Header}\" ContentTemplate=\"{TemplateBinding HeaderTemplate}\" />\n                            </Border>\n\n                            <!--Body-->\n                            <Border Theme=\"{TemplateBinding ContentContainerStyle}\"\n                                    Grid.Row=\"1\">\n                                <Grid>\n                                    <Grid.ColumnDefinitions>\n                                        <ColumnDefinition Width=\"Auto\" />\n                                        <ColumnDefinition Width=\"*\" />\n                                        <ColumnDefinition Width=\"Auto\" />\n                                    </Grid.ColumnDefinitions>\n\n                                    <!--Data IN-->\n                                    <ItemsControl x:Name=\"PART_Input\"\n                                                  ItemsSource=\"{TemplateBinding Input}\"\n                                                  VerticalAlignment=\"{TemplateBinding VerticalAlignment}\"\n                                                  ItemTemplate=\"{TemplateBinding InputConnectorTemplate}\"\n                                                  Focusable=\"False\" />\n\n                                    <!--Content-->\n                                    <Border Grid.Column=\"1\" \n                                            Padding=\"16 0 16 0\"\n                                            Background=\"{TemplateBinding ContentBrush}\">\n                                        <ContentPresenter VerticalAlignment=\"{TemplateBinding VerticalContentAlignment}\"\n                                                          HorizontalAlignment=\"{TemplateBinding HorizontalContentAlignment}\"\n                                                          Content=\"{TemplateBinding Content}\"\n                                                          ContentTemplate=\"{TemplateBinding ContentTemplate}\"/>\n                                    </Border>\n\n                                    <!--Data OUT-->\n                                    <!-- HorizontalContentAlignment=\"Right\" -->\n                                    <ItemsControl x:Name=\"PART_Output\"\n                                                  ItemsSource=\"{TemplateBinding Output}\"\n                                                  ItemTemplate=\"{TemplateBinding OutputConnectorTemplate}\"\n                                                  VerticalAlignment=\"{TemplateBinding VerticalAlignment}\"\n                                                  Grid.Column=\"2\"\n                                                  Focusable=\"False\" />\n                                </Grid>\n                            </Border>\n\n                            <!--Footer-->\n                            <Border IsVisible=\"{TemplateBinding HasFooter}\"\n                                    Background=\"{TemplateBinding FooterBrush}\"\n                                    Theme=\"{TemplateBinding FooterContainerStyle}\"\n                                    Grid.Row=\"2\"\n                                    Grid.ColumnSpan=\"3\">\n                                <ContentControl Content=\"{TemplateBinding Footer}\" ContentTemplate=\"{TemplateBinding FooterTemplate}\" />\n                            </Border>\n                        </Grid>\n                    </Border>\n                </ControlTemplate>\n            </Setter.Value>\n        </Setter>\n        <Style Selector=\"^ /template/ Border#PART_Header\">\n            <Setter Property=\"IsVisible\" Value=\"False\" />\n        </Style>\n        <Style Selector=\"^:has-header /template/ Border#PART_Header\">\n            <Setter Property=\"IsVisible\" Value=\"True\" />\n        </Style>\n    </ControlTheme>\n\n</ResourceDictionary>\n"
  },
  {
    "path": "Nodify/Themes/Styles/NodeInput.xaml",
    "content": "﻿<ResourceDictionary xmlns=\"https://github.com/avaloniaui\"\n                    xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n                    xmlns:local=\"clr-namespace:Nodify\">\n\n    <ControlTemplate x:Key=\"InputConnectorThumbTemplate\"\n                     TargetType=\"TemplatedControl\">\n        <Ellipse Width=\"14\"\n                 Height=\"14\"\n                 Stroke=\"{TemplateBinding BorderBrush}\"\n                 Fill=\"{TemplateBinding Background}\"\n                 StrokeThickness=\"2\" />\n    </ControlTemplate>\n\n    <LinearGradientBrush x:Key=\"InputFadeOpacityMask\"\n                         StartPoint=\"0% 0%\"\n                         EndPoint=\"100% 0%\">\n        <GradientStop Color=\"#22FFFFFF\"\n                      Offset=\"0\" />\n        <GradientStop Color=\"#88FFFFFF\"\n                      Offset=\"0.3\" />\n        <GradientStop Color=\"#88FFFFFF\"\n                      Offset=\"0.7\" />\n        <GradientStop Color=\"#22FFFFFF\"\n                      Offset=\"1\" />\n    </LinearGradientBrush>\n\n    <LinearGradientBrush x:Key=\"FadeOpacityMaskVertical\"\n                         StartPoint=\"0% 0%\"\n                         EndPoint=\"0% 100%\">\n        <GradientStop Color=\"#22FFFFFF\"\n                      Offset=\"0\" />\n        <GradientStop Color=\"#88FFFFFF\"\n                      Offset=\"0.3\" />\n        <GradientStop Color=\"#88FFFFFF\"\n                      Offset=\"0.7\" />\n        <GradientStop Color=\"#22FFFFFF\"\n                      Offset=\"1\" />\n    </LinearGradientBrush>\n\n    <ControlTheme TargetType=\"{x:Type local:NodeInput}\" x:Key=\"{x:Type local:NodeInput}\">\n        <Setter Property=\"BorderBrush\"\n                Value=\"DodgerBlue\" />\n        <Setter Property=\"Background\"\n                Value=\"#2D2D30\" />\n        <Setter Property=\"Foreground\"\n                Value=\"White\" />\n        <Setter Property=\"Padding\"\n                Value=\"4 2\" />\n        <Setter Property=\"ConnectorTemplate\"\n                Value=\"{StaticResource InputConnectorThumbTemplate}\" />\n        <Setter Property=\"Template\">\n            <Setter.Value>\n                <ControlTemplate TargetType=\"{x:Type local:NodeInput}\">\n                    <Grid Background=\"{TemplateBinding Background}\">\n                        <Border IsVisible=\"False\"\n                                x:Name=\"Highlight\"\n                                OpacityMask=\"{StaticResource InputFadeOpacityMask}\"\n                                Background=\"{TemplateBinding BorderBrush}\" />\n\n                        <StackPanel Orientation=\"{TemplateBinding Orientation}\"\n                                    Margin=\"{TemplateBinding Padding}\">\n\n                            <TemplatedControl x:Name=\"PART_Connector\"\n                                     Focusable=\"False\"\n                                     VerticalAlignment=\"Center\"\n                                     Background=\"Transparent\"\n                                     BorderBrush=\"{TemplateBinding BorderBrush}\"\n                                     Template=\"{TemplateBinding ConnectorTemplate}\" />\n\n                            <ContentControl Content=\"{TemplateBinding Header}\" ContentTemplate=\"{TemplateBinding HeaderTemplate}\" />\n                        </StackPanel>\n                    </Grid>\n                </ControlTemplate>\n            </Setter.Value>\n        </Setter>\n        <Style Selector=\"^ /template/ TemplatedControl#PART_Connector\">\n            <Setter Property=\"Margin\"\n                    Value=\"0 0 5 0\" />\n        </Style>\n        <Style Selector=\"^:vertical\">\n            <Setter Property=\"Padding\"\n                    Value=\"2 4\" />\n        </Style>\n        <Style Selector=\"^:vertical /template/ TemplatedControl#PART_Connector\">\n            <Setter Property=\"Margin\"\n                    Value=\"0 0 0 5\" />\n        </Style>\n        <Style Selector=\"^:vertical /template/ Border#Highlight\">\n            <Setter Property=\"OpacityMask\"\n                    Value=\"{StaticResource FadeOpacityMaskVertical}\" />\n        </Style>\n        <Style Selector=\"^:connected /template/ TemplatedControl#PART_Connector\">\n            <Setter Property=\"Background\"\n                    Value=\"{Binding BorderBrush, RelativeSource={RelativeSource TemplatedParent}}\" />\n        </Style>\n        <Style Selector=\"^.isOverElement /template/ TemplatedControl#PART_Connector\">\n            <Setter Property=\"Background\"\n                    Value=\"{Binding BorderBrush, RelativeSource={RelativeSource TemplatedParent}}\" />\n        </Style>\n        <Style Selector=\"^:pointerover /template/ Border#Highlight\">\n            <Setter Property=\"IsVisible\"\n                    Value=\"True\" />\n        </Style>\n        <Style Selector=\"^.isOverElement /template/ Border#Highlight\">\n            <Setter Property=\"IsVisible\"\n                    Value=\"True\" />\n        </Style>\n    </ControlTheme>\n\n</ResourceDictionary>\n"
  },
  {
    "path": "Nodify/Themes/Styles/NodeOutput.xaml",
    "content": "﻿<ResourceDictionary xmlns=\"https://github.com/avaloniaui\"\n                    xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n                    xmlns:local=\"clr-namespace:Nodify\">\n\n    <ControlTemplate x:Key=\"OutputConnectorThumbTemplate\"\n                     TargetType=\"TemplatedControl\">\n        <Ellipse Width=\"14\"\n                 Height=\"14\"\n                 Stroke=\"{TemplateBinding BorderBrush}\"\n                 Fill=\"{TemplateBinding Background}\"\n                 StrokeThickness=\"2\" />\n    </ControlTemplate>\n\n    <LinearGradientBrush x:Key=\"OutputFadeOpacityMask\"\n                         StartPoint=\"0% 0%\"\n                         EndPoint=\"100% 0%\">\n        <GradientStop Color=\"#22FFFFFF\"\n                      Offset=\"0\" />\n        <GradientStop Color=\"#88FFFFFF\"\n                      Offset=\"0.3\" />\n        <GradientStop Color=\"#88FFFFFF\"\n                      Offset=\"0.7\" />\n        <GradientStop Color=\"#22FFFFFF\"\n                      Offset=\"1\" />\n    </LinearGradientBrush>\n\n    <LinearGradientBrush x:Key=\"FadeOpacityMaskVerticalNodeOutput\"\n                         StartPoint=\"0% 0%\"\n                         EndPoint=\"0% 100%\">\n        <GradientStop Color=\"#22FFFFFF\"\n                      Offset=\"0\" />\n        <GradientStop Color=\"#88FFFFFF\"\n                      Offset=\"0.3\" />\n        <GradientStop Color=\"#88FFFFFF\"\n                      Offset=\"0.7\" />\n        <GradientStop Color=\"#22FFFFFF\"\n                      Offset=\"1\" />\n    </LinearGradientBrush>\n\n    <ControlTheme TargetType=\"{x:Type local:NodeOutput}\" x:Key=\"{x:Type local:NodeOutput}\">\n        <Setter Property=\"BorderBrush\"\n                Value=\"DodgerBlue\" />\n        <Setter Property=\"Background\"\n                Value=\"#2D2D30\" />\n        <Setter Property=\"Foreground\"\n                Value=\"White\" />\n        <Setter Property=\"Padding\"\n                Value=\"4 2\" />\n        <Setter Property=\"ConnectorTemplate\"\n                Value=\"{StaticResource OutputConnectorThumbTemplate}\" />\n        <Setter Property=\"Template\">\n            <Setter.Value>\n                <ControlTemplate TargetType=\"{x:Type local:NodeOutput}\">\n                    <Grid Background=\"{TemplateBinding Background}\">\n                        <Border IsVisible=\"False\"\n                                x:Name=\"Highlight\"\n                                OpacityMask=\"{StaticResource OutputFadeOpacityMask}\"\n                                Background=\"{TemplateBinding BorderBrush}\" />\n\n                        <StackPanel Orientation=\"{TemplateBinding Orientation}\"\n                                    HorizontalAlignment=\"Right\"\n                                    Margin=\"{TemplateBinding Padding}\">\n\n                            <ContentControl Content=\"{TemplateBinding Header}\" ContentTemplate=\"{TemplateBinding HeaderTemplate}\" />\n\n                            <TemplatedControl x:Name=\"PART_Connector\"\n                                     Focusable=\"False\"\n                                     VerticalAlignment=\"Center\"\n                                     Background=\"Transparent\"\n                                     BorderBrush=\"{TemplateBinding BorderBrush}\"\n                                     Template=\"{TemplateBinding ConnectorTemplate}\" />\n                        </StackPanel>\n                    </Grid>\n                </ControlTemplate>\n            </Setter.Value>\n        </Setter>\n        <Style Selector=\"^ /template/ TemplatedControl#PART_Connector\">\n            <Setter Property=\"Margin\"\n                    Value=\"5 0 0 0\" />\n        </Style>\n        <Style Selector=\"^:vertical /template/ TemplatedControl#PART_Connector\">\n            <Setter Property=\"Margin\"\n                    Value=\"0 5 0 0\" />\n        </Style>\n        <Style Selector=\"^:vertical /template/ Border#Highlight\">\n            <Setter Property=\"OpacityMask\"\n                    Value=\"{StaticResource FadeOpacityMaskVerticalNodeOutput}\" />\n        </Style>\n        <Style Selector=\"^:connected /template/ TemplatedControl#PART_Connector\">\n            <Setter Property=\"Background\"\n                    Value=\"{Binding BorderBrush, RelativeSource={RelativeSource TemplatedParent}}\" />\n        </Style>\n        <Style Selector=\"^.isOverElement /template/ TemplatedControl#PART_Connector\">\n            <Setter Property=\"Background\"\n                    Value=\"{Binding BorderBrush, RelativeSource={RelativeSource TemplatedParent}}\" />\n        </Style>\n        <Style Selector=\"^:pointerover /template/ Border#Highlight\">\n            <Setter Property=\"IsVisible\"\n                    Value=\"True\" />\n        </Style>\n        <Style Selector=\"^.isOverElement /template/ Border#Highlight\">\n            <Setter Property=\"IsVisible\"\n                    Value=\"True\" />\n        </Style>\n    </ControlTheme>\n</ResourceDictionary>\n"
  },
  {
    "path": "Nodify/Themes/Styles/NodifyEditor.xaml",
    "content": "﻿<ResourceDictionary xmlns=\"https://github.com/avaloniaui\"\n                    xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n                    xmlns:local=\"clr-namespace:Nodify\">\n\n    <local:UnscaleTransformConverter x:Key=\"UnscaleTransformConverter\" />\n    <local:ScaleDoubleConverter x:Key=\"ScaleDoubleConverter\" />\n    <local:ScalePointConverter x:Key=\"ScalePointConverter\" />\n\n    <DataTemplate x:Key=\"ConnectionTemplate\">\n        <local:Connection Theme=\"{DynamicResource {x:Type local:Connection}}\" />\n    </DataTemplate>\n\n    <DataTemplate x:Key=\"PendingConnectionTemplate\">\n        <local:PendingConnection IsTabStop=\"False\" />\n    </DataTemplate>\n\n    <ControlTheme x:Key=\"SelectionRectangleStyle\"\n           TargetType=\"Rectangle\">\n        <Setter Property=\"Stroke\"\n                Value=\"DodgerBlue\" />\n        <Setter Property=\"StrokeThickness\"\n                Value=\"1\" />\n        <Setter Property=\"Fill\">\n            <Setter.Value>\n                <SolidColorBrush Opacity=\"0.1\"\n                                 Color=\"DodgerBlue\" />\n            </Setter.Value>\n        </Setter>\n    </ControlTheme>\n\n    <ControlTheme x:Key=\"PushedAreaStyle\"\n           TargetType=\"Rectangle\">\n        <Setter Property=\"Stroke\"\n                Value=\"#74747c\" />\n        <Setter Property=\"StrokeThickness\"\n                Value=\"1\" />\n        <Setter Property=\"Fill\">\n            <Setter.Value>\n                <SolidColorBrush Opacity=\"0.2\"\n                                 Color=\"#74747c\" />\n            </Setter.Value>\n        </Setter>\n    </ControlTheme>\n\n    <ControlTheme TargetType=\"{x:Type local:NodifyEditor}\" x:Key=\"{x:Type local:NodifyEditor}\">\n        <Setter Property=\"ClipToBounds\"\n                Value=\"True\" />\n        <Setter Property=\"Foreground\"\n                Value=\"White\" />\n        <Setter Property=\"Background\"\n                Value=\"#1E1E1E\" />\n        <Setter Property=\"IsTabStop\"\n                Value=\"False\" />\n        <Setter Property=\"EnableRealtimeSelection\"\n                Value=\"True\" />\n        <Setter Property=\"DisplayConnectionsOnTop\"\n                Value=\"False\" />\n        <Setter Property=\"SelectionRectangleStyle\"\n                Value=\"{StaticResource SelectionRectangleStyle}\" />\n        <Setter Property=\"PushedAreaStyle\"\n                Value=\"{StaticResource PushedAreaStyle}\" />\n        <Setter Property=\"ConnectionTemplate\"\n                Value=\"{StaticResource ConnectionTemplate}\" />\n        <Setter Property=\"PendingConnectionTemplate\"\n                Value=\"{StaticResource PendingConnectionTemplate}\" />\n        <Setter Property=\"Template\">\n            <Setter.Value>\n                <ControlTemplate TargetType=\"{x:Type local:NodifyEditor}\">\n                    <Border Background=\"{TemplateBinding Background}\"\n                            BorderBrush=\"{TemplateBinding BorderBrush}\"\n                            BorderThickness=\"{TemplateBinding BorderThickness}\">\n                        <Canvas RenderTransform=\"{TemplateBinding ViewportTransform}\" RenderTransformOrigin=\"0,0\">\n                            <ItemsPresenter Name=\"PART_ItemsPresenter\">\n                                <ItemsPresenter.ItemsPanel>\n                                    <ItemsPanelTemplate>\n                                        <local:NodifyCanvas Extent=\"{Binding ItemsExtent, Mode=OneWayToSource, RelativeSource={RelativeSource TemplatedParent}}\" />\n                                    </ItemsPanelTemplate>\n                                </ItemsPresenter.ItemsPanel>\n                            </ItemsPresenter>\n\n                            <local:ConnectionsMultiSelector  x:Name=\"PART_ConnectionsHost\"\n                                                             ItemsSource=\"{TemplateBinding Connections}\"\n                                                             SelectedItem=\"{Binding SelectedConnection, RelativeSource={RelativeSource TemplatedParent}}\"\n                                                             SelectedItems=\"{TemplateBinding SelectedConnections}\"\n                                                             CanSelectMultipleItems=\"{TemplateBinding CanSelectMultipleConnections}\"\n                                                             ItemTemplate=\"{TemplateBinding ConnectionTemplate}\"\n                                                             ClipToBounds=\"False\"\n                                                             IsTabStop=\"False\">\n                                <local:ConnectionsMultiSelector.ItemsPanel>\n                                    <ItemsPanelTemplate>\n                                        <Canvas />\n                                    </ItemsPanelTemplate>\n                                </local:ConnectionsMultiSelector.ItemsPanel>\n                            </local:ConnectionsMultiSelector>\n\n                            <ContentControl ClipToBounds=\"False\" Content=\"{TemplateBinding PendingConnection}\" ContentTemplate=\"{TemplateBinding PendingConnectionTemplate}\" />\n\n                            <Rectangle Theme=\"{TemplateBinding SelectionRectangleStyle}\"\n                                       RenderTransform=\"{Binding ViewportTransform, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource UnscaleTransformConverter}}\"\n                                       RenderTransformOrigin=\"0,0\"\n                                       Canvas.Top=\"{Binding SelectedArea.Y, RelativeSource={RelativeSource TemplatedParent}}\"\n                                       Canvas.Left=\"{Binding SelectedArea.X, RelativeSource={RelativeSource TemplatedParent}}\"\n                                       IsVisible=\"{TemplateBinding IsSelecting}\">\n                                <Rectangle.Width>\n                                    <MultiBinding Converter=\"{StaticResource ScaleDoubleConverter}\">\n                                        <Binding Path=\"SelectedArea.Width\"\n                                                 RelativeSource=\"{RelativeSource TemplatedParent}\" />\n                                        <Binding Path=\"ViewportZoom\"\n                                                 RelativeSource=\"{RelativeSource TemplatedParent}\" />\n                                    </MultiBinding>\n                                </Rectangle.Width>\n                                <Rectangle.Height>\n                                    <MultiBinding Converter=\"{StaticResource ScaleDoubleConverter}\">\n                                        <Binding Path=\"SelectedArea.Height\"\n                                                 RelativeSource=\"{RelativeSource TemplatedParent}\" />\n                                        <Binding Path=\"ViewportZoom\"\n                                                 RelativeSource=\"{RelativeSource TemplatedParent}\" />\n                                    </MultiBinding>\n                                </Rectangle.Height>\n                            </Rectangle>\n\n                            <Rectangle Theme=\"{TemplateBinding PushedAreaStyle}\"\n                                    RenderTransform=\"{Binding ViewportTransform, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource UnscaleTransformConverter}}\"\n                                    RenderTransformOrigin=\"0,0\"\n                                    Canvas.Top=\"{Binding PushedArea.Y, RelativeSource={RelativeSource TemplatedParent}}\"\n                                    Canvas.Left=\"{Binding PushedArea.X, RelativeSource={RelativeSource TemplatedParent}}\"\n                                    IsVisible=\"{TemplateBinding IsPushingItems}\">\n                                <Rectangle.Width>\n                                    <MultiBinding Converter=\"{StaticResource ScaleDoubleConverter}\">\n                                        <Binding Path=\"PushedArea.Width\"\n                                                 RelativeSource=\"{RelativeSource TemplatedParent}\" />\n                                        <Binding Path=\"ViewportZoom\"\n                                                 RelativeSource=\"{RelativeSource TemplatedParent}\" />\n                                    </MultiBinding>\n                                </Rectangle.Width>\n                                <Rectangle.Height>\n                                    <MultiBinding Converter=\"{StaticResource ScaleDoubleConverter}\">\n                                        <Binding Path=\"PushedArea.Height\"\n                                                 RelativeSource=\"{RelativeSource TemplatedParent}\" />\n                                        <Binding Path=\"ViewportZoom\"\n                                                 RelativeSource=\"{RelativeSource TemplatedParent}\" />\n                                    </MultiBinding>\n                                </Rectangle.Height>\n                            </Rectangle>\n\n                            <local:CuttingLine Theme=\"{TemplateBinding CuttingLineStyle}\"\n                                               IsVisible=\"{TemplateBinding IsCutting}\"\n                                               RenderTransformOrigin=\"0,0\"\n                                               RenderTransform=\"{Binding ViewportTransform, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource UnscaleTransformConverter}}\">\n                                <local:CuttingLine.StartPoint>\n                                    <MultiBinding Converter=\"{StaticResource ScalePointConverter}\">\n                                        <Binding Path=\"CuttingLineStart\"\n                                                 RelativeSource=\"{RelativeSource TemplatedParent}\" />\n                                        <Binding Path=\"ViewportZoom\"\n                                                 RelativeSource=\"{RelativeSource TemplatedParent}\" />\n                                    </MultiBinding>\n                                </local:CuttingLine.StartPoint>\n                                <local:CuttingLine.EndPoint>\n                                    <MultiBinding Converter=\"{StaticResource ScalePointConverter}\">\n                                        <Binding Path=\"CuttingLineEnd\"\n                                                 RelativeSource=\"{RelativeSource TemplatedParent}\" />\n                                        <Binding Path=\"ViewportZoom\"\n                                                 RelativeSource=\"{RelativeSource TemplatedParent}\" />\n                                    </MultiBinding>\n                                </local:CuttingLine.EndPoint>\n                            </local:CuttingLine>\n\n                            <local:DecoratorsControl ItemsSource=\"{TemplateBinding Decorators}\"\n                                                     ItemContainerTheme=\"{TemplateBinding DecoratorContainerStyle}\"\n                                                     ItemTemplate=\"{TemplateBinding DecoratorTemplate}\"\n                                                     IsTabStop=\"False\"\n                                                     ClipToBounds=\"False\">\n                                <local:DecoratorsControl.ItemsPanel>\n                                    <ItemsPanelTemplate>\n                                        <local:NodifyCanvas Extent=\"{Binding DecoratorsExtent, Mode=OneWayToSource, RelativeSource={RelativeSource AncestorType=local:NodifyEditor}}\" />\n                                    </ItemsPanelTemplate>\n                                </local:DecoratorsControl.ItemsPanel>\n                            </local:DecoratorsControl>\n                        </Canvas>\n                    </Border>\n                </ControlTemplate>\n            </Setter.Value>\n        </Setter>\n        <Style Selector=\"^ /template/ local|ConnectionsMultiSelector#PART_ConnectionsHost\">\n            <Setter Property=\"ZIndex\" Value=\"-1\" />\n        </Style>\n        <Style Selector=\"^:connections-on-top /template/ local|ConnectionsMultiSelector#PART_ConnectionsHost\">\n            <Setter Property=\"ZIndex\" Value=\"0\" />\n        </Style>\n        <!-- TODO: Avalonia does not support MultiTrigger. Cursor changes for pushing items are not yet ported. -->\n    </ControlTheme>\n\n</ResourceDictionary>\n"
  },
  {
    "path": "Nodify/Themes/Styles/PendingConnection.xaml",
    "content": "﻿<ResourceDictionary xmlns=\"https://github.com/avaloniaui\"\n                    xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n                    xmlns:local=\"clr-namespace:Nodify\">\n\n    <ControlTheme TargetType=\"{x:Type local:PendingConnection}\" x:Key=\"{x:Type local:PendingConnection}\">\n        <Setter Property=\"ClipToBounds\"\n                Value=\"False\" />\n        <Setter Property=\"IsHitTestVisible\"\n                Value=\"False\" />\n        <Setter Property=\"Background\"\n                Value=\"#121212\" />\n        <Setter Property=\"Foreground\"\n                Value=\"White\" />\n        <Setter Property=\"Stroke\"\n                Value=\"DodgerBlue\" />\n        <Setter Property=\"BorderBrush\"\n                Value=\"Black\" />\n        <Setter Property=\"EnablePreview\"\n                Value=\"False\" />\n        <Setter Property=\"StrokeThickness\"\n                Value=\"3\" />\n        <Setter Property=\"BorderThickness\"\n                Value=\"1\" />\n        <Setter Property=\"IsVisible\"\n                Value=\"False\" />\n        <Setter Property=\"StrokeDashArray\"\n                Value=\"4,4\" />\n        <Setter Property=\"Padding\"\n                Value=\"5\" />\n        <Setter Property=\"EnableSnapping\"\n                Value=\"True\" />\n        <Setter Property=\"Template\">\n            <Setter.Value>\n                <ControlTemplate TargetType=\"{x:Type local:PendingConnection}\">\n                    <Canvas>\n                        <local:LineConnection Source=\"{TemplateBinding SourceAnchor}\"\n                                              Target=\"{TemplateBinding TargetAnchor}\"\n                                              Stroke=\"{TemplateBinding Stroke}\"\n                                              StrokeThickness=\"{TemplateBinding StrokeThickness}\"\n                                              StrokeDashArray=\"{TemplateBinding StrokeDashArray}\"\n                                              Direction=\"{TemplateBinding Direction}\"\n                                              Spacing=\"0\"\n                                              ArrowSize=\"0 0\"\n                                              SourceOffsetMode=\"None\" \n                                              TargetOffsetMode=\"None\" />\n                        <Border Background=\"{TemplateBinding Background}\"\n                                Canvas.Left=\"{Binding TargetAnchor.X, RelativeSource={RelativeSource TemplatedParent}}\"\n                                Canvas.Top=\"{Binding TargetAnchor.Y, RelativeSource={RelativeSource TemplatedParent}}\"\n                                IsVisible=\"{TemplateBinding EnablePreview}\"\n                                Padding=\"{TemplateBinding Padding}\"\n                                BorderThickness=\"{TemplateBinding BorderThickness}\"\n                                BorderBrush=\"{TemplateBinding BorderBrush}\"\n                                CornerRadius=\"3\"\n                                Margin=\"15\">\n                            <ContentPresenter Content=\"{TemplateBinding Content}\" ContentTemplate=\"{TemplateBinding ContentTemplate}\" />\n                        </Border>\n                    </Canvas>\n                </ControlTemplate>\n            </Setter.Value>\n        </Setter>\n    </ControlTheme>\n\n</ResourceDictionary>"
  },
  {
    "path": "Nodify/Themes/Styles/StateNode.xaml",
    "content": "﻿<ResourceDictionary xmlns=\"https://github.com/avaloniaui\"\n                    xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n                    xmlns:local=\"clr-namespace:Nodify\">\n\n    <ControlTheme TargetType=\"{x:Type local:StateNode}\" x:Key=\"{x:Type local:StateNode}\">\n        <Setter Property=\"Padding\"\n                Value=\"7 5\" />\n        <Setter Property=\"BorderThickness\"\n                Value=\"10\" />\n        <Setter Property=\"BorderBrush\"\n                Value=\"#484848\" />\n        <Setter Property=\"Background\"\n                Value=\"#171717\" />\n        <Setter Property=\"Foreground\"\n                Value=\"White\" />\n        <Setter Property=\"HighlightBrush\"\n                Value=\"#D6D3D6\" />\n        <Setter Property=\"CornerRadius\"\n                Value=\"3\" />\n        <Setter Property=\"Template\">\n            <Setter.Value>\n                <ControlTemplate TargetType=\"{x:Type local:StateNode}\">\n                    <Border x:Name=\"Border\"\n                            Padding=\"{TemplateBinding BorderThickness}\"\n                            Margin=\"{TemplateBinding Margin}\"\n                            CornerRadius=\"{TemplateBinding CornerRadius}\">\n                        <Border x:Name=\"PART_Content\"\n                                Background=\"{TemplateBinding Background}\"\n                                Padding=\"{TemplateBinding Padding}\"\n                                Cursor=\"SizeAll\"\n                                MinWidth=\"30\"\n                                MinHeight=\"30\"\n                                CornerRadius=\"{TemplateBinding CornerRadius}\">\n                            <ContentControl Content=\"{TemplateBinding Content}\" ContentTemplate=\"{TemplateBinding ContentTemplate}\" />\n                        </Border>\n                        <Border.Theme>\n                            <ControlTheme TargetType=\"Border\">\n                                <Setter Property=\"Background\"\n                                        Value=\"{Binding BorderBrush, RelativeSource={RelativeSource TemplatedParent}}\" />\n                                <Style Selector=\"^:pointerover\">\n                                    <Setter Property=\"Background\"\n                                            Value=\"{Binding HighlightBrush, RelativeSource={RelativeSource TemplatedParent}}\" />\n                                </Style>\n                            </ControlTheme>\n                        </Border.Theme>\n                    </Border>\n                </ControlTemplate>\n            </Setter.Value>\n        </Setter>\n        <Style Selector=\"^.isOverElement /template/ Border#Border\">\n            <Setter Property=\"Background\"\n                    Value=\"{Binding HighlightBrush, RelativeSource={RelativeSource TemplatedParent}}\" />\n        </Style>\n    </ControlTheme>\n\n</ResourceDictionary>"
  },
  {
    "path": "Nodify.sln",
    "content": "﻿\nMicrosoft Visual Studio Solution File, Format Version 12.00\n# Visual Studio Version 17\nVisualStudioVersion = 17.8.34330.188\nMinimumVisualStudioVersion = 10.0.40219.1\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"Nodify\", \"Nodify\\Nodify.csproj\", \"{A4349FD1-2774-42F2-90FD-7D8F4CA8BB71}\"\nEndProject\nProject(\"{2150E333-8FDC-42A3-9474-1A3956D46DE8}\") = \"Examples\", \"Examples\", \"{E467F53A-3CCA-4EDB-BD33-FC89CA3C526B}\"\nEndProject\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"Nodify.Playground\", \"Examples\\Nodify.Playground\\Nodify.Playground.csproj\", \"{423D54CA-9F42-4116-8755-13641F94151D}\"\nEndProject\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"Nodify.StateMachine\", \"Examples\\Nodify.StateMachine\\Nodify.StateMachine.csproj\", \"{48569CA8-81C1-4B47-963B-0FC9358C5F01}\"\nEndProject\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"Nodify.Shared\", \"Examples\\Nodify.Shared\\Nodify.Shared.csproj\", \"{499C8539-9973-4102-B976-57DB3F3C7DF3}\"\nEndProject\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"Nodify.Calculator\", \"Examples\\Nodify.Calculator\\Nodify.Calculator.csproj\", \"{7ADD1DAF-B589-4742-8E72-872D2842E73D}\"\nEndProject\nProject(\"{2150E333-8FDC-42A3-9474-1A3956D46DE8}\") = \"Solution Items\", \"Solution Items\", \"{88D231A7-D09F-492F-887F-DDBC46398284}\"\n\tProjectSection(SolutionItems) = preProject\n\t\tCHANGELOG.md = CHANGELOG.md\n\t\tbuild_cloudflare.sh = build_cloudflare.sh\n\t\tREADME.md = README.md\n\t\tDirectory.Build.props = Directory.Build.props\n\tEndProjectSection\nEndProject\nProject(\"{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}\") = \"Nodify.Shapes\", \"Examples\\Nodify.Shapes\\Nodify.Shapes.csproj\", \"{BAD92DB9-BDCA-4BB7-8F24-B7D3FE470476}\"\nEndProject\nProject(\"{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}\") = \"Nodify.Shapes.Web\", \"Examples\\Nodify.Shapes.Web\\Nodify.Shapes.Web.csproj\", \"{8F4D5A6D-CA3A-4C0C-9597-7D0E2EDFAB21}\"\nEndProject\nProject(\"{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}\") = \"Nodify.Shapes.Desktop\", \"Examples\\Nodify.Shapes.Desktop\\Nodify.Shapes.Desktop.csproj\", \"{D53C27C4-C49D-40DD-87BB-CC6431999BF8}\"\nEndProject\nGlobal\n\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\n\t\tDebug|Any CPU = Debug|Any CPU\n\t\tRelease|Any CPU = Release|Any CPU\n\tEndGlobalSection\n\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\n\t\t{A4349FD1-2774-42F2-90FD-7D8F4CA8BB71}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{A4349FD1-2774-42F2-90FD-7D8F4CA8BB71}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{A4349FD1-2774-42F2-90FD-7D8F4CA8BB71}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{A4349FD1-2774-42F2-90FD-7D8F4CA8BB71}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{423D54CA-9F42-4116-8755-13641F94151D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{423D54CA-9F42-4116-8755-13641F94151D}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{423D54CA-9F42-4116-8755-13641F94151D}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{423D54CA-9F42-4116-8755-13641F94151D}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{48569CA8-81C1-4B47-963B-0FC9358C5F01}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{48569CA8-81C1-4B47-963B-0FC9358C5F01}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{48569CA8-81C1-4B47-963B-0FC9358C5F01}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{48569CA8-81C1-4B47-963B-0FC9358C5F01}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{499C8539-9973-4102-B976-57DB3F3C7DF3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{499C8539-9973-4102-B976-57DB3F3C7DF3}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{499C8539-9973-4102-B976-57DB3F3C7DF3}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{499C8539-9973-4102-B976-57DB3F3C7DF3}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{7ADD1DAF-B589-4742-8E72-872D2842E73D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{7ADD1DAF-B589-4742-8E72-872D2842E73D}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{7ADD1DAF-B589-4742-8E72-872D2842E73D}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{7ADD1DAF-B589-4742-8E72-872D2842E73D}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{BAD92DB9-BDCA-4BB7-8F24-B7D3FE470476}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{BAD92DB9-BDCA-4BB7-8F24-B7D3FE470476}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{BAD92DB9-BDCA-4BB7-8F24-B7D3FE470476}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{BAD92DB9-BDCA-4BB7-8F24-B7D3FE470476}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{8F4D5A6D-CA3A-4C0C-9597-7D0E2EDFAB21}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{8F4D5A6D-CA3A-4C0C-9597-7D0E2EDFAB21}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{8F4D5A6D-CA3A-4C0C-9597-7D0E2EDFAB21}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{8F4D5A6D-CA3A-4C0C-9597-7D0E2EDFAB21}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{D53C27C4-C49D-40DD-87BB-CC6431999BF8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{D53C27C4-C49D-40DD-87BB-CC6431999BF8}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{D53C27C4-C49D-40DD-87BB-CC6431999BF8}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{D53C27C4-C49D-40DD-87BB-CC6431999BF8}.Release|Any CPU.Build.0 = Release|Any CPU\n\tEndGlobalSection\n\tGlobalSection(SolutionProperties) = preSolution\n\t\tHideSolutionNode = FALSE\n\tEndGlobalSection\n\tGlobalSection(NestedProjects) = preSolution\n\t\t{423D54CA-9F42-4116-8755-13641F94151D} = {E467F53A-3CCA-4EDB-BD33-FC89CA3C526B}\n\t\t{48569CA8-81C1-4B47-963B-0FC9358C5F01} = {E467F53A-3CCA-4EDB-BD33-FC89CA3C526B}\n\t\t{499C8539-9973-4102-B976-57DB3F3C7DF3} = {E467F53A-3CCA-4EDB-BD33-FC89CA3C526B}\n\t\t{7ADD1DAF-B589-4742-8E72-872D2842E73D} = {E467F53A-3CCA-4EDB-BD33-FC89CA3C526B}\n\t\t{BAD92DB9-BDCA-4BB7-8F24-B7D3FE470476} = {E467F53A-3CCA-4EDB-BD33-FC89CA3C526B}\n\t\t{8F4D5A6D-CA3A-4C0C-9597-7D0E2EDFAB21} = {E467F53A-3CCA-4EDB-BD33-FC89CA3C526B}\n\t\t{D53C27C4-C49D-40DD-87BB-CC6431999BF8} = {E467F53A-3CCA-4EDB-BD33-FC89CA3C526B}\n\tEndGlobalSection\n\tGlobalSection(ExtensibilityGlobals) = postSolution\n\t\tSolutionGuid = {B9E59DB6-FB93-48F3-A8E0-1E549009E043}\n\tEndGlobalSection\nEndGlobal\n"
  },
  {
    "path": "Nodify.sln.DotSettings",
    "content": "﻿<wpf:ResourceDictionary xml:space=\"preserve\" xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" xmlns:s=\"clr-namespace:System;assembly=mscorlib\" xmlns:ss=\"urn:shemas-jetbrains-com:settings-storage-xaml\" xmlns:wpf=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\">\n\t<s:Boolean x:Key=\"/Default/UserDictionary/Words/=Nodify/@EntryIndexedValue\">True</s:Boolean>\n\t<s:Boolean x:Key=\"/Default/UserDictionary/Words/=Templated/@EntryIndexedValue\">True</s:Boolean></wpf:ResourceDictionary>"
  },
  {
    "path": "PULL_REQUEST_TEMPLATE.md",
    "content": "<!-- \n  ## Hello and welcome!\n\n  Consider creating an issue or link to an existing one before submitting the pull request. Thanks!\n-->\n\n### 📝 Description of the Change\n\nWhat is this pull request about?\n\n### 🐛 Possible Drawbacks\n\nAre there any possible side-effects or negative impacts with this code change?\n"
  },
  {
    "path": "README.md",
    "content": "\n# NodifyAvalonia <img src=\"https://github.com/BAndysc/nodify-avalonia/assets/5689666/3b9fe4bd-30c8-4ac7-9b4c-5f1864b83e41\" width=\"120px\" alt=\"Nodify.Avalonia\" align=\"right\">\n\n[![NuGet](https://img.shields.io/nuget/v/NodifyAvalonia?style=for-the-badge&logo=nuget&label=release)](https://www.nuget.org/packages/NodifyAvalonia/)\n[![NuGet](https://img.shields.io/nuget/dt/NodifyAvalonia?label=downloads&style=for-the-badge&logo=nuget)](https://www.nuget.org/packages/NodifyAvalonia)\n[![License](https://img.shields.io/github/license/bandysc/nodify-avalonia?style=for-the-badge)](https://github.com/bandysc/nodify-avalonia/blob/master/LICENSE)\n[![C#](https://img.shields.io/static/v1?label=docs&message=WIP&color=orange&style=for-the-badge)](https://github.com/miroiu/nodify/wiki)\n\nThis is a direct port of [Nodify by miroiu](https://github.com/miroiu/nodify) to Avalonia. \n\n> A collection of highly performant controls for node-based editors designed for MVVM.\n\nThe goal of the port is to keep the codebase as similar to the original code as possible, to the point, where merges from the upstream are not a problem.\n\n## 🚀 Examples of node-based applications\n\n🔶 A canvas application where you can draw and connect shapes.\n\n> [Examples/Nodify.Shapes](Examples/Nodify.Shapes)\n\n#### ➜ Open a [WASM 🌐 browser version of the shapes demo in your browser, without downloading!](https://avalonia-port.nodify-avalonia.pages.dev/) 🚀\n\n_(Note: C# in a browser is still much slower than standalone desktop C#, the performance is much better when used in a standalone application.)_\n\n![Canvas](https://i.imgur.com/fIf8ACd.gif)\n\n🎨 A playground application where you can try all the available settings.\n\n> [Examples/Nodify.Playground](Examples/Nodify.Playground)\n\n![Playground](https://i.imgur.com/jdAwDeh.gif)\n\n🌓 A state machine where each state represents an executable action, and each transition represents a condition for executing the next action.\n\n> [Examples/Nodify.StateMachine](Examples/Nodify.StateMachine)\n\n![StateMachine](https://i.imgur.com/UU0TQxe.gif)\n\n💻 A simple \"real-time\" calculator where each node represents an operation that takes input and feeds its output into other node's input.\n\n> [Examples/Nodify.Calculator](Examples/Nodify.Calculator)\n\n![Calculator](https://i.imgur.com/rup58xn.gif)\n\n## 📥 Installation\nUse the NuGet package manager to install `NodifyAvalonia`.\n\n```\nInstall-Package NodifyAvalonia\n```\n\nAnd include Nodify resources:\n\n```\n<ResourceInclude Source=\"avares://Nodify/Theme.axaml\" />\n```\n\n⚠️⚠️ **Please do not confuse with `Nodify.Avalonia` which is a different package** ⚠️⚠️\n\nAvalonia version compatibility chart:\n\n| Nodify version | Avalonia version |\n|----------------|------------------|\n| 6.6.0          | 11.1.0           |\n| 6.5.0          | 11.1.0           |\n| 6.2.0          | 11.1.0           |\n| 6.1.0          | 11.1.0           |\n| 6.0.0          | 11.1.0-beta-2    |\n| 5.3.0          | 11.1.0-beta-2    |\n| 5.2.0          | 11.1.0-beta-1    |\n\n## ⭐️ Features\n \n - Designed from the start to work with **MVVM**\n - **No dependencies** other than ~~WPF~~ Avalonia\n - **Optimized** for interactions with hundreds of nodes at once\n - Built-in dark and light **themes**\n - **Selecting**, **zooming**, **panning** with **auto panning** when close to edge\n - **Select**, **move** and **connect** nodes\n - Lots of **configurable** dependency properties\n - Ready for undo/redo\n - Example applications: 🎨 [**Playground**](Examples/Nodify.Playground), 🌓 [**State machine**](Examples/Nodify.StateMachine), 💻 [**Calculator**](Examples/Nodify.Calculator), 🔶 [**Canvas**](Examples/Nodify.Shapes)\n\n## 😿 Unsupported Features\n\n - Cutting Lines (blocker: https://github.com/AvaloniaUI/Avalonia/issues/16549)\n\n## 📝 Documentation\n\nFor the wiki please refer to the original [miroiu's Wiki](https://github.com/miroiu/nodify/wiki) since the API is identical, but please report bugs here. However, if you find a bug, please try to check if it also occurs in the original WPF's version.\n\n> [!NOTE]  \n> Avalonia.Point should be used in place of System.Windows.Point (Anchor points).\n\n## ❤️ [Contributing](CONTRIBUTING.md)\n\nIf you find a bug in Avalonia port, bug reports, PRs are more than welcome. If you think this might be not related to Avalonia, please try to reproduce it first in the original [WPF's version](https://github.com/miroiu/nodify).\n"
  },
  {
    "path": "build_cloudflare.sh",
    "content": "#!/bin/sh\n#apt-get install python3 brotli -y\ncurl -sSL https://dot.net/v1/dotnet-install.sh > dotnet-install.sh\nchmod +x dotnet-install.sh\n./dotnet-install.sh -c 9.0 -InstallDir ./dotnet\n./dotnet/dotnet --version\n./dotnet/dotnet workload restore\n./dotnet/dotnet restore\n./dotnet/dotnet publish Examples/Nodify.Shapes.Web -c Release -o output\n#find AppBundle/ -type f -exec brotli {} \\;"
  },
  {
    "path": "docs/API-Reference.md",
    "content": "- [Alignment Enum](#alignment-enum)  \n- [AllGestures Class](#allgestures-class)  \n- [AnyGesture Class](#anygesture-class)  \n- [ArrowHeadEnds Enum](#arrowheadends-enum)  \n- [ArrowHeadShape Enum](#arrowheadshape-enum)  \n- [BaseConnection Class](#baseconnection-class)  \n- [BoxValue Class](#boxvalue-class)  \n- [CircuitConnection Class](#circuitconnection-class)  \n- [Connection Class](#connection-class)  \n- [ConnectionDirection Enum](#connectiondirection-enum)  \n- [ConnectionEventArgs Class](#connectioneventargs-class)  \n- [ConnectionEventHandler Delegate](#connectioneventhandler-delegate)  \n- [ConnectionGestures Class](#connectiongestures-class)  \n- [ConnectionOffsetMode Enum](#connectionoffsetmode-enum)  \n- [Connector Class](#connector-class)  \n- [ConnectorEventArgs Class](#connectoreventargs-class)  \n- [ConnectorEventHandler Delegate](#connectoreventhandler-delegate)  \n- [ConnectorGestures Class](#connectorgestures-class)  \n- [ConnectorPosition Enum](#connectorposition-enum)  \n- [ContainerDefaultState Class](#containerdefaultstate-class)  \n- [ContainerDraggingState Class](#containerdraggingstate-class)  \n- [ContainerState Class](#containerstate-class)  \n- [CuttingLine Class](#cuttingline-class)  \n- [DecoratorContainer Class](#decoratorcontainer-class)  \n- [EditorCommands Class](#editorcommands-class)  \n- [EditorCuttingState Class](#editorcuttingstate-class)  \n- [EditorDefaultState Class](#editordefaultstate-class)  \n- [EditorGestures Class](#editorgestures-class)  \n- [EditorPanningState Class](#editorpanningstate-class)  \n- [EditorPushingItemsState Class](#editorpushingitemsstate-class)  \n- [EditorSelectingState Class](#editorselectingstate-class)  \n- [EditorState Class](#editorstate-class)  \n- [GeneratedInternalTypeHelper Class](#generatedinternaltypehelper-class)  \n- [GroupingMovementMode Enum](#groupingmovementmode-enum)  \n- [GroupingNode Class](#groupingnode-class)  \n- [GroupingNodeGestures Class](#groupingnodegestures-class)  \n- [INodifyCanvasItem Interface](#inodifycanvasitem-interface)  \n- [InputGestureRef Class](#inputgestureref-class)  \n- [ItemContainer Class](#itemcontainer-class)  \n- [ItemContainerGestures Class](#itemcontainergestures-class)  \n- [KnotNode Class](#knotnode-class)  \n- [LineConnection Class](#lineconnection-class)  \n- [Match Enum](#match-enum)  \n- [Minimap Class](#minimap-class)  \n- [MinimapGestures Class](#minimapgestures-class)  \n- [MinimapItem Class](#minimapitem-class)  \n- [MultiGesture Class](#multigesture-class)  \n- [Node Class](#node-class)  \n- [NodeInput Class](#nodeinput-class)  \n- [NodeOutput Class](#nodeoutput-class)  \n- [NodifyCanvas Class](#nodifycanvas-class)  \n- [NodifyEditor Class](#nodifyeditor-class)  \n- [NodifyEditorGestures Class](#nodifyeditorgestures-class)  \n- [PendingConnection Class](#pendingconnection-class)  \n- [PendingConnectionEventArgs Class](#pendingconnectioneventargs-class)  \n- [PendingConnectionEventHandler Delegate](#pendingconnectioneventhandler-delegate)  \n- [PreviewLocationChanged Delegate](#previewlocationchanged-delegate)  \n- [ResizeEventArgs Class](#resizeeventargs-class)  \n- [ResizeEventHandler Delegate](#resizeeventhandler-delegate)  \n- [SelectionGestures Class](#selectiongestures-class)  \n- [SelectionHelper Class](#selectionhelper-class)  \n- [SelectionType Enum](#selectiontype-enum)  \n- [StateNode Class](#statenode-class)  \n- [StepConnection Class](#stepconnection-class)  \n- [ZoomEventArgs Class](#zoomeventargs-class)  \n- [ZoomEventHandler Delegate](#zoomeventhandler-delegate)  \n  \n  \n## Alignment Enum  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n```csharp  \npublic enum Alignment  \n```  \n  \n### Fields  \n  \n#### Bottom  \n  \n```csharp  \nBottom = 2;  \n```  \n  \n#### Center  \n  \n```csharp  \nCenter = 5;  \n```  \n  \n#### Left  \n  \n```csharp  \nLeft = 1;  \n```  \n  \n#### Middle  \n  \n```csharp  \nMiddle = 4;  \n```  \n  \n#### Right  \n  \n```csharp  \nRight = 3;  \n```  \n  \n#### Top  \n  \n```csharp  \nTop = 0;  \n```  \n  \n## AllGestures Class  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**Inheritance:** [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object) → [InputGesture](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.InputGesture) → [MultiGesture](#multigesture-class) → [AllGestures](#allgestures-class)  \n  \n```csharp  \npublic sealed class AllGestures : MultiGesture  \n```  \n  \n### Constructors  \n  \n#### AllGestures(InputGesture[])  \n  \n```csharp  \npublic AllGestures(InputGesture[] gestures);  \n```  \n  \n**Parameters**  \n  \n`gestures` [InputGesture[]](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.InputGesture[])  \n  \n## AnyGesture Class  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**Inheritance:** [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object) → [InputGesture](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.InputGesture) → [MultiGesture](#multigesture-class) → [AnyGesture](#anygesture-class)  \n  \n```csharp  \npublic sealed class AnyGesture : MultiGesture  \n```  \n  \n### Constructors  \n  \n#### AnyGesture(InputGesture[])  \n  \n```csharp  \npublic AnyGesture(InputGesture[] gestures);  \n```  \n  \n**Parameters**  \n  \n`gestures` [InputGesture[]](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.InputGesture[])  \n  \n## ArrowHeadEnds Enum  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**References:** [BaseConnection](#baseconnection-class)  \n  \nThe end at which the arrow head is drawn.  \n  \n```csharp  \npublic enum ArrowHeadEnds  \n```  \n  \n### Fields  \n  \n#### Both  \n  \nArrow heads at both ends.  \n  \n```csharp  \nBoth = 2;  \n```  \n  \n#### End  \n  \nArrow head at end.  \n  \n```csharp  \nEnd = 1;  \n```  \n  \n#### None  \n  \nNo arrow head.  \n  \n```csharp  \nNone = 3;  \n```  \n  \n#### Start  \n  \nArrow head at start.  \n  \n```csharp  \nStart = 0;  \n```  \n  \n## ArrowHeadShape Enum  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**References:** [BaseConnection](#baseconnection-class)  \n  \nThe shape of the arrowhead.  \n  \n```csharp  \npublic enum ArrowHeadShape  \n```  \n  \n### Fields  \n  \n#### Arrowhead  \n  \nThe default arrowhead.  \n  \n```csharp  \nArrowhead = 0;  \n```  \n  \n#### Ellipse  \n  \nAn ellipse.  \n  \n```csharp  \nEllipse = 1;  \n```  \n  \n#### Rectangle  \n  \nA rectangle.  \n  \n```csharp  \nRectangle = 2;  \n```  \n  \n## BaseConnection Class  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**Inheritance:** [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object) → [DispatcherObject](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Threading.DispatcherObject) → [DependencyObject](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.DependencyObject) → [Visual](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Media.Visual) → [UIElement](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.UIElement) → [FrameworkElement](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.FrameworkElement) → [Shape](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Shapes.Shape) → [BaseConnection](#baseconnection-class)  \n  \n**Derived:** [LineConnection](#lineconnection-class), [Connection](#connection-class)  \n  \n**References:** [ArrowHeadEnds](#arrowheadends-enum), [ArrowHeadShape](#arrowheadshape-enum), [ConnectionDirection](#connectiondirection-enum), [ConnectionEventArgs](#connectioneventargs-class), [ConnectionEventHandler](#connectioneventhandler-delegate), [ConnectionOffsetMode](#connectionoffsetmode-enum), [CuttingLine](#cuttingline-class), [NodifyEditor](#nodifyeditor-class)  \n  \nRepresents the base class for shapes that are drawn from a [BaseConnection.Source](#baseconnection-class#source) point to a [BaseConnection.Target](#baseconnection-class#target) point.  \n  \n```csharp  \npublic abstract class BaseConnection : Shape  \n```  \n  \n### Constructors  \n  \n#### BaseConnection()  \n  \n```csharp  \nprotected BaseConnection();  \n```  \n  \n### Fields  \n  \n#### ZeroVector  \n  \nGets a vector that has its coordinates set to 0.  \n  \n```csharp  \nprotected static Vector ZeroVector;  \n```  \n  \n**Field Value**  \n  \n[Vector](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Vector)  \n  \n### Properties  \n  \n#### ArrowEnds  \n  \nGets or sets the arrowhead ends.  \n  \n```csharp  \npublic ArrowHeadEnds ArrowEnds { get; set; }  \n```  \n  \n**Property Value**  \n  \n[ArrowHeadEnds](#arrowheadends-enum)  \n  \n#### ArrowShape  \n  \nGets or sets the arrowhead ends.  \n  \n```csharp  \npublic ArrowHeadShape ArrowShape { get; set; }  \n```  \n  \n**Property Value**  \n  \n[ArrowHeadShape](#arrowheadshape-enum)  \n  \n#### ArrowSize  \n  \nGets or sets the size of the arrow head.  \n  \n```csharp  \npublic Size ArrowSize { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Size](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Size)  \n  \n#### DefiningGeometry  \n  \n```csharp  \nprotected override Geometry DefiningGeometry { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Geometry](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Media.Geometry)  \n  \n#### Direction  \n  \nGets or sets the direction in which this connection is flowing.  \n  \n```csharp  \npublic ConnectionDirection Direction { get; set; }  \n```  \n  \n**Property Value**  \n  \n[ConnectionDirection](#connectiondirection-enum)  \n  \n#### DirectionalArrowsAnimationDuration  \n  \nGets or sets the duration in seconds of a directional arrow flowing from [BaseConnection.Source](#baseconnection-class#source) to [BaseConnection.Target](#baseconnection-class#target).  \n  \n```csharp  \npublic double DirectionalArrowsAnimationDuration { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Double](https://docs.microsoft.com/en-us/dotnet/api/System.Double)  \n  \n#### DirectionalArrowsCount  \n  \nGets or sets the number of arrows to be drawn on the line in the direction of the connection (see [BaseConnection.Direction](#baseconnection-class#direction)).  \n  \n```csharp  \npublic uint DirectionalArrowsCount { get; set; }  \n```  \n  \n**Property Value**  \n  \n[UInt32](https://docs.microsoft.com/en-us/dotnet/api/System.UInt32)  \n  \n#### DirectionalArrowsOffset  \n  \nGets or sets the offset of the arrows drawn by the [BaseConnection.DirectionalArrowsCount](#baseconnection-class#directionalarrowscount) (value is clamped between 0 and 1).  \n  \n```csharp  \npublic double DirectionalArrowsOffset { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Double](https://docs.microsoft.com/en-us/dotnet/api/System.Double)  \n  \n#### DisconnectCommand  \n  \nRemoves this connection. Triggered by Nodify.EditorGestures.ConnectionGestures.Disconnect gesture.\n            Parameter is the location where the disconnect ocurred.  \n  \n```csharp  \npublic ICommand DisconnectCommand { get; set; }  \n```  \n  \n**Property Value**  \n  \n[ICommand](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.ICommand)  \n  \n#### FontFamily  \n  \n```csharp  \npublic FontFamily FontFamily { get; set; }  \n```  \n  \n**Property Value**  \n  \n[FontFamily](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Media.FontFamily)  \n  \n#### FontSize  \n  \n```csharp  \npublic double FontSize { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Double](https://docs.microsoft.com/en-us/dotnet/api/System.Double)  \n  \n#### FontStretch  \n  \n```csharp  \npublic FontStretch FontStretch { get; set; }  \n```  \n  \n**Property Value**  \n  \n[FontStretch](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.FontStretch)  \n  \n#### FontStyle  \n  \n```csharp  \npublic FontStyle FontStyle { get; set; }  \n```  \n  \n**Property Value**  \n  \n[FontStyle](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.FontStyle)  \n  \n#### FontWeight  \n  \n```csharp  \npublic FontWeight FontWeight { get; set; }  \n```  \n  \n**Property Value**  \n  \n[FontWeight](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.FontWeight)  \n  \n#### Foreground  \n  \nThe brush used to render the [BaseConnection.Text](#baseconnection-class#text).  \n  \n```csharp  \npublic Brush Foreground { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Brush](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Media.Brush)  \n  \n#### IsAnimatingDirectionalArrows  \n  \nGets or sets whether the directional arrows should be flowing through the connection wire.  \n  \n```csharp  \npublic bool IsAnimatingDirectionalArrows { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean)  \n  \n#### OutlineBrush  \n  \nThe brush used to render the outline.  \n  \n```csharp  \npublic Brush OutlineBrush { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Brush](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Media.Brush)  \n  \n#### OutlineThickness  \n  \nThe thickness of the outline.  \n  \n```csharp  \npublic double OutlineThickness { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Double](https://docs.microsoft.com/en-us/dotnet/api/System.Double)  \n  \n#### PrioritizeBaseConnectionForSelection  \n  \nWhether to prioritize controls of type [BaseConnection](#baseconnection-class) inside custom connections (connection wrappers) \n            when setting the [BaseConnection.IsSelectableProperty](#baseconnection-class#isselectableproperty) and [BaseConnection.IsSelectedProperty](#baseconnection-class#isselectedproperty) attached properties.  \n  \n```csharp  \npublic static bool PrioritizeBaseConnectionForSelection { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean)  \n  \n#### Source  \n  \nGets or sets the start point of this connection.  \n  \n```csharp  \npublic Point Source { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n#### SourceOffset  \n  \nGets or sets the offset from the [BaseConnection.Source](#baseconnection-class#source) point.  \n  \n```csharp  \npublic Size SourceOffset { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Size](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Size)  \n  \n#### SourceOffsetMode  \n  \nGets or sets the [ConnectionOffsetMode](#connectionoffsetmode-enum) to apply to the [BaseConnection.Source](#baseconnection-class#source) when drawing the connection.  \n  \n```csharp  \npublic ConnectionOffsetMode SourceOffsetMode { get; set; }  \n```  \n  \n**Property Value**  \n  \n[ConnectionOffsetMode](#connectionoffsetmode-enum)  \n  \n#### SourceOrientation  \n  \nGets or sets the orientation in which this connection is flowing.  \n  \n```csharp  \npublic Orientation SourceOrientation { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Orientation](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Controls.Orientation)  \n  \n#### Spacing  \n  \nThe distance between the start point and the where the angle breaks.  \n  \n```csharp  \npublic double Spacing { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Double](https://docs.microsoft.com/en-us/dotnet/api/System.Double)  \n  \n#### SplitCommand  \n  \nSplits the connection. Triggered by Nodify.EditorGestures.ConnectionGestures.Split gesture.\n            Parameter is the location where the splitting ocurred.  \n  \n```csharp  \npublic ICommand SplitCommand { get; set; }  \n```  \n  \n**Property Value**  \n  \n[ICommand](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.ICommand)  \n  \n#### Target  \n  \nGets or sets the end point of this connection.  \n  \n```csharp  \npublic Point Target { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n#### TargetOffset  \n  \nGets or sets the offset from the [BaseConnection.Target](#baseconnection-class#target) point.  \n  \n```csharp  \npublic Size TargetOffset { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Size](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Size)  \n  \n#### TargetOffsetMode  \n  \nGets or sets the [ConnectionOffsetMode](#connectionoffsetmode-enum) to apply to the [BaseConnection.Target](#baseconnection-class#target) when drawing the connection.  \n  \n```csharp  \npublic ConnectionOffsetMode TargetOffsetMode { get; set; }  \n```  \n  \n**Property Value**  \n  \n[ConnectionOffsetMode](#connectionoffsetmode-enum)  \n  \n#### TargetOrientation  \n  \nGets or sets the orientation in which this connection is flowing.  \n  \n```csharp  \npublic Orientation TargetOrientation { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Orientation](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Controls.Orientation)  \n  \n#### Text  \n  \nGets or sets the text contents of the [BaseConnection](#baseconnection-class).  \n  \n```csharp  \npublic string Text { get; set; }  \n```  \n  \n**Property Value**  \n  \n[String](https://docs.microsoft.com/en-us/dotnet/api/System.String)  \n  \n### Methods  \n  \n#### DrawArrowGeometry(StreamGeometryContext, Point, Point, ConnectionDirection, ArrowHeadShape, Orientation)  \n  \n```csharp  \nprotected virtual void DrawArrowGeometry(StreamGeometryContext context, Point source, Point target, ConnectionDirection arrowDirection = 0, ArrowHeadShape shape = 0, Orientation orientation = 0);  \n```  \n  \n**Parameters**  \n  \n`context` [StreamGeometryContext](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Media.StreamGeometryContext)  \n  \n`source` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n`target` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n`arrowDirection` [ConnectionDirection](#connectiondirection-enum)  \n  \n`shape` [ArrowHeadShape](#arrowheadshape-enum)  \n  \n`orientation` [Orientation](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Controls.Orientation)  \n  \n#### DrawDefaultArrowhead(StreamGeometryContext, Point, Point, ConnectionDirection, Orientation)  \n  \n```csharp  \nprotected virtual void DrawDefaultArrowhead(StreamGeometryContext context, Point source, Point target, ConnectionDirection arrowDirection = 0, Orientation orientation = 0);  \n```  \n  \n**Parameters**  \n  \n`context` [StreamGeometryContext](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Media.StreamGeometryContext)  \n  \n`source` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n`target` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n`arrowDirection` [ConnectionDirection](#connectiondirection-enum)  \n  \n`orientation` [Orientation](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Controls.Orientation)  \n  \n#### DrawDirectionalArrowheadGeometry(StreamGeometryContext, Vector, Point)  \n  \n```csharp  \nprotected virtual void DrawDirectionalArrowheadGeometry(StreamGeometryContext context, Vector direction, Point location);  \n```  \n  \n**Parameters**  \n  \n`context` [StreamGeometryContext](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Media.StreamGeometryContext)  \n  \n`direction` [Vector](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Vector)  \n  \n`location` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n#### DrawDirectionalArrowsGeometry(StreamGeometryContext, Point, Point)  \n  \n```csharp  \nprotected virtual void DrawDirectionalArrowsGeometry(StreamGeometryContext context, Point source, Point target);  \n```  \n  \n**Parameters**  \n  \n`context` [StreamGeometryContext](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Media.StreamGeometryContext)  \n  \n`source` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n`target` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n#### DrawEllipseArrowhead(StreamGeometryContext, Point, Point, ConnectionDirection, Orientation)  \n  \n```csharp  \nprotected virtual void DrawEllipseArrowhead(StreamGeometryContext context, Point source, Point target, ConnectionDirection arrowDirection = 0, Orientation orientation = 0);  \n```  \n  \n**Parameters**  \n  \n`context` [StreamGeometryContext](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Media.StreamGeometryContext)  \n  \n`source` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n`target` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n`arrowDirection` [ConnectionDirection](#connectiondirection-enum)  \n  \n`orientation` [Orientation](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Controls.Orientation)  \n  \n#### DrawLineGeometry(StreamGeometryContext, Point, Point)  \n  \n```csharp  \nprotected virtual ValueTuple<ValueTuple<Point, Point>, ValueTuple<Point, Point>> DrawLineGeometry(StreamGeometryContext context, Point source, Point target);  \n```  \n  \n**Parameters**  \n  \n`context` [StreamGeometryContext](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Media.StreamGeometryContext)  \n  \n`source` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n`target` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n**Returns**  \n  \n[ValueTuple<ValueTuple<Point, Point>, ValueTuple<Point, Point>>](https://docs.microsoft.com/en-us/dotnet/api/System.ValueTuple)  \n  \n#### DrawRectangleArrowhead(StreamGeometryContext, Point, Point, ConnectionDirection, Orientation)  \n  \n```csharp  \nprotected virtual void DrawRectangleArrowhead(StreamGeometryContext context, Point source, Point target, ConnectionDirection arrowDirection = 0, Orientation orientation = 0);  \n```  \n  \n**Parameters**  \n  \n`context` [StreamGeometryContext](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Media.StreamGeometryContext)  \n  \n`source` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n`target` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n`arrowDirection` [ConnectionDirection](#connectiondirection-enum)  \n  \n`orientation` [Orientation](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Controls.Orientation)  \n  \n#### GetIsSelectable(UIElement)  \n  \n```csharp  \npublic static bool GetIsSelectable(UIElement elem);  \n```  \n  \n**Parameters**  \n  \n`elem` [UIElement](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.UIElement)  \n  \n**Returns**  \n  \n[Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean)  \n  \n#### GetIsSelected(UIElement)  \n  \n```csharp  \npublic static bool GetIsSelected(UIElement elem);  \n```  \n  \n**Parameters**  \n  \n`elem` [UIElement](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.UIElement)  \n  \n**Returns**  \n  \n[Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean)  \n  \n#### GetOffset()  \n  \nGets the resulting offset after applying the [BaseConnection.SourceOffsetMode](#baseconnection-class#sourceoffsetmode).  \n  \n```csharp  \nprotected virtual ValueTuple<Vector, Vector> GetOffset();  \n```  \n  \n**Returns**  \n  \n[ValueTuple<Vector, Vector>](https://docs.microsoft.com/en-us/dotnet/api/System.ValueTuple)  \n  \n#### GetTextPosition(FormattedText, Point, Point)  \n  \n```csharp  \nprotected virtual Point GetTextPosition(FormattedText text, Point source, Point target);  \n```  \n  \n**Parameters**  \n  \n`text` [FormattedText](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Media.FormattedText)  \n  \n`source` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n`target` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n**Returns**  \n  \n[Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n#### OnMouseDown(MouseButtonEventArgs)  \n  \n```csharp  \nprotected override void OnMouseDown(MouseButtonEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [MouseButtonEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.MouseButtonEventArgs)  \n  \n#### OnMouseUp(MouseButtonEventArgs)  \n  \n```csharp  \nprotected override void OnMouseUp(MouseButtonEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [MouseButtonEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.MouseButtonEventArgs)  \n  \n#### OnRender(DrawingContext)  \n  \n```csharp  \nprotected override void OnRender(DrawingContext drawingContext);  \n```  \n  \n**Parameters**  \n  \n`drawingContext` [DrawingContext](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Media.DrawingContext)  \n  \n#### SetIsSelectable(UIElement, Boolean)  \n  \n```csharp  \npublic static void SetIsSelectable(UIElement elem, bool value);  \n```  \n  \n**Parameters**  \n  \n`elem` [UIElement](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.UIElement)  \n  \n`value` [Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean)  \n  \n#### SetIsSelected(UIElement, Boolean)  \n  \n```csharp  \npublic static void SetIsSelected(UIElement elem, bool value);  \n```  \n  \n**Parameters**  \n  \n`elem` [UIElement](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.UIElement)  \n  \n`value` [Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean)  \n  \n#### StartAnimation(Double)  \n  \nStarts animating the directional arrows.  \n  \n```csharp  \npublic void StartAnimation(double duration = 1.5d);  \n```  \n  \n**Parameters**  \n  \n`duration` [Double](https://docs.microsoft.com/en-us/dotnet/api/System.Double): The duration for moving an arrowhead from [BaseConnection.Source](#baseconnection-class#source) to [BaseConnection.Target](#baseconnection-class#target).  \n  \n#### StopAnimation()  \n  \nStops the animation started by Nodify.BaseConnection.StartAnimation(System.Double)  \n  \n```csharp  \npublic void StopAnimation();  \n```  \n  \n### Events  \n  \n#### Disconnect  \n  \nTriggered by the Nodify.EditorGestures.ConnectionGestures.Disconnect gesture.  \n  \n```csharp  \npublic event ConnectionEventHandler Disconnect;  \n```  \n  \n**Event Type**  \n  \n[ConnectionEventHandler](#connectioneventhandler-delegate)  \n  \n#### Split  \n  \nTriggered by the Nodify.EditorGestures.ConnectionGestures.Split gesture.  \n  \n```csharp  \npublic event ConnectionEventHandler Split;  \n```  \n  \n**Event Type**  \n  \n[ConnectionEventHandler](#connectioneventhandler-delegate)  \n  \n## BoxValue Class  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**Inheritance:** [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object) → [BoxValue](#boxvalue-class)  \n  \n```csharp  \npublic static class BoxValue  \n```  \n  \n### Fields  \n  \n#### ArrowSize  \n  \n```csharp  \npublic static object ArrowSize;  \n```  \n  \n**Field Value**  \n  \n[Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object)  \n  \n#### ConnectionOffset  \n  \n```csharp  \npublic static object ConnectionOffset;  \n```  \n  \n**Field Value**  \n  \n[Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object)  \n  \n#### Double0  \n  \n```csharp  \npublic static object Double0;  \n```  \n  \n**Field Value**  \n  \n[Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object)  \n  \n#### Double1  \n  \n```csharp  \npublic static object Double1;  \n```  \n  \n**Field Value**  \n  \n[Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object)  \n  \n#### Double1000  \n  \n```csharp  \npublic static object Double1000;  \n```  \n  \n**Field Value**  \n  \n[Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object)  \n  \n#### Double2  \n  \n```csharp  \npublic static object Double2;  \n```  \n  \n**Field Value**  \n  \n[Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object)  \n  \n#### Double45  \n  \n```csharp  \npublic static object Double45;  \n```  \n  \n**Field Value**  \n  \n[Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object)  \n  \n#### Double5  \n  \n```csharp  \npublic static object Double5;  \n```  \n  \n**Field Value**  \n  \n[Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object)  \n  \n#### DoubleHalf  \n  \n```csharp  \npublic static object DoubleHalf;  \n```  \n  \n**Field Value**  \n  \n[Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object)  \n  \n#### False  \n  \n```csharp  \npublic static object False;  \n```  \n  \n**Field Value**  \n  \n[Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object)  \n  \n#### Int0  \n  \n```csharp  \npublic static object Int0;  \n```  \n  \n**Field Value**  \n  \n[Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object)  \n  \n#### Int1  \n  \n```csharp  \npublic static object Int1;  \n```  \n  \n**Field Value**  \n  \n[Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object)  \n  \n#### Point  \n  \n```csharp  \npublic static object Point;  \n```  \n  \n**Field Value**  \n  \n[Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object)  \n  \n#### Rect  \n  \n```csharp  \npublic static object Rect;  \n```  \n  \n**Field Value**  \n  \n[Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object)  \n  \n#### Size  \n  \n```csharp  \npublic static object Size;  \n```  \n  \n**Field Value**  \n  \n[Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object)  \n  \n#### Thickness2  \n  \n```csharp  \npublic static object Thickness2;  \n```  \n  \n**Field Value**  \n  \n[Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object)  \n  \n#### True  \n  \n```csharp  \npublic static object True;  \n```  \n  \n**Field Value**  \n  \n[Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object)  \n  \n#### UInt0  \n  \n```csharp  \npublic static object UInt0;  \n```  \n  \n**Field Value**  \n  \n[Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object)  \n  \n#### UInt1  \n  \n```csharp  \npublic static object UInt1;  \n```  \n  \n**Field Value**  \n  \n[Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object)  \n  \n## CircuitConnection Class  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**Inheritance:** [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object) → [DispatcherObject](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Threading.DispatcherObject) → [DependencyObject](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.DependencyObject) → [Visual](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Media.Visual) → [UIElement](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.UIElement) → [FrameworkElement](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.FrameworkElement) → [Shape](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Shapes.Shape) → [BaseConnection](#baseconnection-class) → [LineConnection](#lineconnection-class) → [CircuitConnection](#circuitconnection-class)  \n  \nRepresents a line that is controlled by an angle.  \n  \n```csharp  \npublic class CircuitConnection : LineConnection  \n```  \n  \n### Constructors  \n  \n#### CircuitConnection()  \n  \n```csharp  \npublic CircuitConnection();  \n```  \n  \n### Fields  \n  \n#### Degrees  \n  \n```csharp  \nprotected const double Degrees = 0.017453292519943295d;  \n```  \n  \n**Field Value**  \n  \n[Double](https://docs.microsoft.com/en-us/dotnet/api/System.Double)  \n  \n### Properties  \n  \n#### Angle  \n  \nThe angle of the connection in degrees.  \n  \n```csharp  \npublic double Angle { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Double](https://docs.microsoft.com/en-us/dotnet/api/System.Double)  \n  \n### Methods  \n  \n#### DrawDirectionalArrowsGeometry(StreamGeometryContext, Point, Point)  \n  \n```csharp  \nprotected override void DrawDirectionalArrowsGeometry(StreamGeometryContext context, Point source, Point target);  \n```  \n  \n**Parameters**  \n  \n`context` [StreamGeometryContext](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Media.StreamGeometryContext)  \n  \n`source` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n`target` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n#### DrawLineGeometry(StreamGeometryContext, Point, Point)  \n  \n```csharp  \nprotected override ValueTuple<ValueTuple<Point, Point>, ValueTuple<Point, Point>> DrawLineGeometry(StreamGeometryContext context, Point source, Point target);  \n```  \n  \n**Parameters**  \n  \n`context` [StreamGeometryContext](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Media.StreamGeometryContext)  \n  \n`source` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n`target` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n**Returns**  \n  \n[ValueTuple<ValueTuple<Point, Point>, ValueTuple<Point, Point>>](https://docs.microsoft.com/en-us/dotnet/api/System.ValueTuple)  \n  \n#### GetTextPosition(FormattedText, Point, Point)  \n  \n```csharp  \nprotected override Point GetTextPosition(FormattedText text, Point source, Point target);  \n```  \n  \n**Parameters**  \n  \n`text` [FormattedText](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Media.FormattedText)  \n  \n`source` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n`target` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n**Returns**  \n  \n[Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n## Connection Class  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**Inheritance:** [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object) → [DispatcherObject](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Threading.DispatcherObject) → [DependencyObject](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.DependencyObject) → [Visual](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Media.Visual) → [UIElement](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.UIElement) → [FrameworkElement](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.FrameworkElement) → [Shape](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Shapes.Shape) → [BaseConnection](#baseconnection-class) → [Connection](#connection-class)  \n  \n**References:** [Connector](#connector-class), [NodifyEditor](#nodifyeditor-class)  \n  \nRepresents a cubic bezier curve.  \n  \n```csharp  \npublic class Connection : BaseConnection  \n```  \n  \n### Constructors  \n  \n#### Connection()  \n  \n```csharp  \npublic Connection();  \n```  \n  \n### Methods  \n  \n#### DrawDirectionalArrowsGeometry(StreamGeometryContext, Point, Point)  \n  \n```csharp  \nprotected override void DrawDirectionalArrowsGeometry(StreamGeometryContext context, Point source, Point target);  \n```  \n  \n**Parameters**  \n  \n`context` [StreamGeometryContext](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Media.StreamGeometryContext)  \n  \n`source` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n`target` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n#### DrawLineGeometry(StreamGeometryContext, Point, Point)  \n  \n```csharp  \nprotected override ValueTuple<ValueTuple<Point, Point>, ValueTuple<Point, Point>> DrawLineGeometry(StreamGeometryContext context, Point source, Point target);  \n```  \n  \n**Parameters**  \n  \n`context` [StreamGeometryContext](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Media.StreamGeometryContext)  \n  \n`source` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n`target` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n**Returns**  \n  \n[ValueTuple<ValueTuple<Point, Point>, ValueTuple<Point, Point>>](https://docs.microsoft.com/en-us/dotnet/api/System.ValueTuple)  \n  \n#### GetTextPosition(FormattedText, Point, Point)  \n  \n```csharp  \nprotected override Point GetTextPosition(FormattedText text, Point source, Point target);  \n```  \n  \n**Parameters**  \n  \n`text` [FormattedText](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Media.FormattedText)  \n  \n`source` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n`target` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n**Returns**  \n  \n[Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n#### InterpolateCubicBezier(Point, Point, Point, Point, Double)  \n  \n```csharp  \nprotected static Point InterpolateCubicBezier(Point P0, Point P1, Point P2, Point P3, double t);  \n```  \n  \n**Parameters**  \n  \n`P0` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n`P1` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n`P2` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n`P3` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n`t` [Double](https://docs.microsoft.com/en-us/dotnet/api/System.Double)  \n  \n**Returns**  \n  \n[Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n## ConnectionDirection Enum  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**References:** [BaseConnection](#baseconnection-class), [LineConnection](#lineconnection-class), [PendingConnection](#pendingconnection-class)  \n  \nThe direction in which a connection is oriented.  \n  \n```csharp  \npublic enum ConnectionDirection  \n```  \n  \n### Fields  \n  \n#### Backward  \n  \nFrom [BaseConnection.Target](#baseconnection-class#target) to [BaseConnection.Source](#baseconnection-class#source).  \n  \n```csharp  \nBackward = 1;  \n```  \n  \n#### Forward  \n  \nFrom [BaseConnection.Source](#baseconnection-class#source) to [BaseConnection.Target](#baseconnection-class#target).  \n  \n```csharp  \nForward = 0;  \n```  \n  \n## ConnectionEventArgs Class  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**Inheritance:** [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object) → [EventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.EventArgs) → [RoutedEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.RoutedEventArgs) → [ConnectionEventArgs](#connectioneventargs-class)  \n  \n**References:** [BaseConnection](#baseconnection-class), [ConnectionEventHandler](#connectioneventhandler-delegate)  \n  \nProvides data for [BaseConnection](#baseconnection-class) related routed events.  \n  \n```csharp  \npublic class ConnectionEventArgs : RoutedEventArgs  \n```  \n  \n### Constructors  \n  \n#### ConnectionEventArgs(Object)  \n  \nInitializes a new instance of the [ConnectionEventArgs](#connectioneventargs-class) class using the specified [ConnectionEventArgs.Connection](#connectioneventargs-class#connection).  \n  \n```csharp  \npublic ConnectionEventArgs(object connection);  \n```  \n  \n**Parameters**  \n  \n`connection` [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object): The [FrameworkElement.DataContext](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.FrameworkElement.datacontext) of a related [BaseConnection](#baseconnection-class).  \n  \n### Properties  \n  \n#### Connection  \n  \nGets the [FrameworkElement.DataContext](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.FrameworkElement.datacontext) of the [BaseConnection](#baseconnection-class) associated with this event.  \n  \n```csharp  \npublic object Connection { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object)  \n  \n#### SplitLocation  \n  \nGets or sets the location where the connection should be split.  \n  \n```csharp  \npublic Point SplitLocation { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n### Methods  \n  \n#### InvokeEventHandler(Delegate, Object)  \n  \n```csharp  \nprotected override void InvokeEventHandler(Delegate genericHandler, object genericTarget);  \n```  \n  \n**Parameters**  \n  \n`genericHandler` [Delegate](https://docs.microsoft.com/en-us/dotnet/api/System.Delegate)  \n  \n`genericTarget` [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object)  \n  \n## ConnectionEventHandler Delegate  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**Inheritance:** [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object) → [Delegate](https://docs.microsoft.com/en-us/dotnet/api/System.Delegate) → [MulticastDelegate](https://docs.microsoft.com/en-us/dotnet/api/System.MulticastDelegate) → [ConnectionEventHandler](#connectioneventhandler-delegate)  \n  \n**References:** [BaseConnection](#baseconnection-class), [ConnectionEventArgs](#connectioneventargs-class)  \n  \nRepresents the method that will handle [BaseConnection](#baseconnection-class) related routed events.  \n  \n```csharp  \npublic delegate void ConnectionEventHandler(object sender, ConnectionEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`sender` [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object): The object where the event handler is attached.  \n  \n`e` [ConnectionEventArgs](#connectioneventargs-class): The event data.  \n  \n## ConnectionGestures Class  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**Inheritance:** [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object) → [ConnectionGestures](#connectiongestures-class)  \n  \n**References:** [EditorGestures](#editorgestures-class), [InputGestureRef](#inputgestureref-class), [SelectionGestures](#selectiongestures-class)  \n  \n```csharp  \npublic class ConnectionGestures  \n```  \n  \n### Constructors  \n  \n#### ConnectionGestures()  \n  \n```csharp  \npublic ConnectionGestures();  \n```  \n  \n### Properties  \n  \n#### Disconnect  \n  \n```csharp  \npublic InputGestureRef Disconnect { get; set; }  \n```  \n  \n**Property Value**  \n  \n[InputGestureRef](#inputgestureref-class)  \n  \n#### Selection  \n  \n```csharp  \npublic SelectionGestures Selection { get; set; }  \n```  \n  \n**Property Value**  \n  \n[SelectionGestures](#selectiongestures-class)  \n  \n#### Split  \n  \n```csharp  \npublic InputGestureRef Split { get; set; }  \n```  \n  \n**Property Value**  \n  \n[InputGestureRef](#inputgestureref-class)  \n  \n### Methods  \n  \n#### Apply(ConnectionGestures)  \n  \n```csharp  \npublic void Apply(ConnectionGestures gestures);  \n```  \n  \n**Parameters**  \n  \n`gestures` [ConnectionGestures](#connectiongestures-class)  \n  \n## ConnectionOffsetMode Enum  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**References:** [BaseConnection](#baseconnection-class)  \n  \nSpecifies the offset type that can be applied to a [BaseConnection](#baseconnection-class) using the [BaseConnection.SourceOffset](#baseconnection-class#sourceoffset) and the [BaseConnection.TargetOffset](#baseconnection-class#targetoffset) values.  \n  \n```csharp  \npublic enum ConnectionOffsetMode  \n```  \n  \n### Fields  \n  \n#### Circle  \n  \nThe offset is applied in a circle around the point.  \n  \n```csharp  \nCircle = 1;  \n```  \n  \n#### Edge  \n  \nThe offset is applied in a rectangle shape around the point, perpendicular to the edges.  \n  \n```csharp  \nEdge = 3;  \n```  \n  \n#### None  \n  \nNo offset applied.  \n  \n```csharp  \nNone = 0;  \n```  \n  \n#### Rectangle  \n  \nThe offset is applied in a rectangle shape around the point.  \n  \n```csharp  \nRectangle = 2;  \n```  \n  \n#### Static  \n  \nThe offset is applied as a fixed margin.  \n  \n```csharp  \nStatic = 4;  \n```  \n  \n## Connector Class  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**Inheritance:** [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object) → [DispatcherObject](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Threading.DispatcherObject) → [DependencyObject](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.DependencyObject) → [Visual](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Media.Visual) → [UIElement](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.UIElement) → [FrameworkElement](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.FrameworkElement) → [Control](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Controls.Control) → [Connector](#connector-class)  \n  \n**Derived:** [NodeInput](#nodeinput-class), [NodeOutput](#nodeoutput-class), [StateNode](#statenode-class)  \n  \n**References:** [Connection](#connection-class), [ConnectorEventArgs](#connectoreventargs-class), [ConnectorEventHandler](#connectoreventhandler-delegate), [ItemContainer](#itemcontainer-class), [KnotNode](#knotnode-class), [Node](#node-class), [NodifyEditor](#nodifyeditor-class), [PendingConnection](#pendingconnection-class), [PendingConnectionEventArgs](#pendingconnectioneventargs-class), [PendingConnectionEventHandler](#pendingconnectioneventhandler-delegate)  \n  \nRepresents a connector control that can start and complete a [PendingConnection](#pendingconnection-class).\n            Has a [Connector.ElementConnector](#connector-class#elementconnector) that the [Connector.Anchor](#connector-class#anchor) is calculated from for the [PendingConnection](#pendingconnection-class). Center of this control is used if missing.  \n  \n```csharp  \npublic class Connector : Control  \n```  \n  \n### Constructors  \n  \n#### Connector()  \n  \n```csharp  \npublic Connector();  \n```  \n  \n### Fields  \n  \n#### ElementConnector  \n  \n```csharp  \nprotected const string ElementConnector = \"PART_Connector\";  \n```  \n  \n**Field Value**  \n  \n[String](https://docs.microsoft.com/en-us/dotnet/api/System.String)  \n  \n#### EnableOptimizations  \n  \nGets or sets if [Connector](#connector-class)s should enable optimizations based on [Connector.OptimizeSafeZone](#connector-class#optimizesafezone) and [Connector.OptimizeMinimumSelectedItems](#connector-class#optimizeminimumselecteditems).  \n  \n```csharp  \npublic static bool EnableOptimizations;  \n```  \n  \n**Field Value**  \n  \n[Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean)  \n  \n#### OptimizeMinimumSelectedItems  \n  \nGets or sets the minimum selected items needed to trigger optimizations when outside of the [Connector.OptimizeSafeZone](#connector-class#optimizesafezone).  \n  \n```csharp  \npublic static uint OptimizeMinimumSelectedItems;  \n```  \n  \n**Field Value**  \n  \n[UInt32](https://docs.microsoft.com/en-us/dotnet/api/System.UInt32)  \n  \n#### OptimizeSafeZone  \n  \nGets or sets the safe zone outside the editor's viewport that will not trigger optimizations.  \n  \n```csharp  \npublic static double OptimizeSafeZone;  \n```  \n  \n**Field Value**  \n  \n[Double](https://docs.microsoft.com/en-us/dotnet/api/System.Double)  \n  \n### Properties  \n  \n#### AllowPendingConnectionCancellation  \n  \nGets or sets whether cancelling a pending connection is allowed.  \n  \n```csharp  \npublic static bool AllowPendingConnectionCancellation { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean)  \n  \n#### Anchor  \n  \nGets the location where [Connection](#connection-class)s can be attached to. \n            Bind with System.Windows.Data.BindingMode.OneWayToSource  \n  \n```csharp  \npublic Point Anchor { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n#### Container  \n  \nGets the [ItemContainer](#itemcontainer-class) that contains this [Connector](#connector-class).  \n  \n```csharp  \nprotected ItemContainer Container { get; set; }  \n```  \n  \n**Property Value**  \n  \n[ItemContainer](#itemcontainer-class)  \n  \n#### DisconnectCommand  \n  \nInvoked if the [Connector.Disconnect](#connector-class#disconnect) event is not handled.\n            Parameter is the [FrameworkElement.DataContext](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.FrameworkElement.datacontext) of this control.  \n  \n```csharp  \npublic ICommand DisconnectCommand { get; set; }  \n```  \n  \n**Property Value**  \n  \n[ICommand](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.ICommand)  \n  \n#### EnableStickyConnections  \n  \nGets or sets whether the connection should be completed in two steps.  \n  \n```csharp  \npublic static bool EnableStickyConnections { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean)  \n  \n#### IsConnected  \n  \nIf this is set to false, the [Connector.Disconnect](#connector-class#disconnect) event will not be invoked and the connector will stop updating its [Connector.Anchor](#connector-class#anchor) when moved, resized etc.  \n  \n```csharp  \npublic bool IsConnected { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean)  \n  \n#### IsPendingConnection  \n  \nGets a value that indicates whether a [PendingConnection](#pendingconnection-class) is in progress for this [Connector](#connector-class).  \n  \n```csharp  \npublic bool IsPendingConnection { get; protected set; }  \n```  \n  \n**Property Value**  \n  \n[Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean)  \n  \n#### Thumb  \n  \nGets the [FrameworkElement](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.FrameworkElement) used to calculate the [Connector.Anchor](#connector-class#anchor).  \n  \n```csharp  \nprotected FrameworkElement Thumb { get; set; }  \n```  \n  \n**Property Value**  \n  \n[FrameworkElement](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.FrameworkElement)  \n  \n### Methods  \n  \n#### OnApplyTemplate()  \n  \n```csharp  \npublic override void OnApplyTemplate();  \n```  \n  \n#### OnConnectorDrag(Vector)  \n  \n```csharp  \nprotected virtual void OnConnectorDrag(Vector offset);  \n```  \n  \n**Parameters**  \n  \n`offset` [Vector](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Vector)  \n  \n#### OnConnectorDragCompleted(Boolean)  \n  \n```csharp  \nprotected virtual void OnConnectorDragCompleted(bool cancel = false);  \n```  \n  \n**Parameters**  \n  \n`cancel` [Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean)  \n  \n#### OnConnectorDragStarted()  \n  \n```csharp  \nprotected virtual void OnConnectorDragStarted();  \n```  \n  \n#### OnDisconnect()  \n  \n```csharp  \nprotected virtual void OnDisconnect();  \n```  \n  \n#### OnKeyUp(KeyEventArgs)  \n  \n```csharp  \nprotected override void OnKeyUp(KeyEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [KeyEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.KeyEventArgs)  \n  \n#### OnLostMouseCapture(MouseEventArgs)  \n  \n```csharp  \nprotected override void OnLostMouseCapture(MouseEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [MouseEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.MouseEventArgs)  \n  \n#### OnMouseDown(MouseButtonEventArgs)  \n  \n```csharp  \nprotected override void OnMouseDown(MouseButtonEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [MouseButtonEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.MouseButtonEventArgs)  \n  \n#### OnMouseMove(MouseEventArgs)  \n  \n```csharp  \nprotected override void OnMouseMove(MouseEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [MouseEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.MouseEventArgs)  \n  \n#### OnMouseUp(MouseButtonEventArgs)  \n  \n```csharp  \nprotected override void OnMouseUp(MouseButtonEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [MouseButtonEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.MouseButtonEventArgs)  \n  \n#### OnRenderSizeChanged(SizeChangedInfo)  \n  \n```csharp  \nprotected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo);  \n```  \n  \n**Parameters**  \n  \n`sizeInfo` [SizeChangedInfo](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.SizeChangedInfo)  \n  \n#### UpdateAnchor(Point)  \n  \nUpdates the [Connector.Anchor](#connector-class#anchor) relative to a location. (usually [Connector.Container](#connector-class#container)'s location)  \n  \n```csharp  \nprotected void UpdateAnchor(Point location);  \n```  \n  \n**Parameters**  \n  \n`location` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point): The relative location  \n  \n#### UpdateAnchor()  \n  \nUpdates the [Connector.Anchor](#connector-class#anchor) based on [Connector.Container](#connector-class#container)'s location.  \n  \n```csharp  \npublic void UpdateAnchor();  \n```  \n  \n#### UpdateAnchorOptimized(Point)  \n  \nUpdates the [Connector.Anchor](#connector-class#anchor) and applies optimizations if needed based on [Connector.EnableOptimizations](#connector-class#enableoptimizations) flag  \n  \n```csharp  \nprotected void UpdateAnchorOptimized(Point location);  \n```  \n  \n**Parameters**  \n  \n`location` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n### Events  \n  \n#### Disconnect  \n  \nTriggered by the Nodify.EditorGestures.ConnectorGestures.Disconnect gesture.  \n  \n```csharp  \npublic event ConnectorEventHandler Disconnect;  \n```  \n  \n**Event Type**  \n  \n[ConnectorEventHandler](#connectoreventhandler-delegate)  \n  \n#### PendingConnectionCompleted  \n  \nTriggered by the Nodify.EditorGestures.ConnectorGestures.Connect gesture.  \n  \n```csharp  \npublic event PendingConnectionEventHandler PendingConnectionCompleted;  \n```  \n  \n**Event Type**  \n  \n[PendingConnectionEventHandler](#pendingconnectioneventhandler-delegate)  \n  \n#### PendingConnectionDrag  \n  \nOccurs when the mouse is changing position and the [Connector](#connector-class) has mouse capture.  \n  \n```csharp  \npublic event PendingConnectionEventHandler PendingConnectionDrag;  \n```  \n  \n**Event Type**  \n  \n[PendingConnectionEventHandler](#pendingconnectioneventhandler-delegate)  \n  \n#### PendingConnectionStarted  \n  \nTriggered by the Nodify.EditorGestures.ConnectorGestures.Connect gesture.  \n  \n```csharp  \npublic event PendingConnectionEventHandler PendingConnectionStarted;  \n```  \n  \n**Event Type**  \n  \n[PendingConnectionEventHandler](#pendingconnectioneventhandler-delegate)  \n  \n## ConnectorEventArgs Class  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**Inheritance:** [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object) → [EventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.EventArgs) → [RoutedEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.RoutedEventArgs) → [ConnectorEventArgs](#connectoreventargs-class)  \n  \n**References:** [Connector](#connector-class), [ConnectorEventHandler](#connectoreventhandler-delegate)  \n  \nProvides data for [Connector](#connector-class) related routed events.  \n  \n```csharp  \npublic class ConnectorEventArgs : RoutedEventArgs  \n```  \n  \n### Constructors  \n  \n#### ConnectorEventArgs(Object)  \n  \nInitializes a new instance of the [ConnectorEventArgs](#connectoreventargs-class) class using the specified [ConnectorEventArgs.Connector](#connectoreventargs-class#connector).  \n  \n```csharp  \npublic ConnectorEventArgs(object connector);  \n```  \n  \n**Parameters**  \n  \n`connector` [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object): The [FrameworkElement.DataContext](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.FrameworkElement.datacontext) of a related [Connector](#connector-class).  \n  \n### Properties  \n  \n#### Anchor  \n  \nGets or sets the [Connector.Anchor](#connector-class#anchor) of the [Connector](#connector-class) associated with this event.  \n  \n```csharp  \npublic Point Anchor { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n#### Connector  \n  \nGets the [FrameworkElement.DataContext](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.FrameworkElement.datacontext) of the [Connector](#connector-class) associated with this event.  \n  \n```csharp  \npublic object Connector { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object)  \n  \n### Methods  \n  \n#### InvokeEventHandler(Delegate, Object)  \n  \n```csharp  \nprotected override void InvokeEventHandler(Delegate genericHandler, object genericTarget);  \n```  \n  \n**Parameters**  \n  \n`genericHandler` [Delegate](https://docs.microsoft.com/en-us/dotnet/api/System.Delegate)  \n  \n`genericTarget` [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object)  \n  \n## ConnectorEventHandler Delegate  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**Inheritance:** [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object) → [Delegate](https://docs.microsoft.com/en-us/dotnet/api/System.Delegate) → [MulticastDelegate](https://docs.microsoft.com/en-us/dotnet/api/System.MulticastDelegate) → [ConnectorEventHandler](#connectoreventhandler-delegate)  \n  \n**References:** [Connector](#connector-class), [ConnectorEventArgs](#connectoreventargs-class)  \n  \nRepresents the method that will handle [Connector](#connector-class) related routed events.  \n  \n```csharp  \npublic delegate void ConnectorEventHandler(object sender, ConnectorEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`sender` [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object): The object where the event handler is attached.  \n  \n`e` [ConnectorEventArgs](#connectoreventargs-class): The event data.  \n  \n## ConnectorGestures Class  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**Inheritance:** [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object) → [ConnectorGestures](#connectorgestures-class)  \n  \n**References:** [EditorGestures](#editorgestures-class), [InputGestureRef](#inputgestureref-class)  \n  \n```csharp  \npublic class ConnectorGestures  \n```  \n  \n### Constructors  \n  \n#### ConnectorGestures()  \n  \n```csharp  \npublic ConnectorGestures();  \n```  \n  \n### Properties  \n  \n#### CancelAction  \n  \n```csharp  \npublic InputGestureRef CancelAction { get; set; }  \n```  \n  \n**Property Value**  \n  \n[InputGestureRef](#inputgestureref-class)  \n  \n#### Connect  \n  \n```csharp  \npublic InputGestureRef Connect { get; set; }  \n```  \n  \n**Property Value**  \n  \n[InputGestureRef](#inputgestureref-class)  \n  \n#### Disconnect  \n  \n```csharp  \npublic InputGestureRef Disconnect { get; set; }  \n```  \n  \n**Property Value**  \n  \n[InputGestureRef](#inputgestureref-class)  \n  \n### Methods  \n  \n#### Apply(ConnectorGestures)  \n  \n```csharp  \npublic void Apply(ConnectorGestures gestures);  \n```  \n  \n**Parameters**  \n  \n`gestures` [ConnectorGestures](#connectorgestures-class)  \n  \n## ConnectorPosition Enum  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**References:** [StepConnection](#stepconnection-class)  \n  \n```csharp  \npublic enum ConnectorPosition  \n```  \n  \n### Fields  \n  \n#### Bottom  \n  \n```csharp  \nBottom = 2;  \n```  \n  \n#### Left  \n  \n```csharp  \nLeft = 1;  \n```  \n  \n#### Right  \n  \n```csharp  \nRight = 3;  \n```  \n  \n#### Top  \n  \n```csharp  \nTop = 0;  \n```  \n  \n## ContainerDefaultState Class  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**Inheritance:** [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object) → [ContainerState](#containerstate-class) → [ContainerDefaultState](#containerdefaultstate-class)  \n  \n**References:** [ItemContainer](#itemcontainer-class)  \n  \nThe default state of the [ItemContainer](#itemcontainer-class).  \n  \n```csharp  \npublic class ContainerDefaultState : ContainerState  \n```  \n  \n### Constructors  \n  \n#### ContainerDefaultState(ItemContainer)  \n  \nCreates a new instance of the [ContainerDefaultState](#containerdefaultstate-class).  \n  \n```csharp  \npublic ContainerDefaultState(ItemContainer container);  \n```  \n  \n**Parameters**  \n  \n`container` [ItemContainer](#itemcontainer-class): The owner of the state.  \n  \n### Methods  \n  \n#### HandleMouseDown(MouseButtonEventArgs)  \n  \n```csharp  \npublic override void HandleMouseDown(MouseButtonEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [MouseButtonEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.MouseButtonEventArgs)  \n  \n#### HandleMouseMove(MouseEventArgs)  \n  \n```csharp  \npublic override void HandleMouseMove(MouseEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [MouseEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.MouseEventArgs)  \n  \n#### HandleMouseUp(MouseButtonEventArgs)  \n  \n```csharp  \npublic override void HandleMouseUp(MouseButtonEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [MouseButtonEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.MouseButtonEventArgs)  \n  \n#### ReEnter(ContainerState)  \n  \n```csharp  \npublic override void ReEnter(ContainerState from);  \n```  \n  \n**Parameters**  \n  \n`from` [ContainerState](#containerstate-class)  \n  \n## ContainerDraggingState Class  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**Inheritance:** [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object) → [ContainerState](#containerstate-class) → [ContainerDraggingState](#containerdraggingstate-class)  \n  \n**References:** [ItemContainer](#itemcontainer-class)  \n  \nDragging state of the container.  \n  \n```csharp  \npublic class ContainerDraggingState : ContainerState  \n```  \n  \n### Constructors  \n  \n#### ContainerDraggingState(ItemContainer)  \n  \nConstructs an instance of the [ContainerDraggingState](#containerdraggingstate-class) state.  \n  \n```csharp  \npublic ContainerDraggingState(ItemContainer container);  \n```  \n  \n**Parameters**  \n  \n`container` [ItemContainer](#itemcontainer-class): The owner of the state.  \n  \n### Properties  \n  \n#### Canceled  \n  \n```csharp  \npublic bool Canceled { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean)  \n  \n### Methods  \n  \n#### Enter(ContainerState)  \n  \n```csharp  \npublic override void Enter(ContainerState from);  \n```  \n  \n**Parameters**  \n  \n`from` [ContainerState](#containerstate-class)  \n  \n#### Exit()  \n  \n```csharp  \npublic override void Exit();  \n```  \n  \n#### HandleKeyUp(KeyEventArgs)  \n  \n```csharp  \npublic override void HandleKeyUp(KeyEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [KeyEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.KeyEventArgs)  \n  \n#### HandleMouseMove(MouseEventArgs)  \n  \n```csharp  \npublic override void HandleMouseMove(MouseEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [MouseEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.MouseEventArgs)  \n  \n#### HandleMouseUp(MouseButtonEventArgs)  \n  \n```csharp  \npublic override void HandleMouseUp(MouseButtonEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [MouseButtonEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.MouseButtonEventArgs)  \n  \n## ContainerState Class  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**Inheritance:** [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object) → [ContainerState](#containerstate-class)  \n  \n**Derived:** [ContainerDefaultState](#containerdefaultstate-class), [ContainerDraggingState](#containerdraggingstate-class)  \n  \n**References:** [ContainerDefaultState](#containerdefaultstate-class), [ContainerDraggingState](#containerdraggingstate-class), [ItemContainer](#itemcontainer-class), [NodifyEditor](#nodifyeditor-class)  \n  \nThe base class for container states.  \n  \n```csharp  \npublic abstract class ContainerState  \n```  \n  \n### Constructors  \n  \n#### ContainerState(ItemContainer)  \n  \nConstructs a new [ContainerState](#containerstate-class).  \n  \n```csharp  \npublic ContainerState(ItemContainer container);  \n```  \n  \n**Parameters**  \n  \n`container` [ItemContainer](#itemcontainer-class): The owner of the state.  \n  \n### Properties  \n  \n#### Container  \n  \nThe owner of the state.  \n  \n```csharp  \nprotected ItemContainer Container { get; set; }  \n```  \n  \n**Property Value**  \n  \n[ItemContainer](#itemcontainer-class)  \n  \n#### Editor  \n  \nThe owner of the state.  \n  \n```csharp  \nprotected NodifyEditor Editor { get; set; }  \n```  \n  \n**Property Value**  \n  \n[NodifyEditor](#nodifyeditor-class)  \n  \n### Methods  \n  \n#### Enter(ContainerState)  \n  \nCalled when Nodify.ItemContainer.PushState(Nodify.ContainerState) or Nodify.ItemContainer.PopState is called.  \n  \n```csharp  \npublic virtual void Enter(ContainerState from);  \n```  \n  \n**Parameters**  \n  \n`from` [ContainerState](#containerstate-class): The state we enter from (is null for root state).  \n  \n#### Exit()  \n  \nCalled when Nodify.ItemContainer.PopState is called.  \n  \n```csharp  \npublic virtual void Exit();  \n```  \n  \n#### HandleKeyDown(KeyEventArgs)  \n  \n```csharp  \npublic virtual void HandleKeyDown(KeyEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [KeyEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.KeyEventArgs)  \n  \n#### HandleKeyUp(KeyEventArgs)  \n  \n```csharp  \npublic virtual void HandleKeyUp(KeyEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [KeyEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.KeyEventArgs)  \n  \n#### HandleMouseDown(MouseButtonEventArgs)  \n  \n```csharp  \npublic virtual void HandleMouseDown(MouseButtonEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [MouseButtonEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.MouseButtonEventArgs)  \n  \n#### HandleMouseMove(MouseEventArgs)  \n  \n```csharp  \npublic virtual void HandleMouseMove(MouseEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [MouseEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.MouseEventArgs)  \n  \n#### HandleMouseUp(MouseButtonEventArgs)  \n  \n```csharp  \npublic virtual void HandleMouseUp(MouseButtonEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [MouseButtonEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.MouseButtonEventArgs)  \n  \n#### HandleMouseWheel(MouseWheelEventArgs)  \n  \n```csharp  \npublic virtual void HandleMouseWheel(MouseWheelEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [MouseWheelEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.MouseWheelEventArgs)  \n  \n#### PopState()  \n  \nPops the current state from the stack.  \n  \n```csharp  \npublic virtual void PopState();  \n```  \n  \n#### PushState(ContainerState)  \n  \nPushes a new state into the stack.  \n  \n```csharp  \npublic virtual void PushState(ContainerState newState);  \n```  \n  \n**Parameters**  \n  \n`newState` [ContainerState](#containerstate-class): The new state.  \n  \n#### ReEnter(ContainerState)  \n  \nCalled when Nodify.ItemContainer.PopState is called.  \n  \n```csharp  \npublic virtual void ReEnter(ContainerState from);  \n```  \n  \n**Parameters**  \n  \n`from` [ContainerState](#containerstate-class): The state we re-enter from.  \n  \n## CuttingLine Class  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**Inheritance:** [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object) → [DispatcherObject](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Threading.DispatcherObject) → [DependencyObject](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.DependencyObject) → [Visual](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Media.Visual) → [UIElement](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.UIElement) → [FrameworkElement](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.FrameworkElement) → [Shape](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Shapes.Shape) → [CuttingLine](#cuttingline-class)  \n  \n**References:** [BaseConnection](#baseconnection-class), [NodifyEditor](#nodifyeditor-class)  \n  \n```csharp  \npublic class CuttingLine : Shape  \n```  \n  \n### Constructors  \n  \n#### CuttingLine()  \n  \n```csharp  \npublic CuttingLine();  \n```  \n  \n### Properties  \n  \n#### AllowCuttingCancellation  \n  \nGets or sets whether cancelling a cutting operation is allowed.  \n  \n```csharp  \npublic static bool AllowCuttingCancellation { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean)  \n  \n#### DefiningGeometry  \n  \n```csharp  \nprotected override Geometry DefiningGeometry { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Geometry](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Media.Geometry)  \n  \n#### EndPoint  \n  \nGets or sets the end point.  \n  \n```csharp  \npublic Point EndPoint { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n#### StartPoint  \n  \nGets or sets the start point.  \n  \n```csharp  \npublic Point StartPoint { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n### Methods  \n  \n#### GetIsOverElement(UIElement)  \n  \n```csharp  \npublic static bool GetIsOverElement(UIElement elem);  \n```  \n  \n**Parameters**  \n  \n`elem` [UIElement](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.UIElement)  \n  \n**Returns**  \n  \n[Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean)  \n  \n#### OnRender(DrawingContext)  \n  \n```csharp  \nprotected override void OnRender(DrawingContext drawingContext);  \n```  \n  \n**Parameters**  \n  \n`drawingContext` [DrawingContext](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Media.DrawingContext)  \n  \n#### SetIsOverElement(UIElement, Boolean)  \n  \n```csharp  \npublic static void SetIsOverElement(UIElement elem, bool value);  \n```  \n  \n**Parameters**  \n  \n`elem` [UIElement](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.UIElement)  \n  \n`value` [Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean)  \n  \n## DecoratorContainer Class  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**Inheritance:** [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object) → [DispatcherObject](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Threading.DispatcherObject) → [DependencyObject](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.DependencyObject) → [Visual](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Media.Visual) → [UIElement](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.UIElement) → [FrameworkElement](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.FrameworkElement) → [Control](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Controls.Control) → [ContentControl](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Controls.ContentControl) → [DecoratorContainer](#decoratorcontainer-class)  \n  \n**Implements:** [INodifyCanvasItem](#inodifycanvasitem-interface)  \n  \n**References:** [NodifyEditor](#nodifyeditor-class)  \n  \nThe container for all the items generated from the [NodifyEditor.Decorators](#nodifyeditor-class#decorators) collection.  \n  \n```csharp  \npublic class DecoratorContainer : ContentControl, INodifyCanvasItem  \n```  \n  \n### Constructors  \n  \n#### DecoratorContainer()  \n  \n```csharp  \npublic DecoratorContainer();  \n```  \n  \n### Properties  \n  \n#### ActualSize  \n  \nGets the actual size of this [DecoratorContainer](#decoratorcontainer-class).  \n  \n```csharp  \npublic Size ActualSize { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Size](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Size)  \n  \n#### Location  \n  \nGets or sets the location of this [DecoratorContainer](#decoratorcontainer-class) inside the NodifyEditor.DecoratorsHost.  \n  \n```csharp  \npublic virtual Point Location { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n### Methods  \n  \n#### OnLocationChanged()  \n  \nRaises the [DecoratorContainer.LocationChangedEvent](#decoratorcontainer-class#locationchangedevent).  \n  \n```csharp  \nprotected void OnLocationChanged();  \n```  \n  \n#### OnRenderSizeChanged(SizeChangedInfo)  \n  \n```csharp  \nprotected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo);  \n```  \n  \n**Parameters**  \n  \n`sizeInfo` [SizeChangedInfo](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.SizeChangedInfo)  \n  \n### Events  \n  \n#### LocationChanged  \n  \nOccurs when the [DecoratorContainer.Location](#decoratorcontainer-class#location) of this [DecoratorContainer](#decoratorcontainer-class) is changed.  \n  \n```csharp  \npublic event RoutedEventHandler LocationChanged;  \n```  \n  \n**Event Type**  \n  \n[RoutedEventHandler](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.RoutedEventHandler)  \n  \n## EditorCommands Class  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**Inheritance:** [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object) → [EditorCommands](#editorcommands-class)  \n  \n**References:** [InputGestureRef](#inputgestureref-class), [ItemContainer](#itemcontainer-class), [NodifyEditor](#nodifyeditor-class)  \n  \n```csharp  \npublic static class EditorCommands  \n```  \n  \n### Properties  \n  \n#### Align  \n  \nAligns [NodifyEditor.SelectedItems](#nodifyeditor-class#selecteditems) using the specified alignment method.\n            Parameter is of type Nodify.EditorCommands.Alignment or a string that can be converted to an alignment.  \n  \n```csharp  \npublic static RoutedUICommand Align { get; set; }  \n```  \n  \n**Property Value**  \n  \n[RoutedUICommand](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.RoutedUICommand)  \n  \n#### BringIntoView  \n  \nMoves the [NodifyEditor.ViewportLocation](#nodifyeditor-class#viewportlocation) to the specified location.\n            Parameter is a [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point) or a string that can be converted to a point.  \n  \n```csharp  \npublic static RoutedUICommand BringIntoView { get; set; }  \n```  \n  \n**Property Value**  \n  \n[RoutedUICommand](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.RoutedUICommand)  \n  \n#### FitToScreen  \n  \nScales the editor's viewport to fit all the [ItemContainer](#itemcontainer-class)s if that's possible.  \n  \n```csharp  \npublic static RoutedUICommand FitToScreen { get; set; }  \n```  \n  \n**Property Value**  \n  \n[RoutedUICommand](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.RoutedUICommand)  \n  \n#### SelectAll  \n  \nSelect all [ItemContainer](#itemcontainer-class)s in the [NodifyEditor](#nodifyeditor-class).  \n  \n```csharp  \npublic static RoutedUICommand SelectAll { get; set; }  \n```  \n  \n**Property Value**  \n  \n[RoutedUICommand](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.RoutedUICommand)  \n  \n#### ZoomIn  \n  \nZoom in relative to the editor's viewport center.  \n  \n```csharp  \npublic static RoutedUICommand ZoomIn { get; set; }  \n```  \n  \n**Property Value**  \n  \n[RoutedUICommand](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.RoutedUICommand)  \n  \n#### ZoomOut  \n  \nZoom out relative to the editor's viewport center.  \n  \n```csharp  \npublic static RoutedUICommand ZoomOut { get; set; }  \n```  \n  \n**Property Value**  \n  \n[RoutedUICommand](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.RoutedUICommand)  \n  \n## EditorCuttingState Class  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**Inheritance:** [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object) → [EditorState](#editorstate-class) → [EditorCuttingState](#editorcuttingstate-class)  \n  \n**References:** [NodifyEditor](#nodifyeditor-class)  \n  \n```csharp  \npublic class EditorCuttingState : EditorState  \n```  \n  \n### Constructors  \n  \n#### EditorCuttingState(NodifyEditor)  \n  \n```csharp  \npublic EditorCuttingState(NodifyEditor editor);  \n```  \n  \n**Parameters**  \n  \n`editor` [NodifyEditor](#nodifyeditor-class)  \n  \n### Properties  \n  \n#### Canceled  \n  \n```csharp  \npublic bool Canceled { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean)  \n  \n### Methods  \n  \n#### Enter(EditorState)  \n  \n```csharp  \npublic override void Enter(EditorState from);  \n```  \n  \n**Parameters**  \n  \n`from` [EditorState](#editorstate-class)  \n  \n#### Exit()  \n  \n```csharp  \npublic override void Exit();  \n```  \n  \n#### HandleKeyUp(KeyEventArgs)  \n  \n```csharp  \npublic override void HandleKeyUp(KeyEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [KeyEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.KeyEventArgs)  \n  \n#### HandleMouseMove(MouseEventArgs)  \n  \n```csharp  \npublic override void HandleMouseMove(MouseEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [MouseEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.MouseEventArgs)  \n  \n#### HandleMouseUp(MouseButtonEventArgs)  \n  \n```csharp  \npublic override void HandleMouseUp(MouseButtonEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [MouseButtonEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.MouseButtonEventArgs)  \n  \n## EditorDefaultState Class  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**Inheritance:** [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object) → [EditorState](#editorstate-class) → [EditorDefaultState](#editordefaultstate-class)  \n  \n**References:** [NodifyEditor](#nodifyeditor-class)  \n  \nThe default state of the editor.\n              Default State\n              \t- mouse left down  \t-> Selecting State\n              \t- mouse right down  -> Panning State\n              Selecting State\n              \t- mouse left up \t-> Default State\n              \t- mouse right down \t-> Panning State\n              Panning State\n              \t- mouse right up\t-> previous state (Selecting State or Default State)\n              \t- mouse left up\t\t-> Panning State  \n  \n```csharp  \npublic class EditorDefaultState : EditorState  \n```  \n  \n### Constructors  \n  \n#### EditorDefaultState(NodifyEditor)  \n  \nConstructs an instance of the [EditorDefaultState](#editordefaultstate-class) state.  \n  \n```csharp  \npublic EditorDefaultState(NodifyEditor editor);  \n```  \n  \n**Parameters**  \n  \n`editor` [NodifyEditor](#nodifyeditor-class): The owner of the state.  \n  \n### Methods  \n  \n#### HandleMouseDown(MouseButtonEventArgs)  \n  \n```csharp  \npublic override void HandleMouseDown(MouseButtonEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [MouseButtonEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.MouseButtonEventArgs)  \n  \n#### HandleMouseWheel(MouseWheelEventArgs)  \n  \n```csharp  \npublic override void HandleMouseWheel(MouseWheelEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [MouseWheelEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.MouseWheelEventArgs)  \n  \n## EditorGestures Class  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**Inheritance:** [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object) → [EditorGestures](#editorgestures-class)  \n  \n**References:** [ConnectionGestures](#connectiongestures-class), [ConnectorGestures](#connectorgestures-class), [GroupingNodeGestures](#groupingnodegestures-class), [ItemContainerGestures](#itemcontainergestures-class), [MinimapGestures](#minimapgestures-class), [NodifyEditor](#nodifyeditor-class), [NodifyEditorGestures](#nodifyeditorgestures-class)  \n  \nGestures used by built-in controls inside the [NodifyEditor](#nodifyeditor-class).  \n  \n```csharp  \npublic class EditorGestures  \n```  \n  \n### Constructors  \n  \n#### EditorGestures()  \n  \n```csharp  \npublic EditorGestures();  \n```  \n  \n### Fields  \n  \n#### Mappings  \n  \n```csharp  \npublic static EditorGestures Mappings;  \n```  \n  \n**Field Value**  \n  \n[EditorGestures](#editorgestures-class)  \n  \n### Properties  \n  \n#### Connection  \n  \nGestures for the connection.  \n  \n```csharp  \npublic ConnectionGestures Connection { get; set; }  \n```  \n  \n**Property Value**  \n  \n[ConnectionGestures](#connectiongestures-class)  \n  \n#### Connector  \n  \nGestures for the connector.  \n  \n```csharp  \npublic ConnectorGestures Connector { get; set; }  \n```  \n  \n**Property Value**  \n  \n[ConnectorGestures](#connectorgestures-class)  \n  \n#### Editor  \n  \nGestures for the editor.  \n  \n```csharp  \npublic NodifyEditorGestures Editor { get; set; }  \n```  \n  \n**Property Value**  \n  \n[NodifyEditorGestures](#nodifyeditorgestures-class)  \n  \n#### GroupingNode  \n  \nGestures for the grouping node.  \n  \n```csharp  \npublic GroupingNodeGestures GroupingNode { get; set; }  \n```  \n  \n**Property Value**  \n  \n[GroupingNodeGestures](#groupingnodegestures-class)  \n  \n#### ItemContainer  \n  \nGestures for the item container.  \n  \n```csharp  \npublic ItemContainerGestures ItemContainer { get; set; }  \n```  \n  \n**Property Value**  \n  \n[ItemContainerGestures](#itemcontainergestures-class)  \n  \n#### Minimap  \n  \nGestures for the minimap.  \n  \n```csharp  \npublic MinimapGestures Minimap { get; set; }  \n```  \n  \n**Property Value**  \n  \n[MinimapGestures](#minimapgestures-class)  \n  \n### Methods  \n  \n#### Apply(EditorGestures)  \n  \nCopies from the specified gestures.  \n  \n```csharp  \npublic void Apply(EditorGestures gestures);  \n```  \n  \n**Parameters**  \n  \n`gestures` [EditorGestures](#editorgestures-class): The gestures to copy.  \n  \n## EditorPanningState Class  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**Inheritance:** [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object) → [EditorState](#editorstate-class) → [EditorPanningState](#editorpanningstate-class)  \n  \n**References:** [NodifyEditor](#nodifyeditor-class)  \n  \nThe panning state of the editor.  \n  \n```csharp  \npublic class EditorPanningState : EditorState  \n```  \n  \n### Constructors  \n  \n#### EditorPanningState(NodifyEditor)  \n  \nConstructs an instance of the [EditorPanningState](#editorpanningstate-class) state.  \n  \n```csharp  \npublic EditorPanningState(NodifyEditor editor);  \n```  \n  \n**Parameters**  \n  \n`editor` [NodifyEditor](#nodifyeditor-class): The owner of the state.  \n  \n### Methods  \n  \n#### Enter(EditorState)  \n  \n```csharp  \npublic override void Enter(EditorState from);  \n```  \n  \n**Parameters**  \n  \n`from` [EditorState](#editorstate-class)  \n  \n#### Exit()  \n  \n```csharp  \npublic override void Exit();  \n```  \n  \n#### HandleMouseMove(MouseEventArgs)  \n  \n```csharp  \npublic override void HandleMouseMove(MouseEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [MouseEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.MouseEventArgs)  \n  \n#### HandleMouseUp(MouseButtonEventArgs)  \n  \n```csharp  \npublic override void HandleMouseUp(MouseButtonEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [MouseButtonEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.MouseButtonEventArgs)  \n  \n## EditorPushingItemsState Class  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**Inheritance:** [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object) → [EditorState](#editorstate-class) → [EditorPushingItemsState](#editorpushingitemsstate-class)  \n  \n**References:** [NodifyEditor](#nodifyeditor-class)  \n  \n```csharp  \npublic class EditorPushingItemsState : EditorState  \n```  \n  \n### Constructors  \n  \n#### EditorPushingItemsState(NodifyEditor)  \n  \n```csharp  \npublic EditorPushingItemsState(NodifyEditor editor);  \n```  \n  \n**Parameters**  \n  \n`editor` [NodifyEditor](#nodifyeditor-class)  \n  \n### Properties  \n  \n#### Canceled  \n  \n```csharp  \npublic bool Canceled { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean)  \n  \n### Methods  \n  \n#### Enter(EditorState)  \n  \n```csharp  \npublic override void Enter(EditorState from);  \n```  \n  \n**Parameters**  \n  \n`from` [EditorState](#editorstate-class)  \n  \n#### Exit()  \n  \n```csharp  \npublic override void Exit();  \n```  \n  \n#### HandleKeyUp(KeyEventArgs)  \n  \n```csharp  \npublic override void HandleKeyUp(KeyEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [KeyEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.KeyEventArgs)  \n  \n#### HandleMouseMove(MouseEventArgs)  \n  \n```csharp  \npublic override void HandleMouseMove(MouseEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [MouseEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.MouseEventArgs)  \n  \n#### HandleMouseUp(MouseButtonEventArgs)  \n  \n```csharp  \npublic override void HandleMouseUp(MouseButtonEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [MouseButtonEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.MouseButtonEventArgs)  \n  \n## EditorSelectingState Class  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**Inheritance:** [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object) → [EditorState](#editorstate-class) → [EditorSelectingState](#editorselectingstate-class)  \n  \n**References:** [NodifyEditor](#nodifyeditor-class), [SelectionHelper](#selectionhelper-class), [SelectionType](#selectiontype-enum)  \n  \nThe selecting state of the editor.  \n  \n```csharp  \npublic class EditorSelectingState : EditorState  \n```  \n  \n### Constructors  \n  \n#### EditorSelectingState(NodifyEditor, SelectionType)  \n  \nConstructs an instance of the [EditorSelectingState](#editorselectingstate-class) state.  \n  \n```csharp  \npublic EditorSelectingState(NodifyEditor editor, SelectionType type);  \n```  \n  \n**Parameters**  \n  \n`editor` [NodifyEditor](#nodifyeditor-class): The owner of the state.  \n  \n`type` [SelectionType](#selectiontype-enum): The selection strategy.  \n  \n### Properties  \n  \n#### Selection  \n  \nThe selection helper.  \n  \n```csharp  \nprotected SelectionHelper Selection { get; set; }  \n```  \n  \n**Property Value**  \n  \n[SelectionHelper](#selectionhelper-class)  \n  \n### Methods  \n  \n#### Enter(EditorState)  \n  \n```csharp  \npublic override void Enter(EditorState from);  \n```  \n  \n**Parameters**  \n  \n`from` [EditorState](#editorstate-class)  \n  \n#### Exit()  \n  \n```csharp  \npublic override void Exit();  \n```  \n  \n#### HandleAutoPanning(MouseEventArgs)  \n  \n```csharp  \npublic override void HandleAutoPanning(MouseEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [MouseEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.MouseEventArgs)  \n  \n#### HandleKeyUp(KeyEventArgs)  \n  \n```csharp  \npublic override void HandleKeyUp(KeyEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [KeyEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.KeyEventArgs)  \n  \n#### HandleMouseDown(MouseButtonEventArgs)  \n  \n```csharp  \npublic override void HandleMouseDown(MouseButtonEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [MouseButtonEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.MouseButtonEventArgs)  \n  \n#### HandleMouseMove(MouseEventArgs)  \n  \n```csharp  \npublic override void HandleMouseMove(MouseEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [MouseEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.MouseEventArgs)  \n  \n#### HandleMouseUp(MouseButtonEventArgs)  \n  \n```csharp  \npublic override void HandleMouseUp(MouseButtonEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [MouseButtonEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.MouseButtonEventArgs)  \n  \n## EditorState Class  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**Inheritance:** [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object) → [EditorState](#editorstate-class)  \n  \n**Derived:** [EditorCuttingState](#editorcuttingstate-class), [EditorDefaultState](#editordefaultstate-class), [EditorPanningState](#editorpanningstate-class), [EditorPushingItemsState](#editorpushingitemsstate-class), [EditorSelectingState](#editorselectingstate-class)  \n  \n**References:** [EditorCuttingState](#editorcuttingstate-class), [EditorPanningState](#editorpanningstate-class), [EditorPushingItemsState](#editorpushingitemsstate-class), [EditorSelectingState](#editorselectingstate-class), [NodifyEditor](#nodifyeditor-class)  \n  \nThe base class for editor states.  \n  \n```csharp  \npublic abstract class EditorState  \n```  \n  \n### Constructors  \n  \n#### EditorState(NodifyEditor)  \n  \nConstructs a new [EditorState](#editorstate-class).  \n  \n```csharp  \npublic EditorState(NodifyEditor editor);  \n```  \n  \n**Parameters**  \n  \n`editor` [NodifyEditor](#nodifyeditor-class): The owner of the state.  \n  \n### Properties  \n  \n#### Editor  \n  \nThe owner of the state.  \n  \n```csharp  \nprotected NodifyEditor Editor { get; set; }  \n```  \n  \n**Property Value**  \n  \n[NodifyEditor](#nodifyeditor-class)  \n  \n### Methods  \n  \n#### Enter(EditorState)  \n  \nCalled when Nodify.NodifyEditor.PushState(Nodify.EditorState) is called.  \n  \n```csharp  \npublic virtual void Enter(EditorState from);  \n```  \n  \n**Parameters**  \n  \n`from` [EditorState](#editorstate-class): The state we enter from (is null for root state).  \n  \n#### Exit()  \n  \nCalled when Nodify.NodifyEditor.PopState is called.  \n  \n```csharp  \npublic virtual void Exit();  \n```  \n  \n#### HandleAutoPanning(MouseEventArgs)  \n  \nHandles auto panning when mouse is outside the editor.  \n  \n```csharp  \npublic virtual void HandleAutoPanning(MouseEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [MouseEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.MouseEventArgs): The [MouseEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.MouseEventArgs) that contains the event data.  \n  \n#### HandleKeyDown(KeyEventArgs)  \n  \n```csharp  \npublic virtual void HandleKeyDown(KeyEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [KeyEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.KeyEventArgs)  \n  \n#### HandleKeyUp(KeyEventArgs)  \n  \n```csharp  \npublic virtual void HandleKeyUp(KeyEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [KeyEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.KeyEventArgs)  \n  \n#### HandleMouseDown(MouseButtonEventArgs)  \n  \n```csharp  \npublic virtual void HandleMouseDown(MouseButtonEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [MouseButtonEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.MouseButtonEventArgs)  \n  \n#### HandleMouseMove(MouseEventArgs)  \n  \n```csharp  \npublic virtual void HandleMouseMove(MouseEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [MouseEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.MouseEventArgs)  \n  \n#### HandleMouseUp(MouseButtonEventArgs)  \n  \n```csharp  \npublic virtual void HandleMouseUp(MouseButtonEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [MouseButtonEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.MouseButtonEventArgs)  \n  \n#### HandleMouseWheel(MouseWheelEventArgs)  \n  \n```csharp  \npublic virtual void HandleMouseWheel(MouseWheelEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [MouseWheelEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.MouseWheelEventArgs)  \n  \n#### PopState()  \n  \nPops the current state from the stack.  \n  \n```csharp  \npublic virtual void PopState();  \n```  \n  \n#### PushState(EditorState)  \n  \nPushes a new state into the stack.  \n  \n```csharp  \npublic virtual void PushState(EditorState newState);  \n```  \n  \n**Parameters**  \n  \n`newState` [EditorState](#editorstate-class): The new state.  \n  \n#### ReEnter(EditorState)  \n  \nCalled when Nodify.NodifyEditor.PopState is called.  \n  \n```csharp  \npublic virtual void ReEnter(EditorState from);  \n```  \n  \n**Parameters**  \n  \n`from` [EditorState](#editorstate-class): The state we re-enter from.  \n  \n## GeneratedInternalTypeHelper Class  \n  \n**Namespace:** XamlGeneratedNamespace  \n  \n**Assembly:** Nodify  \n  \n**Inheritance:** [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object) → [InternalTypeHelper](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Markup.InternalTypeHelper) → [GeneratedInternalTypeHelper](#generatedinternaltypehelper-class)  \n  \nGeneratedInternalTypeHelper  \n  \n```csharp  \npublic sealed class GeneratedInternalTypeHelper : InternalTypeHelper  \n```  \n  \n### Constructors  \n  \n#### GeneratedInternalTypeHelper()  \n  \n```csharp  \npublic GeneratedInternalTypeHelper();  \n```  \n  \n### Methods  \n  \n#### AddEventHandler(EventInfo, Object, Delegate)  \n  \nAddEventHandler  \n  \n```csharp  \nprotected override void AddEventHandler(EventInfo eventInfo, object target, Delegate handler);  \n```  \n  \n**Parameters**  \n  \n`eventInfo` [EventInfo](https://docs.microsoft.com/en-us/dotnet/api/System.Reflection.EventInfo)  \n  \n`target` [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object)  \n  \n`handler` [Delegate](https://docs.microsoft.com/en-us/dotnet/api/System.Delegate)  \n  \n#### CreateDelegate(Type, Object, String)  \n  \nCreateDelegate  \n  \n```csharp  \nprotected override Delegate CreateDelegate(Type delegateType, object target, string handler);  \n```  \n  \n**Parameters**  \n  \n`delegateType` [Type](https://docs.microsoft.com/en-us/dotnet/api/System.Type)  \n  \n`target` [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object)  \n  \n`handler` [String](https://docs.microsoft.com/en-us/dotnet/api/System.String)  \n  \n**Returns**  \n  \n[Delegate](https://docs.microsoft.com/en-us/dotnet/api/System.Delegate)  \n  \n#### CreateInstance(Type, CultureInfo)  \n  \nCreateInstance  \n  \n```csharp  \nprotected override object CreateInstance(Type type, CultureInfo culture);  \n```  \n  \n**Parameters**  \n  \n`type` [Type](https://docs.microsoft.com/en-us/dotnet/api/System.Type)  \n  \n`culture` [CultureInfo](https://docs.microsoft.com/en-us/dotnet/api/System.Globalization.CultureInfo)  \n  \n**Returns**  \n  \n[Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object)  \n  \n#### GetPropertyValue(PropertyInfo, Object, CultureInfo)  \n  \nGetPropertyValue  \n  \n```csharp  \nprotected override object GetPropertyValue(PropertyInfo propertyInfo, object target, CultureInfo culture);  \n```  \n  \n**Parameters**  \n  \n`propertyInfo` [PropertyInfo](https://docs.microsoft.com/en-us/dotnet/api/System.Reflection.PropertyInfo)  \n  \n`target` [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object)  \n  \n`culture` [CultureInfo](https://docs.microsoft.com/en-us/dotnet/api/System.Globalization.CultureInfo)  \n  \n**Returns**  \n  \n[Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object)  \n  \n#### SetPropertyValue(PropertyInfo, Object, Object, CultureInfo)  \n  \nSetPropertyValue  \n  \n```csharp  \nprotected override void SetPropertyValue(PropertyInfo propertyInfo, object target, object value, CultureInfo culture);  \n```  \n  \n**Parameters**  \n  \n`propertyInfo` [PropertyInfo](https://docs.microsoft.com/en-us/dotnet/api/System.Reflection.PropertyInfo)  \n  \n`target` [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object)  \n  \n`value` [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object)  \n  \n`culture` [CultureInfo](https://docs.microsoft.com/en-us/dotnet/api/System.Globalization.CultureInfo)  \n  \n## GroupingMovementMode Enum  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**References:** [GroupingNode](#groupingnode-class)  \n  \nSpecifies the possible movement modes of a [GroupingNode](#groupingnode-class).  \n  \n```csharp  \npublic enum GroupingMovementMode  \n```  \n  \n### Fields  \n  \n#### Group  \n  \nThe [GroupingNode](#groupingnode-class) will move its content when moved.  \n  \n```csharp  \nGroup = 0;  \n```  \n  \n#### Self  \n  \nThe [GroupingNode](#groupingnode-class) will not move its content when moved.  \n  \n```csharp  \nSelf = 1;  \n```  \n  \n## GroupingNode Class  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**Inheritance:** [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object) → [DispatcherObject](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Threading.DispatcherObject) → [DependencyObject](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.DependencyObject) → [Visual](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Media.Visual) → [UIElement](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.UIElement) → [FrameworkElement](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.FrameworkElement) → [Control](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Controls.Control) → [ContentControl](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Controls.ContentControl) → [HeaderedContentControl](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Controls.HeaderedContentControl) → [GroupingNode](#groupingnode-class)  \n  \n**References:** [GroupingMovementMode](#groupingmovementmode-enum), [ItemContainer](#itemcontainer-class), [NodifyEditor](#nodifyeditor-class), [ResizeEventHandler](#resizeeventhandler-delegate)  \n  \nDefines a panel with a header that groups [ItemContainer](#itemcontainer-class)s inside it and can be resized.  \n  \n```csharp  \npublic class GroupingNode : HeaderedContentControl  \n```  \n  \n### Constructors  \n  \n#### GroupingNode()  \n  \nInitializes a new instance of the [GroupingNode](#groupingnode-class) class.  \n  \n```csharp  \npublic GroupingNode();  \n```  \n  \n### Fields  \n  \n#### ContentControl  \n  \nGets the [ContentControl](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Controls.ContentControl) control of this [GroupingNode](#groupingnode-class).  \n  \n```csharp  \nprotected FrameworkElement ContentControl;  \n```  \n  \n**Field Value**  \n  \n[FrameworkElement](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.FrameworkElement)  \n  \n#### ElementContent  \n  \n```csharp  \nprotected const string ElementContent = \"PART_Content\";  \n```  \n  \n**Field Value**  \n  \n[String](https://docs.microsoft.com/en-us/dotnet/api/System.String)  \n  \n#### ElementHeader  \n  \n```csharp  \nprotected const string ElementHeader = \"PART_Header\";  \n```  \n  \n**Field Value**  \n  \n[String](https://docs.microsoft.com/en-us/dotnet/api/System.String)  \n  \n#### ElementResizeThumb  \n  \n```csharp  \nprotected const string ElementResizeThumb = \"PART_ResizeThumb\";  \n```  \n  \n**Field Value**  \n  \n[String](https://docs.microsoft.com/en-us/dotnet/api/System.String)  \n  \n#### GroupMovementBoxed  \n  \n```csharp  \nprotected static object GroupMovementBoxed;  \n```  \n  \n**Field Value**  \n  \n[Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object)  \n  \n#### HeaderControl  \n  \nGets the [HeaderedContentControl.Header](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Controls.HeaderedContentControl.header) control of this [GroupingNode](#groupingnode-class).  \n  \n```csharp  \nprotected FrameworkElement HeaderControl;  \n```  \n  \n**Field Value**  \n  \n[FrameworkElement](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.FrameworkElement)  \n  \n#### ResizeThumb  \n  \nGets the [FrameworkElement](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.FrameworkElement) used to resize this [GroupingNode](#groupingnode-class).  \n  \n```csharp  \nprotected FrameworkElement ResizeThumb;  \n```  \n  \n**Field Value**  \n  \n[FrameworkElement](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.FrameworkElement)  \n  \n### Properties  \n  \n#### ActualSize  \n  \nGets or sets the actual size of this [GroupingNode](#groupingnode-class).  \n  \n```csharp  \npublic Size ActualSize { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Size](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Size)  \n  \n#### CanResize  \n  \nGets or sets a value that indicates whether this [GroupingNode](#groupingnode-class) can be resized.  \n  \n```csharp  \npublic bool CanResize { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean)  \n  \n#### Container  \n  \nGets the [NodifyEditor](#nodifyeditor-class) that owns this [GroupingNode.Container](#groupingnode-class#container).  \n  \n```csharp  \nprotected ItemContainer Container { get; set; }  \n```  \n  \n**Property Value**  \n  \n[ItemContainer](#itemcontainer-class)  \n  \n#### Editor  \n  \nGets the [NodifyEditor](#nodifyeditor-class) that owns this [GroupingNode](#groupingnode-class).  \n  \n```csharp  \nprotected NodifyEditor Editor { get; set; }  \n```  \n  \n**Property Value**  \n  \n[NodifyEditor](#nodifyeditor-class)  \n  \n#### HeaderBrush  \n  \nGets or sets the brush used for the background of the [HeaderedContentControl.Header](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Controls.HeaderedContentControl.header) of this [GroupingNode](#groupingnode-class).  \n  \n```csharp  \npublic Brush HeaderBrush { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Brush](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Media.Brush)  \n  \n#### MovementMode  \n  \nGets or sets the default movement mode which can be temporarily changed by holding the SwitchMovementModeModifierKey while dragging by the header.  \n  \n```csharp  \npublic GroupingMovementMode MovementMode { get; set; }  \n```  \n  \n**Property Value**  \n  \n[GroupingMovementMode](#groupingmovementmode-enum)  \n  \n#### ResizeCompletedCommand  \n  \nInvoked when the [GroupingNode.ResizeCompleted](#groupingnode-class#resizecompleted) event is not handled.\n            Parameter is the [ItemContainer.ActualSize](#itemcontainer-class#actualsize) of the container.  \n  \n```csharp  \npublic ICommand ResizeCompletedCommand { get; set; }  \n```  \n  \n**Property Value**  \n  \n[ICommand](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.ICommand)  \n  \n#### ResizeStartedCommand  \n  \nInvoked when the [GroupingNode.ResizeStarted](#groupingnode-class#resizestarted) event is not handled.\n            Parameter is the [ItemContainer.ActualSize](#itemcontainer-class#actualsize) of the container.  \n  \n```csharp  \npublic ICommand ResizeStartedCommand { get; set; }  \n```  \n  \n**Property Value**  \n  \n[ICommand](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.ICommand)  \n  \n### Methods  \n  \n#### OnApplyTemplate()  \n  \n```csharp  \npublic override void OnApplyTemplate();  \n```  \n  \n### Events  \n  \n#### ResizeCompleted  \n  \nOccurs when the node finished resizing.  \n  \n```csharp  \npublic event ResizeEventHandler ResizeCompleted;  \n```  \n  \n**Event Type**  \n  \n[ResizeEventHandler](#resizeeventhandler-delegate)  \n  \n#### ResizeStarted  \n  \nOccurs when the node started resizing.  \n  \n```csharp  \npublic event ResizeEventHandler ResizeStarted;  \n```  \n  \n**Event Type**  \n  \n[ResizeEventHandler](#resizeeventhandler-delegate)  \n  \n## GroupingNodeGestures Class  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**Inheritance:** [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object) → [GroupingNodeGestures](#groupingnodegestures-class)  \n  \n**References:** [EditorGestures](#editorgestures-class)  \n  \n```csharp  \npublic class GroupingNodeGestures  \n```  \n  \n### Constructors  \n  \n#### GroupingNodeGestures()  \n  \n```csharp  \npublic GroupingNodeGestures();  \n```  \n  \n### Properties  \n  \n#### SwitchMovementMode  \n  \n```csharp  \npublic ModifierKeys SwitchMovementMode { get; set; }  \n```  \n  \n**Property Value**  \n  \n[ModifierKeys](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.ModifierKeys)  \n  \n### Methods  \n  \n#### Apply(GroupingNodeGestures)  \n  \n```csharp  \npublic void Apply(GroupingNodeGestures gestures);  \n```  \n  \n**Parameters**  \n  \n`gestures` [GroupingNodeGestures](#groupingnodegestures-class)  \n  \n## INodifyCanvasItem Interface  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**Inheritance:** [INodifyCanvasItem](#inodifycanvasitem-interface)  \n  \n**Derived:** [DecoratorContainer](#decoratorcontainer-class), [ItemContainer](#itemcontainer-class)  \n  \n**References:** [NodifyCanvas](#nodifycanvas-class)  \n  \nInterface for items inside a [NodifyCanvas](#nodifycanvas-class).  \n  \n```csharp  \npublic abstract interface INodifyCanvasItem  \n```  \n  \n### Properties  \n  \n#### DesiredSize  \n  \nThe desired size of the item.  \n  \n```csharp  \npublic virtual Size DesiredSize { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Size](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Size)  \n  \n#### Location  \n  \nThe location of the item.  \n  \n```csharp  \npublic virtual Point Location { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n### Methods  \n  \n#### Arrange(Rect)  \n  \n```csharp  \npublic virtual void Arrange(Rect rect);  \n```  \n  \n**Parameters**  \n  \n`rect` [Rect](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Rect)  \n  \n## InputGestureRef Class  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**Inheritance:** [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object) → [InputGesture](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.InputGesture) → [InputGestureRef](#inputgestureref-class)  \n  \n**References:** [ConnectionGestures](#connectiongestures-class), [ConnectorGestures](#connectorgestures-class), [EditorCommands](#editorcommands-class), [ItemContainerGestures](#itemcontainergestures-class), [MinimapGestures](#minimapgestures-class), [NodifyEditorGestures](#nodifyeditorgestures-class), [SelectionGestures](#selectiongestures-class)  \n  \nAn input gesture that allows changing its logic at runtime without changing its reference.\n            Useful for classes that capture the object reference without the posibility of updating it. (e.g. [EditorCommands](#editorcommands-class))  \n  \n```csharp  \npublic sealed class InputGestureRef : InputGesture  \n```  \n  \n### Properties  \n  \n#### Value  \n  \nThe referenced gesture.  \n  \n```csharp  \npublic InputGesture Value { get; set; }  \n```  \n  \n**Property Value**  \n  \n[InputGesture](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.InputGesture)  \n  \n### Methods  \n  \n#### Matches(Object, InputEventArgs)  \n  \n```csharp  \npublic override bool Matches(object targetElement, InputEventArgs inputEventArgs);  \n```  \n  \n**Parameters**  \n  \n`targetElement` [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object)  \n  \n`inputEventArgs` [InputEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.InputEventArgs)  \n  \n**Returns**  \n  \n[Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean)  \n  \n## ItemContainer Class  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**Inheritance:** [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object) → [DispatcherObject](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Threading.DispatcherObject) → [DependencyObject](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.DependencyObject) → [Visual](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Media.Visual) → [UIElement](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.UIElement) → [FrameworkElement](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.FrameworkElement) → [Control](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Controls.Control) → [ContentControl](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Controls.ContentControl) → [ItemContainer](#itemcontainer-class)  \n  \n**Implements:** [INodifyCanvasItem](#inodifycanvasitem-interface)  \n  \n**References:** [Connector](#connector-class), [ContainerDefaultState](#containerdefaultstate-class), [ContainerDraggingState](#containerdraggingstate-class), [ContainerState](#containerstate-class), [EditorCommands](#editorcommands-class), [GroupingNode](#groupingnode-class), [NodifyEditor](#nodifyeditor-class), [PendingConnection](#pendingconnection-class), [PreviewLocationChanged](#previewlocationchanged-delegate), [SelectionHelper](#selectionhelper-class)  \n  \nThe container for all the items generated by the [ItemsControl.ItemsSource](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Controls.ItemsControl.itemssource) of the [NodifyEditor](#nodifyeditor-class).  \n  \n```csharp  \npublic class ItemContainer : ContentControl, INodifyCanvasItem  \n```  \n  \n### Constructors  \n  \n#### ItemContainer(NodifyEditor)  \n  \nConstructs an instance of an [ItemContainer](#itemcontainer-class) in the specified [NodifyEditor](#nodifyeditor-class).  \n  \n```csharp  \npublic ItemContainer(NodifyEditor editor);  \n```  \n  \n**Parameters**  \n  \n`editor` [NodifyEditor](#nodifyeditor-class)  \n  \n### Fields  \n  \n#### IsPreviewingLocationPropertyKey  \n  \n```csharp  \npublic static DependencyPropertyKey IsPreviewingLocationPropertyKey;  \n```  \n  \n**Field Value**  \n  \n[DependencyPropertyKey](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.DependencyPropertyKey)  \n  \n#### IsPreviewingSelectionPropertyKey  \n  \n```csharp  \npublic static DependencyPropertyKey IsPreviewingSelectionPropertyKey;  \n```  \n  \n**Field Value**  \n  \n[DependencyPropertyKey](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.DependencyPropertyKey)  \n  \n### Properties  \n  \n#### ActualSize  \n  \nGets the actual size of this [ItemContainer](#itemcontainer-class).  \n  \n```csharp  \npublic Size ActualSize { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Size](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Size)  \n  \n#### AllowDraggingCancellation  \n  \nGets or sets whether cancelling a dragging operation is allowed.  \n  \n```csharp  \npublic static bool AllowDraggingCancellation { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean)  \n  \n#### DesiredSizeForSelection  \n  \nOverrides the size to check against when calculating if this [ItemContainer](#itemcontainer-class) can be part of the current [NodifyEditor.SelectedArea](#nodifyeditor-class#selectedarea).\n            Defaults to [UIElement.RenderSize](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.UIElement.rendersize).  \n  \n```csharp  \npublic Size? DesiredSizeForSelection { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Size?](https://docs.microsoft.com/en-us/dotnet/api/System.Nullable)  \n  \n#### Editor  \n  \nThe [NodifyEditor](#nodifyeditor-class) that owns this [ItemContainer](#itemcontainer-class).  \n  \n```csharp  \npublic NodifyEditor Editor { get; set; }  \n```  \n  \n**Property Value**  \n  \n[NodifyEditor](#nodifyeditor-class)  \n  \n#### HighlightBrush  \n  \nGets or sets the brush used when the [PendingConnection.IsOverElementProperty](#pendingconnection-class#isoverelementproperty) attached property is true for this [ItemContainer](#itemcontainer-class).  \n  \n```csharp  \npublic Brush HighlightBrush { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Brush](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Media.Brush)  \n  \n#### IsDraggable  \n  \nGets or sets whether this [ItemContainer](#itemcontainer-class) can be dragged.  \n  \n```csharp  \npublic bool IsDraggable { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean)  \n  \n#### IsPreviewingLocation  \n  \nGets a value indicating whether this [ItemContainer](#itemcontainer-class) is previewing a new location but didn't logically move there.  \n  \n```csharp  \npublic bool IsPreviewingLocation { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean)  \n  \n#### IsPreviewingSelection  \n  \nGets a value indicating whether this [ItemContainer](#itemcontainer-class) is about to change its [ItemContainer.IsSelected](#itemcontainer-class#isselected) state.  \n  \n```csharp  \npublic Boolean? IsPreviewingSelection { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Boolean?](https://docs.microsoft.com/en-us/dotnet/api/System.Nullable)  \n  \n#### IsSelectable  \n  \nGets or sets whether this [ItemContainer](#itemcontainer-class) can be selected.  \n  \n```csharp  \npublic bool IsSelectable { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean)  \n  \n#### IsSelected  \n  \nGets or sets a value that indicates whether this [ItemContainer](#itemcontainer-class) is selected.\n            Can only be set if [ItemContainer.IsSelectable](#itemcontainer-class#isselectable) is true.  \n  \n```csharp  \npublic bool IsSelected { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean)  \n  \n#### Location  \n  \nGets or sets the location of this [ItemContainer](#itemcontainer-class) inside the [NodifyEditor](#nodifyeditor-class) in graph space coordinates.  \n  \n```csharp  \npublic virtual Point Location { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n#### SelectedBorderThickness  \n  \nGets or sets the border thickness used when [ItemContainer.IsSelected](#itemcontainer-class#isselected) or [ItemContainer.IsPreviewingSelection](#itemcontainer-class#ispreviewingselection) is true.  \n  \n```csharp  \npublic Thickness SelectedBorderThickness { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Thickness](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Thickness)  \n  \n#### SelectedBrush  \n  \nGets or sets the brush used when [ItemContainer.IsSelected](#itemcontainer-class#isselected) or [ItemContainer.IsPreviewingSelection](#itemcontainer-class#ispreviewingselection) is true.  \n  \n```csharp  \npublic Brush SelectedBrush { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Brush](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Media.Brush)  \n  \n#### SelectedMargin  \n  \nThe calculated margin when the container is selected or previewing selection.  \n  \n```csharp  \npublic Thickness SelectedMargin { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Thickness](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Thickness)  \n  \n#### State  \n  \nThe current state of the container.  \n  \n```csharp  \npublic ContainerState State { get; set; }  \n```  \n  \n**Property Value**  \n  \n[ContainerState](#containerstate-class)  \n  \n### Methods  \n  \n#### GetInitialState()  \n  \nCreates the initial state of the container.  \n  \n```csharp  \nprotected virtual ContainerState GetInitialState();  \n```  \n  \n**Returns**  \n  \n[ContainerState](#containerstate-class): The initial state.  \n  \n#### IsSelectableInArea(Rect, Boolean)  \n  \nChecks if area contains or intersects with this [ItemContainer](#itemcontainer-class) taking into consideration the [ItemContainer.DesiredSizeForSelection](#itemcontainer-class#desiredsizeforselection).  \n  \n```csharp  \npublic virtual bool IsSelectableInArea(Rect area, bool isContained);  \n```  \n  \n**Parameters**  \n  \n`area` [Rect](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Rect): The area to check if contains or intersects this [ItemContainer](#itemcontainer-class).  \n  \n`isContained` [Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean): If true will check if area contains this, otherwise will check if area intersects with this.  \n  \n**Returns**  \n  \n[Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean): True if area contains or intersects this [ItemContainer](#itemcontainer-class).  \n  \n#### IsSelectableLocation(Point)  \n  \nChecks if position is selectable.  \n  \n```csharp  \nprotected virtual bool IsSelectableLocation(Point position);  \n```  \n  \n**Parameters**  \n  \n`position` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point): A position relative to this [ItemContainer](#itemcontainer-class).  \n  \n**Returns**  \n  \n[Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean): True if position is selectable.  \n  \n#### OnApplyTemplate()  \n  \n```csharp  \npublic override void OnApplyTemplate();  \n```  \n  \n#### OnKeyDown(KeyEventArgs)  \n  \n```csharp  \nprotected override void OnKeyDown(KeyEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [KeyEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.KeyEventArgs)  \n  \n#### OnKeyUp(KeyEventArgs)  \n  \n```csharp  \nprotected override void OnKeyUp(KeyEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [KeyEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.KeyEventArgs)  \n  \n#### OnLocationChanged()  \n  \nRaises the [ItemContainer.LocationChangedEvent](#itemcontainer-class#locationchangedevent) and sets [ItemContainer.IsPreviewingLocation](#itemcontainer-class#ispreviewinglocation) to false.  \n  \n```csharp  \nprotected void OnLocationChanged();  \n```  \n  \n#### OnLostMouseCapture(MouseEventArgs)  \n  \n```csharp  \nprotected override void OnLostMouseCapture(MouseEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [MouseEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.MouseEventArgs)  \n  \n#### OnMouseDown(MouseButtonEventArgs)  \n  \n```csharp  \nprotected override void OnMouseDown(MouseButtonEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [MouseButtonEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.MouseButtonEventArgs)  \n  \n#### OnMouseMove(MouseEventArgs)  \n  \n```csharp  \nprotected override void OnMouseMove(MouseEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [MouseEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.MouseEventArgs)  \n  \n#### OnMouseUp(MouseButtonEventArgs)  \n  \n```csharp  \nprotected override void OnMouseUp(MouseButtonEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [MouseButtonEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.MouseButtonEventArgs)  \n  \n#### OnMouseWheel(MouseWheelEventArgs)  \n  \n```csharp  \nprotected override void OnMouseWheel(MouseWheelEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [MouseWheelEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.MouseWheelEventArgs)  \n  \n#### OnRenderSizeChanged(SizeChangedInfo)  \n  \n```csharp  \nprotected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo);  \n```  \n  \n**Parameters**  \n  \n`sizeInfo` [SizeChangedInfo](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.SizeChangedInfo)  \n  \n#### OnSelectedChanged(Boolean)  \n  \nRaises the [ItemContainer.SelectedEvent](#itemcontainer-class#selectedevent) or [ItemContainer.UnselectedEvent](#itemcontainer-class#unselectedevent) based on newValue.\n            Called when the [ItemContainer.IsSelected](#itemcontainer-class#isselected) value is changed.  \n  \n```csharp  \nprotected void OnSelectedChanged(bool newValue);  \n```  \n  \n**Parameters**  \n  \n`newValue` [Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean): True if selected, false otherwise.  \n  \n#### PopAllStates()  \n  \nPops all states from the container.  \n  \n```csharp  \npublic void PopAllStates();  \n```  \n  \n#### PopState()  \n  \nPops the current [ItemContainer.State](#itemcontainer-class#state) from the stack.  \n  \n```csharp  \npublic void PopState();  \n```  \n  \n#### PushState(ContainerState)  \n  \nPushes the given state to the stack.  \n  \n```csharp  \npublic void PushState(ContainerState state);  \n```  \n  \n**Parameters**  \n  \n`state` [ContainerState](#containerstate-class): The new state of the container.  \n  \n### Events  \n  \n#### DragCompleted  \n  \nOccurs when this [ItemContainer](#itemcontainer-class) completed the drag operation.  \n  \n```csharp  \npublic event DragCompletedEventHandler DragCompleted;  \n```  \n  \n**Event Type**  \n  \n[DragCompletedEventHandler](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Controls.Primitives.DragCompletedEventHandler)  \n  \n#### DragDelta  \n  \nOccurs when this [ItemContainer](#itemcontainer-class) is being dragged.  \n  \n```csharp  \npublic event DragDeltaEventHandler DragDelta;  \n```  \n  \n**Event Type**  \n  \n[DragDeltaEventHandler](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Controls.Primitives.DragDeltaEventHandler)  \n  \n#### DragStarted  \n  \nOccurs when this [ItemContainer](#itemcontainer-class) is the instigator of a drag operation.  \n  \n```csharp  \npublic event DragStartedEventHandler DragStarted;  \n```  \n  \n**Event Type**  \n  \n[DragStartedEventHandler](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Controls.Primitives.DragStartedEventHandler)  \n  \n#### LocationChanged  \n  \nOccurs when the [ItemContainer.Location](#itemcontainer-class#location) of this [ItemContainer](#itemcontainer-class) is changed.  \n  \n```csharp  \npublic event RoutedEventHandler LocationChanged;  \n```  \n  \n**Event Type**  \n  \n[RoutedEventHandler](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.RoutedEventHandler)  \n  \n#### PreviewLocationChanged  \n  \nOccurs when the [ItemContainer](#itemcontainer-class) is previewing a new location.  \n  \n```csharp  \npublic event PreviewLocationChanged PreviewLocationChanged;  \n```  \n  \n**Event Type**  \n  \n[PreviewLocationChanged](#previewlocationchanged-delegate)  \n  \n#### Selected  \n  \nOccurs when this [ItemContainer](#itemcontainer-class) is selected.  \n  \n```csharp  \npublic event RoutedEventHandler Selected;  \n```  \n  \n**Event Type**  \n  \n[RoutedEventHandler](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.RoutedEventHandler)  \n  \n#### Unselected  \n  \nOccurs when this [ItemContainer](#itemcontainer-class) is unselected.  \n  \n```csharp  \npublic event RoutedEventHandler Unselected;  \n```  \n  \n**Event Type**  \n  \n[RoutedEventHandler](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.RoutedEventHandler)  \n  \n## ItemContainerGestures Class  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**Inheritance:** [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object) → [ItemContainerGestures](#itemcontainergestures-class)  \n  \n**References:** [EditorGestures](#editorgestures-class), [InputGestureRef](#inputgestureref-class), [SelectionGestures](#selectiongestures-class)  \n  \n```csharp  \npublic class ItemContainerGestures  \n```  \n  \n### Constructors  \n  \n#### ItemContainerGestures()  \n  \n```csharp  \npublic ItemContainerGestures();  \n```  \n  \n### Properties  \n  \n#### CancelAction  \n  \n```csharp  \npublic InputGestureRef CancelAction { get; set; }  \n```  \n  \n**Property Value**  \n  \n[InputGestureRef](#inputgestureref-class)  \n  \n#### Drag  \n  \n```csharp  \npublic InputGestureRef Drag { get; set; }  \n```  \n  \n**Property Value**  \n  \n[InputGestureRef](#inputgestureref-class)  \n  \n#### Selection  \n  \n```csharp  \npublic SelectionGestures Selection { get; set; }  \n```  \n  \n**Property Value**  \n  \n[SelectionGestures](#selectiongestures-class)  \n  \n### Methods  \n  \n#### Apply(ItemContainerGestures)  \n  \n```csharp  \npublic void Apply(ItemContainerGestures gestures);  \n```  \n  \n**Parameters**  \n  \n`gestures` [ItemContainerGestures](#itemcontainergestures-class)  \n  \n## KnotNode Class  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**Inheritance:** [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object) → [DispatcherObject](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Threading.DispatcherObject) → [DependencyObject](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.DependencyObject) → [Visual](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Media.Visual) → [UIElement](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.UIElement) → [FrameworkElement](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.FrameworkElement) → [Control](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Controls.Control) → [ContentControl](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Controls.ContentControl) → [KnotNode](#knotnode-class)  \n  \n**References:** [Connector](#connector-class)  \n  \nRepresents a control that owns a [Connector](#connector-class).  \n  \n```csharp  \npublic class KnotNode : ContentControl  \n```  \n  \n### Constructors  \n  \n#### KnotNode()  \n  \n```csharp  \npublic KnotNode();  \n```  \n  \n## LineConnection Class  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**Inheritance:** [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object) → [DispatcherObject](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Threading.DispatcherObject) → [DependencyObject](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.DependencyObject) → [Visual](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Media.Visual) → [UIElement](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.UIElement) → [FrameworkElement](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.FrameworkElement) → [Shape](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Shapes.Shape) → [BaseConnection](#baseconnection-class) → [LineConnection](#lineconnection-class)  \n  \n**Derived:** [CircuitConnection](#circuitconnection-class), [StepConnection](#stepconnection-class)  \n  \n**References:** [ConnectionDirection](#connectiondirection-enum)  \n  \nRepresents a line that has an arrow indicating its [BaseConnection.Direction](#baseconnection-class#direction).  \n  \n```csharp  \npublic class LineConnection : BaseConnection  \n```  \n  \n### Constructors  \n  \n#### LineConnection()  \n  \n```csharp  \npublic LineConnection();  \n```  \n  \n### Properties  \n  \n#### CornerRadius  \n  \nThe radius of the corners between the line segments.  \n  \n```csharp  \npublic double CornerRadius { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Double](https://docs.microsoft.com/en-us/dotnet/api/System.Double)  \n  \n### Methods  \n  \n#### AddSmoothCorner(StreamGeometryContext, Point, Point, Point, Double)  \n  \n```csharp  \nprotected static void AddSmoothCorner(StreamGeometryContext context, Point start, Point corner, Point end, double radius);  \n```  \n  \n**Parameters**  \n  \n`context` [StreamGeometryContext](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Media.StreamGeometryContext)  \n  \n`start` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n`corner` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n`end` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n`radius` [Double](https://docs.microsoft.com/en-us/dotnet/api/System.Double)  \n  \n#### DrawDefaultArrowhead(StreamGeometryContext, Point, Point, ConnectionDirection, Orientation)  \n  \n```csharp  \nprotected override void DrawDefaultArrowhead(StreamGeometryContext context, Point source, Point target, ConnectionDirection arrowDirection = 0, Orientation orientation = 0);  \n```  \n  \n**Parameters**  \n  \n`context` [StreamGeometryContext](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Media.StreamGeometryContext)  \n  \n`source` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n`target` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n`arrowDirection` [ConnectionDirection](#connectiondirection-enum)  \n  \n`orientation` [Orientation](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Controls.Orientation)  \n  \n#### DrawDirectionalArrowsGeometry(StreamGeometryContext, Point, Point)  \n  \n```csharp  \nprotected override void DrawDirectionalArrowsGeometry(StreamGeometryContext context, Point source, Point target);  \n```  \n  \n**Parameters**  \n  \n`context` [StreamGeometryContext](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Media.StreamGeometryContext)  \n  \n`source` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n`target` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n#### DrawLineGeometry(StreamGeometryContext, Point, Point)  \n  \n```csharp  \nprotected override ValueTuple<ValueTuple<Point, Point>, ValueTuple<Point, Point>> DrawLineGeometry(StreamGeometryContext context, Point source, Point target);  \n```  \n  \n**Parameters**  \n  \n`context` [StreamGeometryContext](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Media.StreamGeometryContext)  \n  \n`source` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n`target` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n**Returns**  \n  \n[ValueTuple<ValueTuple<Point, Point>, ValueTuple<Point, Point>>](https://docs.microsoft.com/en-us/dotnet/api/System.ValueTuple)  \n  \n#### InterpolateLine(Point, Point, Point, Point, Double)  \n  \n```csharp  \nprotected static ValueTuple<ValueTuple<Point, Point>, Point> InterpolateLine(Point p0, Point p1, Point p2, Point p3, double t);  \n```  \n  \n**Parameters**  \n  \n`p0` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n`p1` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n`p2` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n`p3` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n`t` [Double](https://docs.microsoft.com/en-us/dotnet/api/System.Double)  \n  \n**Returns**  \n  \n[ValueTuple<ValueTuple<Point, Point>, Point>](https://docs.microsoft.com/en-us/dotnet/api/System.ValueTuple)  \n  \n#### InterpolateLine(Point, Point, Point, Double)  \n  \n```csharp  \nprotected static ValueTuple<ValueTuple<Point, Point>, Point> InterpolateLine(Point p0, Point p1, Point p2, double t);  \n```  \n  \n**Parameters**  \n  \n`p0` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n`p1` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n`p2` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n`t` [Double](https://docs.microsoft.com/en-us/dotnet/api/System.Double)  \n  \n**Returns**  \n  \n[ValueTuple<ValueTuple<Point, Point>, Point>](https://docs.microsoft.com/en-us/dotnet/api/System.ValueTuple)  \n  \n#### InterpolateLineSegment(Point, Point, Double)  \n  \n```csharp  \nprotected static Point InterpolateLineSegment(Point p0, Point p1, double t);  \n```  \n  \n**Parameters**  \n  \n`p0` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n`p1` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n`t` [Double](https://docs.microsoft.com/en-us/dotnet/api/System.Double)  \n  \n**Returns**  \n  \n[Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n## Match Enum  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**References:** [MultiGesture](#multigesture-class)  \n  \n```csharp  \npublic enum Match  \n```  \n  \n### Fields  \n  \n#### All  \n  \n```csharp  \nAll = 1;  \n```  \n  \n#### Any  \n  \n```csharp  \nAny = 0;  \n```  \n  \n## Minimap Class  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**Inheritance:** [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object) → [DispatcherObject](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Threading.DispatcherObject) → [DependencyObject](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.DependencyObject) → [Visual](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Media.Visual) → [UIElement](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.UIElement) → [FrameworkElement](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.FrameworkElement) → [Control](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Controls.Control) → [ItemsControl](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Controls.ItemsControl) → [Minimap](#minimap-class)  \n  \n**References:** [MinimapItem](#minimapitem-class), [NodifyEditor](#nodifyeditor-class), [ZoomEventArgs](#zoomeventargs-class), [ZoomEventHandler](#zoomeventhandler-delegate)  \n  \nA minimap control that can position the viewport, and zoom in and out.  \n  \n```csharp  \npublic class Minimap : ItemsControl  \n```  \n  \n### Constructors  \n  \n#### Minimap()  \n  \n```csharp  \npublic Minimap();  \n```  \n  \n### Fields  \n  \n#### ElementItemsHost  \n  \n```csharp  \nprotected const string ElementItemsHost = \"PART_ItemsHost\";  \n```  \n  \n**Field Value**  \n  \n[String](https://docs.microsoft.com/en-us/dotnet/api/System.String)  \n  \n### Properties  \n  \n#### Extent  \n  \nThe area covered by the items and the viewport rectangle in graph space.  \n  \n```csharp  \npublic Rect Extent { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Rect](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Rect)  \n  \n#### IsDragging  \n  \n```csharp  \nprotected bool IsDragging { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean)  \n  \n#### IsReadOnly  \n  \nWhether the minimap can move and zoom the viewport.  \n  \n```csharp  \npublic bool IsReadOnly { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean)  \n  \n#### ItemsExtent  \n  \nThe area covered by the [MinimapItem](#minimapitem-class)s in graph space.  \n  \n```csharp  \npublic Rect ItemsExtent { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Rect](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Rect)  \n  \n#### MaxViewportOffset  \n  \nThe max position from the [NodifyEditor.ItemsExtent](#nodifyeditor-class#itemsextent) that the viewport can move to.  \n  \n```csharp  \npublic Size MaxViewportOffset { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Size](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Size)  \n  \n#### ResizeToViewport  \n  \nWhether the minimap should resize to also display the whole viewport.  \n  \n```csharp  \npublic bool ResizeToViewport { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean)  \n  \n#### ViewportLocation  \n  \n```csharp  \npublic Point ViewportLocation { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n#### ViewportSize  \n  \n```csharp  \npublic Size ViewportSize { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Size](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Size)  \n  \n#### ViewportStyle  \n  \nGets or sets the style to use for the viewport rectangle.  \n  \n```csharp  \npublic Style ViewportStyle { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Style](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Style)  \n  \n### Methods  \n  \n#### GetContainerForItemOverride()  \n  \n```csharp  \nprotected override DependencyObject GetContainerForItemOverride();  \n```  \n  \n**Returns**  \n  \n[DependencyObject](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.DependencyObject)  \n  \n#### IsItemItsOwnContainerOverride(Object)  \n  \n```csharp  \nprotected override bool IsItemItsOwnContainerOverride(object item);  \n```  \n  \n**Parameters**  \n  \n`item` [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object)  \n  \n**Returns**  \n  \n[Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean)  \n  \n#### OnApplyTemplate()  \n  \n```csharp  \npublic override void OnApplyTemplate();  \n```  \n  \n#### OnMouseDown(MouseButtonEventArgs)  \n  \n```csharp  \nprotected override void OnMouseDown(MouseButtonEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [MouseButtonEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.MouseButtonEventArgs)  \n  \n#### OnMouseMove(MouseEventArgs)  \n  \n```csharp  \nprotected override void OnMouseMove(MouseEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [MouseEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.MouseEventArgs)  \n  \n#### OnMouseUp(MouseButtonEventArgs)  \n  \n```csharp  \nprotected override void OnMouseUp(MouseButtonEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [MouseButtonEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.MouseButtonEventArgs)  \n  \n#### OnMouseWheel(MouseWheelEventArgs)  \n  \n```csharp  \nprotected override void OnMouseWheel(MouseWheelEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [MouseWheelEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.MouseWheelEventArgs)  \n  \n### Events  \n  \n#### Zoom  \n  \nTriggered when zooming in or out using the mouse wheel.  \n  \n```csharp  \npublic event ZoomEventHandler Zoom;  \n```  \n  \n**Event Type**  \n  \n[ZoomEventHandler](#zoomeventhandler-delegate)  \n  \n## MinimapGestures Class  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**Inheritance:** [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object) → [MinimapGestures](#minimapgestures-class)  \n  \n**References:** [EditorGestures](#editorgestures-class), [InputGestureRef](#inputgestureref-class)  \n  \n```csharp  \npublic class MinimapGestures  \n```  \n  \n### Constructors  \n  \n#### MinimapGestures()  \n  \n```csharp  \npublic MinimapGestures();  \n```  \n  \n### Properties  \n  \n#### DragViewport  \n  \n```csharp  \npublic InputGestureRef DragViewport { get; set; }  \n```  \n  \n**Property Value**  \n  \n[InputGestureRef](#inputgestureref-class)  \n  \n#### ZoomModifierKey  \n  \n```csharp  \npublic ModifierKeys ZoomModifierKey { get; set; }  \n```  \n  \n**Property Value**  \n  \n[ModifierKeys](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.ModifierKeys)  \n  \n### Methods  \n  \n#### Apply(MinimapGestures)  \n  \n```csharp  \npublic void Apply(MinimapGestures gestures);  \n```  \n  \n**Parameters**  \n  \n`gestures` [MinimapGestures](#minimapgestures-class)  \n  \n## MinimapItem Class  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**Inheritance:** [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object) → [DispatcherObject](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Threading.DispatcherObject) → [DependencyObject](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.DependencyObject) → [Visual](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Media.Visual) → [UIElement](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.UIElement) → [FrameworkElement](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.FrameworkElement) → [Control](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Controls.Control) → [ContentControl](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Controls.ContentControl) → [MinimapItem](#minimapitem-class)  \n  \n**References:** [Minimap](#minimap-class)  \n  \n```csharp  \npublic class MinimapItem : ContentControl  \n```  \n  \n### Constructors  \n  \n#### MinimapItem()  \n  \n```csharp  \npublic MinimapItem();  \n```  \n  \n### Properties  \n  \n#### Location  \n  \nGets or sets the location of this [MinimapItem](#minimapitem-class) inside the [Minimap](#minimap-class).  \n  \n```csharp  \npublic Point Location { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n## MultiGesture Class  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**Inheritance:** [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object) → [InputGesture](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.InputGesture) → [MultiGesture](#multigesture-class)  \n  \n**Derived:** [AnyGesture](#anygesture-class), [AllGestures](#allgestures-class)  \n  \n**References:** [Match](#match-enum)  \n  \nCombines multiple input gestures.  \n  \n```csharp  \npublic class MultiGesture : InputGesture  \n```  \n  \n### Constructors  \n  \n#### MultiGesture(Match, InputGesture[])  \n  \nConstructs an instance of a [MultiGesture](#multigesture-class).  \n  \n```csharp  \npublic MultiGesture(Match match, InputGesture[] gestures);  \n```  \n  \n**Parameters**  \n  \n`match` [Match](#match-enum): The matching strategy.  \n  \n`gestures` [InputGesture[]](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.InputGesture[]): The input gestures.  \n  \n### Fields  \n  \n#### None  \n  \n```csharp  \npublic static MultiGesture None;  \n```  \n  \n**Field Value**  \n  \n[MultiGesture](#multigesture-class)  \n  \n### Methods  \n  \n#### Matches(Object, InputEventArgs)  \n  \n```csharp  \npublic override bool Matches(object targetElement, InputEventArgs inputEventArgs);  \n```  \n  \n**Parameters**  \n  \n`targetElement` [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object)  \n  \n`inputEventArgs` [InputEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.InputEventArgs)  \n  \n**Returns**  \n  \n[Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean)  \n  \n## Node Class  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**Inheritance:** [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object) → [DispatcherObject](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Threading.DispatcherObject) → [DependencyObject](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.DependencyObject) → [Visual](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Media.Visual) → [UIElement](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.UIElement) → [FrameworkElement](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.FrameworkElement) → [Control](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Controls.Control) → [ContentControl](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Controls.ContentControl) → [HeaderedContentControl](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Controls.HeaderedContentControl) → [Node](#node-class)  \n  \n**References:** [Connector](#connector-class), [NodeInput](#nodeinput-class), [NodeOutput](#nodeoutput-class)  \n  \nRepresents a control that has a list of [Node.Input](#node-class#input)[Connector](#connector-class)s and a list of [Node.Output](#node-class#output)[Connector](#connector-class)s.  \n  \n```csharp  \npublic class Node : HeaderedContentControl  \n```  \n  \n### Constructors  \n  \n#### Node()  \n  \n```csharp  \npublic Node();  \n```  \n  \n### Fields  \n  \n#### ElementInputItemsControl  \n  \n```csharp  \nprotected const string ElementInputItemsControl = \"PART_Input\";  \n```  \n  \n**Field Value**  \n  \n[String](https://docs.microsoft.com/en-us/dotnet/api/System.String)  \n  \n#### ElementOutputItemsControl  \n  \n```csharp  \nprotected const string ElementOutputItemsControl = \"PART_Output\";  \n```  \n  \n**Field Value**  \n  \n[String](https://docs.microsoft.com/en-us/dotnet/api/System.String)  \n  \n### Properties  \n  \n#### ContentBrush  \n  \nGets or sets the brush used for the background of the [ContentControl.Content](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Controls.ContentControl.content) of this [Node](#node-class).  \n  \n```csharp  \npublic Brush ContentBrush { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Brush](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Media.Brush)  \n  \n#### ContentContainerStyle  \n  \nGets or sets the style for the content container.  \n  \n```csharp  \npublic Style ContentContainerStyle { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Style](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Style)  \n  \n#### Footer  \n  \nGets or sets the data for the footer of this control.  \n  \n```csharp  \npublic object Footer { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object)  \n  \n#### FooterBrush  \n  \nGets or sets the brush used for the background of the [Node.Footer](#node-class#footer) of this [Node](#node-class).  \n  \n```csharp  \npublic Brush FooterBrush { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Brush](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Media.Brush)  \n  \n#### FooterContainerStyle  \n  \nGets or sets the style for the footer container.  \n  \n```csharp  \npublic Style FooterContainerStyle { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Style](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Style)  \n  \n#### FooterTemplate  \n  \nGets or sets the template used to display the content of the control's footer.  \n  \n```csharp  \npublic DataTemplate FooterTemplate { get; set; }  \n```  \n  \n**Property Value**  \n  \n[DataTemplate](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.DataTemplate)  \n  \n#### HasFooter  \n  \nGets a value that indicates whether the [Node.Footer](#node-class#footer) is .  \n  \n```csharp  \npublic bool HasFooter { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean)  \n  \n#### HeaderBrush  \n  \nGets or sets the brush used for the background of the [HeaderedContentControl.Header](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Controls.HeaderedContentControl.header) of this [Node](#node-class).  \n  \n```csharp  \npublic Brush HeaderBrush { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Brush](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Media.Brush)  \n  \n#### HeaderContainerStyle  \n  \nGets or sets the style for the header container.  \n  \n```csharp  \npublic Style HeaderContainerStyle { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Style](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Style)  \n  \n#### Input  \n  \nGets or sets the data for the input [Connector](#connector-class)s of this control.  \n  \n```csharp  \npublic IEnumerable Input { get; set; }  \n```  \n  \n**Property Value**  \n  \n[IEnumerable](https://docs.microsoft.com/en-us/dotnet/api/System.Collections.IEnumerable)  \n  \n#### InputConnectorTemplate  \n  \nGets or sets the template used to display the content of the control's [Node.Input](#node-class#input) connectors.  \n  \n```csharp  \npublic DataTemplate InputConnectorTemplate { get; set; }  \n```  \n  \n**Property Value**  \n  \n[DataTemplate](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.DataTemplate)  \n  \n#### InputGroupStyle  \n  \n```csharp  \npublic ObservableCollection<GroupStyle> InputGroupStyle { get; set; }  \n```  \n  \n**Property Value**  \n  \n[ObservableCollection<GroupStyle>](https://docs.microsoft.com/en-us/dotnet/api/System.Collections.ObjectModel.ObservableCollection)  \n  \n#### InputItemsControl  \n  \n```csharp  \nprotected ItemsControl InputItemsControl { get; set; }  \n```  \n  \n**Property Value**  \n  \n[ItemsControl](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Controls.ItemsControl)  \n  \n#### Output  \n  \nGets or sets the data for the output [Connector](#connector-class)s of this control.  \n  \n```csharp  \npublic IEnumerable Output { get; set; }  \n```  \n  \n**Property Value**  \n  \n[IEnumerable](https://docs.microsoft.com/en-us/dotnet/api/System.Collections.IEnumerable)  \n  \n#### OutputConnectorTemplate  \n  \nGets or sets the template used to display the content of the control's [Node.Output](#node-class#output) connectors.  \n  \n```csharp  \npublic DataTemplate OutputConnectorTemplate { get; set; }  \n```  \n  \n**Property Value**  \n  \n[DataTemplate](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.DataTemplate)  \n  \n#### OutputGroupStyle  \n  \n```csharp  \npublic ObservableCollection<GroupStyle> OutputGroupStyle { get; set; }  \n```  \n  \n**Property Value**  \n  \n[ObservableCollection<GroupStyle>](https://docs.microsoft.com/en-us/dotnet/api/System.Collections.ObjectModel.ObservableCollection)  \n  \n#### OutputItemsControl  \n  \n```csharp  \nprotected ItemsControl OutputItemsControl { get; set; }  \n```  \n  \n**Property Value**  \n  \n[ItemsControl](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Controls.ItemsControl)  \n  \n### Methods  \n  \n#### OnApplyTemplate()  \n  \n```csharp  \npublic override void OnApplyTemplate();  \n```  \n  \n## NodeInput Class  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**Inheritance:** [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object) → [DispatcherObject](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Threading.DispatcherObject) → [DependencyObject](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.DependencyObject) → [Visual](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Media.Visual) → [UIElement](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.UIElement) → [FrameworkElement](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.FrameworkElement) → [Control](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Controls.Control) → [Connector](#connector-class) → [NodeInput](#nodeinput-class)  \n  \n**References:** [Node](#node-class)  \n  \nRepresents the default control for the [Node.InputConnectorTemplate](#node-class#inputconnectortemplate).  \n  \n```csharp  \npublic class NodeInput : Connector  \n```  \n  \n### Constructors  \n  \n#### NodeInput()  \n  \n```csharp  \npublic NodeInput();  \n```  \n  \n### Properties  \n  \n#### ConnectorTemplate  \n  \nGets or sets the template used to display the connecting point of this [Connector](#connector-class).  \n  \n```csharp  \npublic ControlTemplate ConnectorTemplate { get; set; }  \n```  \n  \n**Property Value**  \n  \n[ControlTemplate](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Controls.ControlTemplate)  \n  \n#### Header  \n  \nGets of sets the data used for the control's header.  \n  \n```csharp  \npublic object Header { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object)  \n  \n#### HeaderTemplate  \n  \nGets or sets the template used to display the content of the control's header.  \n  \n```csharp  \npublic DataTemplate HeaderTemplate { get; set; }  \n```  \n  \n**Property Value**  \n  \n[DataTemplate](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.DataTemplate)  \n  \n#### Orientation  \n  \n```csharp  \npublic Orientation Orientation { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Orientation](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Controls.Orientation)  \n  \n## NodeOutput Class  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**Inheritance:** [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object) → [DispatcherObject](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Threading.DispatcherObject) → [DependencyObject](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.DependencyObject) → [Visual](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Media.Visual) → [UIElement](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.UIElement) → [FrameworkElement](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.FrameworkElement) → [Control](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Controls.Control) → [Connector](#connector-class) → [NodeOutput](#nodeoutput-class)  \n  \n**References:** [Node](#node-class)  \n  \nRepresents the default control for the [Node.OutputConnectorTemplate](#node-class#outputconnectortemplate).  \n  \n```csharp  \npublic class NodeOutput : Connector  \n```  \n  \n### Constructors  \n  \n#### NodeOutput()  \n  \n```csharp  \npublic NodeOutput();  \n```  \n  \n### Properties  \n  \n#### ConnectorTemplate  \n  \nGets or sets the template used to display the connecting point of this [Connector](#connector-class).  \n  \n```csharp  \npublic ControlTemplate ConnectorTemplate { get; set; }  \n```  \n  \n**Property Value**  \n  \n[ControlTemplate](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Controls.ControlTemplate)  \n  \n#### Header  \n  \nGets of sets the data used for the control's header.  \n  \n```csharp  \npublic object Header { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object)  \n  \n#### HeaderTemplate  \n  \nGets or sets the template used to display the content of the control's header.  \n  \n```csharp  \npublic DataTemplate HeaderTemplate { get; set; }  \n```  \n  \n**Property Value**  \n  \n[DataTemplate](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.DataTemplate)  \n  \n#### Orientation  \n  \n```csharp  \npublic Orientation Orientation { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Orientation](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Controls.Orientation)  \n  \n## NodifyCanvas Class  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**Inheritance:** [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object) → [DispatcherObject](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Threading.DispatcherObject) → [DependencyObject](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.DependencyObject) → [Visual](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Media.Visual) → [UIElement](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.UIElement) → [FrameworkElement](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.FrameworkElement) → [Panel](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Controls.Panel) → [NodifyCanvas](#nodifycanvas-class)  \n  \n**References:** [INodifyCanvasItem](#inodifycanvasitem-interface)  \n  \nA canvas like panel that works with [INodifyCanvasItem](#inodifycanvasitem-interface)s.  \n  \n```csharp  \npublic class NodifyCanvas : Panel  \n```  \n  \n### Constructors  \n  \n#### NodifyCanvas()  \n  \n```csharp  \npublic NodifyCanvas();  \n```  \n  \n### Properties  \n  \n#### Extent  \n  \nThe area covered by the children of this panel.  \n  \n```csharp  \npublic Rect Extent { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Rect](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Rect)  \n  \n### Methods  \n  \n#### ArrangeOverride(Size)  \n  \n```csharp  \nprotected override Size ArrangeOverride(Size arrangeSize);  \n```  \n  \n**Parameters**  \n  \n`arrangeSize` [Size](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Size)  \n  \n**Returns**  \n  \n[Size](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Size)  \n  \n#### MeasureOverride(Size)  \n  \n```csharp  \nprotected override Size MeasureOverride(Size constraint);  \n```  \n  \n**Parameters**  \n  \n`constraint` [Size](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Size)  \n  \n**Returns**  \n  \n[Size](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Size)  \n  \n## NodifyEditor Class  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**Inheritance:** [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object) → [DispatcherObject](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Threading.DispatcherObject) → [DependencyObject](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.DependencyObject) → [Visual](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Media.Visual) → [UIElement](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.UIElement) → [FrameworkElement](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.FrameworkElement) → [Control](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Controls.Control) → [ItemsControl](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Controls.ItemsControl) → [Selector](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Controls.Primitives.Selector) → [MultiSelector](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Controls.Primitives.MultiSelector) → [NodifyEditor](#nodifyeditor-class)  \n  \n**Implements:** [IScrollInfo](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Controls.Primitives.IScrollInfo)  \n  \n**References:** [BaseConnection](#baseconnection-class), [Connection](#connection-class), [Connector](#connector-class), [ContainerState](#containerstate-class), [CuttingLine](#cuttingline-class), [DecoratorContainer](#decoratorcontainer-class), [EditorCommands](#editorcommands-class), [EditorCuttingState](#editorcuttingstate-class), [EditorDefaultState](#editordefaultstate-class), [EditorGestures](#editorgestures-class), [EditorPanningState](#editorpanningstate-class), [EditorPushingItemsState](#editorpushingitemsstate-class), [EditorSelectingState](#editorselectingstate-class), [EditorState](#editorstate-class), [GroupingNode](#groupingnode-class), [ItemContainer](#itemcontainer-class), [Minimap](#minimap-class), [PendingConnection](#pendingconnection-class), [SelectionHelper](#selectionhelper-class)  \n  \nGroups [ItemContainer](#itemcontainer-class)s and [Connection](#connection-class)s in an area that you can drag, zoom and select.  \n  \n```csharp  \npublic class NodifyEditor : MultiSelector, IScrollInfo  \n```  \n  \n### Constructors  \n  \n#### NodifyEditor()  \n  \nInitializes a new instance of the [NodifyEditor](#nodifyeditor-class) class.  \n  \n```csharp  \npublic NodifyEditor();  \n```  \n  \n### Fields  \n  \n#### CuttingConnectionTypes  \n  \nThe list of supported connection types for cutting. Type must be derived from [FrameworkElement](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.FrameworkElement).  \n  \n```csharp  \npublic static HashSet<Type> CuttingConnectionTypes;  \n```  \n  \n**Field Value**  \n  \n[HashSet<Type>](https://docs.microsoft.com/en-us/dotnet/api/System.Collections.Generic.HashSet)  \n  \n#### CuttingLineEndPropertyKey  \n  \n```csharp  \nprotected static DependencyPropertyKey CuttingLineEndPropertyKey;  \n```  \n  \n**Field Value**  \n  \n[DependencyPropertyKey](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.DependencyPropertyKey)  \n  \n#### CuttingLineStartPropertyKey  \n  \n```csharp  \nprotected static DependencyPropertyKey CuttingLineStartPropertyKey;  \n```  \n  \n**Field Value**  \n  \n[DependencyPropertyKey](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.DependencyPropertyKey)  \n  \n#### ElementConnectionsHost  \n  \n```csharp  \nprotected const string ElementConnectionsHost = \"PART_ConnectionsHost\";  \n```  \n  \n**Field Value**  \n  \n[String](https://docs.microsoft.com/en-us/dotnet/api/System.String)  \n  \n#### ElementItemsHost  \n  \n```csharp  \nprotected const string ElementItemsHost = \"PART_ItemsHost\";  \n```  \n  \n**Field Value**  \n  \n[String](https://docs.microsoft.com/en-us/dotnet/api/System.String)  \n  \n#### IsCuttingPropertyKey  \n  \n```csharp  \nprotected static DependencyPropertyKey IsCuttingPropertyKey;  \n```  \n  \n**Field Value**  \n  \n[DependencyPropertyKey](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.DependencyPropertyKey)  \n  \n#### IsPanningPropertyKey  \n  \n```csharp  \npublic static DependencyPropertyKey IsPanningPropertyKey;  \n```  \n  \n**Field Value**  \n  \n[DependencyPropertyKey](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.DependencyPropertyKey)  \n  \n#### IsPushingItemsPropertyKey  \n  \n```csharp  \nprotected static DependencyPropertyKey IsPushingItemsPropertyKey;  \n```  \n  \n**Field Value**  \n  \n[DependencyPropertyKey](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.DependencyPropertyKey)  \n  \n#### IsSelectingPropertyKey  \n  \n```csharp  \nprotected static DependencyPropertyKey IsSelectingPropertyKey;  \n```  \n  \n**Field Value**  \n  \n[DependencyPropertyKey](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.DependencyPropertyKey)  \n  \n#### MouseLocationPropertyKey  \n  \n```csharp  \nprotected static DependencyPropertyKey MouseLocationPropertyKey;  \n```  \n  \n**Field Value**  \n  \n[DependencyPropertyKey](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.DependencyPropertyKey)  \n  \n#### PushedAreaOrientationPropertyKey  \n  \n```csharp  \nprotected static DependencyPropertyKey PushedAreaOrientationPropertyKey;  \n```  \n  \n**Field Value**  \n  \n[DependencyPropertyKey](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.DependencyPropertyKey)  \n  \n#### PushedAreaPropertyKey  \n  \n```csharp  \nprotected static DependencyPropertyKey PushedAreaPropertyKey;  \n```  \n  \n**Field Value**  \n  \n[DependencyPropertyKey](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.DependencyPropertyKey)  \n  \n#### ScaleTransform  \n  \nGets the transform used to zoom on the viewport.  \n  \n```csharp  \nprotected readonly ScaleTransform ScaleTransform;  \n```  \n  \n**Field Value**  \n  \n[ScaleTransform](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Media.ScaleTransform)  \n  \n#### SelectedAreaPropertyKey  \n  \n```csharp  \nprotected static DependencyPropertyKey SelectedAreaPropertyKey;  \n```  \n  \n**Field Value**  \n  \n[DependencyPropertyKey](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.DependencyPropertyKey)  \n  \n#### TranslateTransform  \n  \nGets the transform used to offset the viewport.  \n  \n```csharp  \nprotected readonly TranslateTransform TranslateTransform;  \n```  \n  \n**Field Value**  \n  \n[TranslateTransform](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Media.TranslateTransform)  \n  \n### Properties  \n  \n#### AllowPushItemsCancellation  \n  \nGets or sets whether push items cancellation is allowed.  \n  \n```csharp  \npublic static bool AllowPushItemsCancellation { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean)  \n  \n#### AutoPanEdgeDistance  \n  \nGets or sets the maximum distance in pixels from the edge of the editor that will trigger auto-panning.  \n  \n```csharp  \npublic double AutoPanEdgeDistance { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Double](https://docs.microsoft.com/en-us/dotnet/api/System.Double)  \n  \n#### AutoPanningTickRate  \n  \nGets or sets how often the new [NodifyEditor.ViewportLocation](#nodifyeditor-class#viewportlocation) is calculated in milliseconds when [NodifyEditor.DisableAutoPanning](#nodifyeditor-class#disableautopanning) is false.  \n  \n```csharp  \npublic static double AutoPanningTickRate { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Double](https://docs.microsoft.com/en-us/dotnet/api/System.Double)  \n  \n#### AutoPanSpeed  \n  \nGets or sets the speed used when auto-panning scaled by [NodifyEditor.AutoPanningTickRate](#nodifyeditor-class#autopanningtickrate)  \n  \n```csharp  \npublic double AutoPanSpeed { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Double](https://docs.microsoft.com/en-us/dotnet/api/System.Double)  \n  \n#### BringIntoViewMaxDuration  \n  \nGets or sets the maximum animation duration in seconds for bringing a location into view.  \n  \n```csharp  \npublic double BringIntoViewMaxDuration { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Double](https://docs.microsoft.com/en-us/dotnet/api/System.Double)  \n  \n#### BringIntoViewSpeed  \n  \nGets or sets the animation speed in pixels per second for bringing a location into view.  \n  \n```csharp  \npublic double BringIntoViewSpeed { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Double](https://docs.microsoft.com/en-us/dotnet/api/System.Double)  \n  \n#### CanSelectMultipleConnections  \n  \nGets or sets whether multiple connections can be selected.  \n  \n```csharp  \npublic bool CanSelectMultipleConnections { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean)  \n  \n#### CanSelectMultipleItems  \n  \nGets or sets whether multiple [ItemContainer](#itemcontainer-class)s can be selected.  \n  \n```csharp  \npublic bool CanSelectMultipleItems { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean)  \n  \n#### ConnectionCompletedCommand  \n  \nInvoked when the [PendingConnection](#pendingconnection-class) is completed. \n            Use [PendingConnection.CompletedCommand](#pendingconnection-class#completedcommand) if you want to control the visibility of the connection from the viewmodel. \n            Parameter is System.Tuple`2 where System.Tuple`2.Item1 is the [PendingConnection.Source](#pendingconnection-class#source) and System.Tuple`2.Item2 is [PendingConnection.Target](#pendingconnection-class#target).  \n  \n```csharp  \npublic ICommand ConnectionCompletedCommand { get; set; }  \n```  \n  \n**Property Value**  \n  \n[ICommand](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.ICommand)  \n  \n#### Connections  \n  \nGets or sets the data source that [BaseConnection](#baseconnection-class)s will be generated for.  \n  \n```csharp  \npublic IEnumerable Connections { get; set; }  \n```  \n  \n**Property Value**  \n  \n[IEnumerable](https://docs.microsoft.com/en-us/dotnet/api/System.Collections.IEnumerable)  \n  \n#### ConnectionStartedCommand  \n  \nInvoked when the [PendingConnection](#pendingconnection-class) is completed. \n            Use [PendingConnection.StartedCommand](#pendingconnection-class#startedcommand) if you want to control the visibility of the connection from the viewmodel. \n            Parameter is [PendingConnection.Source](#pendingconnection-class#source).  \n  \n```csharp  \npublic ICommand ConnectionStartedCommand { get; set; }  \n```  \n  \n**Property Value**  \n  \n[ICommand](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.ICommand)  \n  \n#### ConnectionTemplate  \n  \nGets or sets the [DataTemplate](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.DataTemplate) to use when generating a new [BaseConnection](#baseconnection-class).  \n  \n```csharp  \npublic DataTemplate ConnectionTemplate { get; set; }  \n```  \n  \n**Property Value**  \n  \n[DataTemplate](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.DataTemplate)  \n  \n#### CuttingCompletedCommand  \n  \nInvoked when a cutting operation is completed.  \n  \n```csharp  \npublic ICommand CuttingCompletedCommand { get; set; }  \n```  \n  \n**Property Value**  \n  \n[ICommand](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.ICommand)  \n  \n#### CuttingLineEnd  \n  \nGets the end point of the [CuttingLine](#cuttingline-class) while [NodifyEditor.IsCutting](#nodifyeditor-class#iscutting) is true.  \n  \n```csharp  \npublic Point CuttingLineEnd { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n#### CuttingLineStart  \n  \nGets the start point of the [CuttingLine](#cuttingline-class) while [NodifyEditor.IsCutting](#nodifyeditor-class#iscutting) is true.  \n  \n```csharp  \npublic Point CuttingLineStart { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n#### CuttingLineStyle  \n  \nGets or sets the style to use for the cutting line.  \n  \n```csharp  \npublic Style CuttingLineStyle { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Style](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Style)  \n  \n#### CuttingStartedCommand  \n  \nInvoked when a cutting operation is started.  \n  \n```csharp  \npublic ICommand CuttingStartedCommand { get; set; }  \n```  \n  \n**Property Value**  \n  \n[ICommand](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.ICommand)  \n  \n#### DecoratorContainerStyle  \n  \nGets or sets the style to use for the [DecoratorContainer](#decoratorcontainer-class).  \n  \n```csharp  \npublic Style DecoratorContainerStyle { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Style](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Style)  \n  \n#### Decorators  \n  \nGets or sets the items that will be rendered in the decorators layer via [DecoratorContainer](#decoratorcontainer-class)s.  \n  \n```csharp  \npublic IEnumerable Decorators { get; set; }  \n```  \n  \n**Property Value**  \n  \n[IEnumerable](https://docs.microsoft.com/en-us/dotnet/api/System.Collections.IEnumerable)  \n  \n#### DecoratorsExtent  \n  \nThe area covered by the [DecoratorContainer](#decoratorcontainer-class)s.  \n  \n```csharp  \npublic Rect DecoratorsExtent { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Rect](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Rect)  \n  \n#### DecoratorTemplate  \n  \nGets or sets the [DataTemplate](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.DataTemplate) to use when generating a new [DecoratorContainer](#decoratorcontainer-class).  \n  \n```csharp  \npublic DataTemplate DecoratorTemplate { get; set; }  \n```  \n  \n**Property Value**  \n  \n[DataTemplate](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.DataTemplate)  \n  \n#### DisableAutoPanning  \n  \nGets or sets whether to disable the auto panning when selecting or dragging near the edge of the editor configured by [NodifyEditor.AutoPanEdgeDistance](#nodifyeditor-class#autopanedgedistance).  \n  \n```csharp  \npublic bool DisableAutoPanning { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean)  \n  \n#### DisablePanning  \n  \nGets or sets whether panning should be disabled.  \n  \n```csharp  \npublic bool DisablePanning { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean)  \n  \n#### DisableZooming  \n  \nGets or sets whether zooming should be disabled.  \n  \n```csharp  \npublic bool DisableZooming { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean)  \n  \n#### DisconnectConnectorCommand  \n  \nInvoked when the [Connector.Disconnect](#connector-class#disconnect) event is raised. \n            Can also be handled at the [Connector](#connector-class) level using the [Connector.DisconnectCommand](#connector-class#disconnectcommand) command. \n            Parameter is the [Connector](#connector-class)'s [FrameworkElement.DataContext](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.FrameworkElement.datacontext).  \n  \n```csharp  \npublic ICommand DisconnectConnectorCommand { get; set; }  \n```  \n  \n**Property Value**  \n  \n[ICommand](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.ICommand)  \n  \n#### DisplayConnectionsOnTop  \n  \nGets or sets whether to display connections on top of [ItemContainer](#itemcontainer-class)s or not.  \n  \n```csharp  \npublic bool DisplayConnectionsOnTop { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean)  \n  \n#### EnableCuttingLinePreview  \n  \nGets or sets whether the cutting line should apply the preview style to the interesected elements.  \n  \n```csharp  \npublic static bool EnableCuttingLinePreview { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean)  \n  \n#### EnableDraggingContainersOptimizations  \n  \nGets or sets if the current position of containers that are being dragged should not be committed until the end of the dragging operation.  \n  \n```csharp  \npublic static bool EnableDraggingContainersOptimizations { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean)  \n  \n#### EnableRealtimeSelection  \n  \nEnables selecting and deselecting items while the [NodifyEditor.SelectedArea](#nodifyeditor-class#selectedarea) changes.\n            Disable for maximum performance when hundreds of items are generated.  \n  \n```csharp  \npublic bool EnableRealtimeSelection { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean)  \n  \n#### EnableRenderingContainersOptimizations  \n  \nGets or sets if [NodifyEditor](#nodifyeditor-class)s should enable optimizations based on [NodifyEditor.OptimizeRenderingMinimumContainers](#nodifyeditor-class#optimizerenderingminimumcontainers) and [NodifyEditor.OptimizeRenderingZoomOutPercent](#nodifyeditor-class#optimizerenderingzoomoutpercent).  \n  \n```csharp  \npublic static bool EnableRenderingContainersOptimizations { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean)  \n  \n#### EnableSnappingCorrection  \n  \nCorrect [ItemContainer](#itemcontainer-class)'s position after moving if starting position is not snapped to grid.  \n  \n```csharp  \npublic static bool EnableSnappingCorrection { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean)  \n  \n#### FitToScreenExtentMargin  \n  \nGets or sets the margin to add in all directions to the [NodifyEditor.ItemsExtent](#nodifyeditor-class#itemsextent) or area parameter when using Nodify.NodifyEditor.FitToScreen(System.Nullable{System.Windows.Rect}).  \n  \n```csharp  \npublic static double FitToScreenExtentMargin { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Double](https://docs.microsoft.com/en-us/dotnet/api/System.Double)  \n  \n#### GridCellSize  \n  \nGets or sets the value of an invisible grid used to adjust locations (snapping) of [ItemContainer](#itemcontainer-class)s.  \n  \n```csharp  \npublic uint GridCellSize { get; set; }  \n```  \n  \n**Property Value**  \n  \n[UInt32](https://docs.microsoft.com/en-us/dotnet/api/System.UInt32)  \n  \n#### HandleRightClickAfterPanningThreshold  \n  \nGets or sets the maximum number of pixels allowed to move the mouse before cancelling the mouse event.\n            Useful for System.Windows.Controls.ContextMenus to appear if mouse only moved a bit or not at all.  \n  \n```csharp  \npublic static double HandleRightClickAfterPanningThreshold { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Double](https://docs.microsoft.com/en-us/dotnet/api/System.Double)  \n  \n#### IsBulkUpdatingItems  \n  \nTells if the [NodifyEditor](#nodifyeditor-class) is doing operations on multiple items at once.  \n  \n```csharp  \npublic bool IsBulkUpdatingItems { get; protected set; }  \n```  \n  \n**Property Value**  \n  \n[Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean)  \n  \n#### IsCutting  \n  \nGets a value that indicates whether a cutting operation is in progress.  \n  \n```csharp  \npublic bool IsCutting { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean)  \n  \n#### IsPanning  \n  \nGets a value that indicates whether a panning operation is in progress.  \n  \n```csharp  \npublic bool IsPanning { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean)  \n  \n#### IsPushingItems  \n  \nGets a value that indicates whether a pushing operation is in progress.  \n  \n```csharp  \npublic bool IsPushingItems { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean)  \n  \n#### IsSelecting  \n  \nGets a value that indicates whether a selection operation is in progress.  \n  \n```csharp  \npublic bool IsSelecting { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean)  \n  \n#### ItemsDragCompletedCommand  \n  \nInvoked when a drag operation is completed for the [NodifyEditor.SelectedItems](#nodifyeditor-class#selecteditems).  \n  \n```csharp  \npublic ICommand ItemsDragCompletedCommand { get; set; }  \n```  \n  \n**Property Value**  \n  \n[ICommand](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.ICommand)  \n  \n#### ItemsDragStartedCommand  \n  \nInvoked when a drag operation starts for the [NodifyEditor.SelectedItems](#nodifyeditor-class#selecteditems).  \n  \n```csharp  \npublic ICommand ItemsDragStartedCommand { get; set; }  \n```  \n  \n**Property Value**  \n  \n[ICommand](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.ICommand)  \n  \n#### ItemsExtent  \n  \nThe area covered by the [ItemContainer](#itemcontainer-class)s.  \n  \n```csharp  \npublic Rect ItemsExtent { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Rect](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Rect)  \n  \n#### ItemsSelectCompletedCommand  \n  \nInvoked when a selection operation is completed.  \n  \n```csharp  \npublic ICommand ItemsSelectCompletedCommand { get; set; }  \n```  \n  \n**Property Value**  \n  \n[ICommand](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.ICommand)  \n  \n#### ItemsSelectStartedCommand  \n  \nInvoked when a selection operation is started.  \n  \n```csharp  \npublic ICommand ItemsSelectStartedCommand { get; set; }  \n```  \n  \n**Property Value**  \n  \n[ICommand](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.ICommand)  \n  \n#### MaxViewportZoom  \n  \nGets or sets the maximum zoom factor of the viewport  \n  \n```csharp  \npublic double MaxViewportZoom { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Double](https://docs.microsoft.com/en-us/dotnet/api/System.Double)  \n  \n#### MinViewportZoom  \n  \nGets or sets the minimum zoom factor of the viewport  \n  \n```csharp  \npublic double MinViewportZoom { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Double](https://docs.microsoft.com/en-us/dotnet/api/System.Double)  \n  \n#### MouseLocation  \n  \nGets the current mouse location in graph space coordinates (relative to the [NodifyEditor.ItemsHost](#nodifyeditor-class#itemshost)).  \n  \n```csharp  \npublic Point MouseLocation { get; protected set; }  \n```  \n  \n**Property Value**  \n  \n[Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n#### OptimizeRenderingMinimumContainers  \n  \nGets or sets the minimum number of [ItemContainer](#itemcontainer-class)s needed to trigger optimizations when reaching the [NodifyEditor.OptimizeRenderingZoomOutPercent](#nodifyeditor-class#optimizerenderingzoomoutpercent).  \n  \n```csharp  \npublic static uint OptimizeRenderingMinimumContainers { get; set; }  \n```  \n  \n**Property Value**  \n  \n[UInt32](https://docs.microsoft.com/en-us/dotnet/api/System.UInt32)  \n  \n#### OptimizeRenderingZoomOutPercent  \n  \nGets or sets the minimum zoom out percent needed to start optimizing the rendering for [ItemContainer](#itemcontainer-class)s.\n            Value is between 0 and 1.  \n  \n```csharp  \npublic static double OptimizeRenderingZoomOutPercent { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Double](https://docs.microsoft.com/en-us/dotnet/api/System.Double)  \n  \n#### PendingConnection  \n  \nGets of sets the [FrameworkElement.DataContext](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.FrameworkElement.datacontext) of the [PendingConnection](#pendingconnection-class).  \n  \n```csharp  \npublic object PendingConnection { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object)  \n  \n#### PendingConnectionTemplate  \n  \nGets or sets the [DataTemplate](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.DataTemplate) to use for the [NodifyEditor.PendingConnection](#nodifyeditor-class#pendingconnection).  \n  \n```csharp  \npublic DataTemplate PendingConnectionTemplate { get; set; }  \n```  \n  \n**Property Value**  \n  \n[DataTemplate](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.DataTemplate)  \n  \n#### PushedArea  \n  \nGets the currently pushed area while [NodifyEditor.IsPushingItems](#nodifyeditor-class#ispushingitems) is true.  \n  \n```csharp  \npublic Rect PushedArea { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Rect](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Rect)  \n  \n#### PushedAreaOrientation  \n  \nGets the orientation of the [NodifyEditor.PushedArea](#nodifyeditor-class#pushedarea).  \n  \n```csharp  \npublic Orientation PushedAreaOrientation { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Orientation](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Controls.Orientation)  \n  \n#### PushedAreaStyle  \n  \nGets or sets the style to use for the pushed area.  \n  \n```csharp  \npublic Style PushedAreaStyle { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Style](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Style)  \n  \n#### RemoveConnectionCommand  \n  \nInvoked when the [BaseConnection.Disconnect](#baseconnection-class#disconnect) event is raised. \n            Can also be handled at the [BaseConnection](#baseconnection-class) level using the [BaseConnection.DisconnectCommand](#baseconnection-class#disconnectcommand) command. \n            Parameter is the [BaseConnection](#baseconnection-class)'s [FrameworkElement.DataContext](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.FrameworkElement.datacontext).  \n  \n```csharp  \npublic ICommand RemoveConnectionCommand { get; set; }  \n```  \n  \n**Property Value**  \n  \n[ICommand](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.ICommand)  \n  \n#### ScrollIncrement  \n  \nThe number of units the mouse wheel is rotated to scroll one line.  \n  \n```csharp  \npublic static double ScrollIncrement { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Double](https://docs.microsoft.com/en-us/dotnet/api/System.Double)  \n  \n#### SelectedArea  \n  \nGets the currently selected area while [NodifyEditor.IsSelecting](#nodifyeditor-class#isselecting) is true.  \n  \n```csharp  \npublic Rect SelectedArea { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Rect](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Rect)  \n  \n#### SelectedConnection  \n  \nGets or sets the selected connection.  \n  \n```csharp  \npublic object SelectedConnection { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object)  \n  \n#### SelectedConnections  \n  \nGets or sets the selected connections in the [NodifyEditor](#nodifyeditor-class).  \n  \n```csharp  \npublic IList SelectedConnections { get; set; }  \n```  \n  \n**Property Value**  \n  \n[IList](https://docs.microsoft.com/en-us/dotnet/api/System.Collections.IList)  \n  \n#### SelectedItems  \n  \nGets or sets the selected items in the [NodifyEditor](#nodifyeditor-class).  \n  \n```csharp  \npublic IList SelectedItems { get; set; }  \n```  \n  \n**Property Value**  \n  \n[IList](https://docs.microsoft.com/en-us/dotnet/api/System.Collections.IList)  \n  \n#### SelectionRectangleStyle  \n  \nGets or sets the style to use for the selection rectangle.  \n  \n```csharp  \npublic Style SelectionRectangleStyle { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Style](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Style)  \n  \n#### State  \n  \nThe current state of the editor.  \n  \n```csharp  \npublic EditorState State { get; set; }  \n```  \n  \n**Property Value**  \n  \n[EditorState](#editorstate-class)  \n  \n#### ViewportLocation  \n  \nGets or sets the viewport's top-left coordinates in graph space coordinates.  \n  \n```csharp  \npublic Point ViewportLocation { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n#### ViewportSize  \n  \nGets the size of the viewport in graph space (scaled by the [NodifyEditor.ViewportZoom](#nodifyeditor-class#viewportzoom)).  \n  \n```csharp  \npublic Size ViewportSize { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Size](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Size)  \n  \n#### ViewportTransform  \n  \nGets the transform that is applied to all child controls.  \n  \n```csharp  \npublic Transform ViewportTransform { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Transform](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Media.Transform)  \n  \n#### ViewportZoom  \n  \nGets or sets the zoom factor of the viewport.  \n  \n```csharp  \npublic double ViewportZoom { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Double](https://docs.microsoft.com/en-us/dotnet/api/System.Double)  \n  \n### Methods  \n  \n#### BringIntoView(Point, Boolean, Action)  \n  \nMoves the viewport center at the specified location.  \n  \n```csharp  \npublic void BringIntoView(Point point, bool animated = true, Action onFinish = null);  \n```  \n  \n**Parameters**  \n  \n`point` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point): The location in graph space coordinates.  \n  \n`animated` [Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean): True to animate the movement.  \n  \n`onFinish` [Action](https://docs.microsoft.com/en-us/dotnet/api/System.Action): The callback invoked when movement is finished.  \n  \n#### BringIntoView(Rect)  \n  \nMoves the viewport center at the center of the specified area.  \n  \n```csharp  \npublic void BringIntoView(Rect area);  \n```  \n  \n**Parameters**  \n  \n`area` [Rect](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Rect): The location in graph space coordinates.  \n  \n#### FitToScreen(Rect?)  \n  \nScales the viewport to fit the specified area or all the [ItemContainer](#itemcontainer-class)s if that's possible.  \n  \n```csharp  \npublic void FitToScreen(Rect? area = null);  \n```  \n  \n**Parameters**  \n  \n`area` [Rect?](https://docs.microsoft.com/en-us/dotnet/api/System.Nullable)  \n  \n#### GetContainerForItemOverride()  \n  \n```csharp  \nprotected override DependencyObject GetContainerForItemOverride();  \n```  \n  \n**Returns**  \n  \n[DependencyObject](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.DependencyObject)  \n  \n#### GetInitialState()  \n  \nCreates the initial state of the editor.  \n  \n```csharp  \nprotected virtual EditorState GetInitialState();  \n```  \n  \n**Returns**  \n  \n[EditorState](#editorstate-class): The initial state.  \n  \n#### GetLocationInsideEditor(Point, UIElement)  \n  \nTranslates the specified location to graph space coordinates (relative to the [NodifyEditor.ItemsHost](#nodifyeditor-class#itemshost)).  \n  \n```csharp  \npublic Point GetLocationInsideEditor(Point location, UIElement relativeTo);  \n```  \n  \n**Parameters**  \n  \n`location` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point): The location coordinates relative to relativeTo  \n  \n`relativeTo` [UIElement](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.UIElement): The element where the location was calculated from.  \n  \n**Returns**  \n  \n[Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point): A location inside the graph.  \n  \n#### GetLocationInsideEditor(DragEventArgs)  \n  \nTranslates the event location to graph space coordinates (relative to the [NodifyEditor.ItemsHost](#nodifyeditor-class#itemshost)).  \n  \n```csharp  \npublic Point GetLocationInsideEditor(DragEventArgs args);  \n```  \n  \n**Parameters**  \n  \n`args` [DragEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.DragEventArgs): The drag event.  \n  \n**Returns**  \n  \n[Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point): A location inside the graph  \n  \n#### GetLocationInsideEditor(MouseEventArgs)  \n  \nTranslates the event location to graph space coordinates (relative to the [NodifyEditor.ItemsHost](#nodifyeditor-class#itemshost)).  \n  \n```csharp  \npublic Point GetLocationInsideEditor(MouseEventArgs args);  \n```  \n  \n**Parameters**  \n  \n`args` [MouseEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.MouseEventArgs): The mouse event.  \n  \n**Returns**  \n  \n[Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point): A location inside the graph  \n  \n#### InvertSelection(Rect, Boolean)  \n  \nInverts the [ItemContainer](#itemcontainer-class)s selection in the specified area.  \n  \n```csharp  \npublic void InvertSelection(Rect area, bool fit = false);  \n```  \n  \n**Parameters**  \n  \n`area` [Rect](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Rect): The area to look for [ItemContainer](#itemcontainer-class)s.  \n  \n`fit` [Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean): True to check if the area contains the [ItemContainer](#itemcontainer-class). False to check if area intersects the [ItemContainer](#itemcontainer-class).  \n  \n#### IsItemItsOwnContainerOverride(Object)  \n  \n```csharp  \nprotected override bool IsItemItsOwnContainerOverride(object item);  \n```  \n  \n**Parameters**  \n  \n`item` [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object)  \n  \n**Returns**  \n  \n[Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean)  \n  \n#### OnApplyTemplate()  \n  \n```csharp  \npublic override void OnApplyTemplate();  \n```  \n  \n#### OnDisableAutoPanningChanged(Boolean)  \n  \nCalled when the [NodifyEditor.DisableAutoPanning](#nodifyeditor-class#disableautopanning) changes.  \n  \n```csharp  \nprotected virtual void OnDisableAutoPanningChanged(bool shouldDisable);  \n```  \n  \n**Parameters**  \n  \n`shouldDisable` [Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean): Whether to enable or disable auto panning.  \n  \n#### OnKeyDown(KeyEventArgs)  \n  \n```csharp  \nprotected override void OnKeyDown(KeyEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [KeyEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.KeyEventArgs)  \n  \n#### OnKeyUp(KeyEventArgs)  \n  \n```csharp  \nprotected override void OnKeyUp(KeyEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [KeyEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.KeyEventArgs)  \n  \n#### OnLostMouseCapture(MouseEventArgs)  \n  \n```csharp  \nprotected override void OnLostMouseCapture(MouseEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [MouseEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.MouseEventArgs)  \n  \n#### OnMouseDown(MouseButtonEventArgs)  \n  \n```csharp  \nprotected override void OnMouseDown(MouseButtonEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [MouseButtonEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.MouseButtonEventArgs)  \n  \n#### OnMouseMove(MouseEventArgs)  \n  \n```csharp  \nprotected override void OnMouseMove(MouseEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [MouseEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.MouseEventArgs)  \n  \n#### OnMouseUp(MouseButtonEventArgs)  \n  \n```csharp  \nprotected override void OnMouseUp(MouseButtonEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [MouseButtonEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.MouseButtonEventArgs)  \n  \n#### OnMouseWheel(MouseWheelEventArgs)  \n  \n```csharp  \nprotected override void OnMouseWheel(MouseWheelEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [MouseWheelEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.MouseWheelEventArgs)  \n  \n#### OnRemoveConnection(Object)  \n  \n```csharp  \nprotected void OnRemoveConnection(object dataContext);  \n```  \n  \n**Parameters**  \n  \n`dataContext` [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object)  \n  \n#### OnRenderSizeChanged(SizeChangedInfo)  \n  \n```csharp  \nprotected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo);  \n```  \n  \n**Parameters**  \n  \n`sizeInfo` [SizeChangedInfo](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.SizeChangedInfo)  \n  \n#### OnSelectionChanged(SelectionChangedEventArgs)  \n  \n```csharp  \nprotected override void OnSelectionChanged(SelectionChangedEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [SelectionChangedEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Controls.SelectionChangedEventArgs)  \n  \n#### OnViewportUpdated()  \n  \nUpdates the [NodifyEditor.ViewportSize](#nodifyeditor-class#viewportsize) and raises the [NodifyEditor.ViewportUpdatedEvent](#nodifyeditor-class#viewportupdatedevent).\n            Called when the [UIElement.RenderSize](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.UIElement.rendersize) or [NodifyEditor.ViewportZoom](#nodifyeditor-class#viewportzoom) is changed.  \n  \n```csharp  \nprotected void OnViewportUpdated();  \n```  \n  \n#### PopAllStates()  \n  \nPops all states from the editor.  \n  \n```csharp  \npublic void PopAllStates();  \n```  \n  \n#### PopState()  \n  \nPops the current [NodifyEditor.State](#nodifyeditor-class#state) from the stack.  \n  \n```csharp  \npublic void PopState();  \n```  \n  \n#### PushState(EditorState)  \n  \nPushes the given state to the stack.  \n  \n```csharp  \npublic void PushState(EditorState state);  \n```  \n  \n**Parameters**  \n  \n`state` [EditorState](#editorstate-class): The new state of the editor.  \n  \n#### SelectAllConnections()  \n  \nSelect all [NodifyEditor.Connections](#nodifyeditor-class#connections).  \n  \n```csharp  \npublic void SelectAllConnections();  \n```  \n  \n#### SelectArea(Rect, Boolean, Boolean)  \n  \nSelects the [ItemContainer](#itemcontainer-class)s in the specified area.  \n  \n```csharp  \npublic void SelectArea(Rect area, bool append = false, bool fit = false);  \n```  \n  \n**Parameters**  \n  \n`area` [Rect](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Rect): The area to look for [ItemContainer](#itemcontainer-class)s.  \n  \n`append` [Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean): If true, it will add to the existing selection.  \n  \n`fit` [Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean): True to check if the area contains the [ItemContainer](#itemcontainer-class). False to check if area intersects the [ItemContainer](#itemcontainer-class).  \n  \n#### SnapToGrid(Double)  \n  \nSnaps the given value down to the nearest multiple of the grid cell size.  \n  \n```csharp  \npublic double SnapToGrid(double value);  \n```  \n  \n**Parameters**  \n  \n`value` [Double](https://docs.microsoft.com/en-us/dotnet/api/System.Double): The value to be snapped to the grid.  \n  \n**Returns**  \n  \n[Double](https://docs.microsoft.com/en-us/dotnet/api/System.Double): The largest multiple of the grid cell size less than or equal to the value.  \n  \n#### UnselectAllConnection()  \n  \nUnselect all [NodifyEditor.Connections](#nodifyeditor-class#connections).  \n  \n```csharp  \npublic void UnselectAllConnection();  \n```  \n  \n#### UnselectArea(Rect, Boolean)  \n  \nUnselect the [ItemContainer](#itemcontainer-class)s in the specified area.  \n  \n```csharp  \npublic void UnselectArea(Rect area, bool fit = false);  \n```  \n  \n**Parameters**  \n  \n`area` [Rect](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Rect): The area to look for [ItemContainer](#itemcontainer-class)s.  \n  \n`fit` [Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean): True to check if the area contains the [ItemContainer](#itemcontainer-class). False to check if area intersects the [ItemContainer](#itemcontainer-class).  \n  \n#### ZoomAtPosition(Double, Point)  \n  \nZoom at the specified location in graph space coordinates.  \n  \n```csharp  \npublic void ZoomAtPosition(double zoom, Point location);  \n```  \n  \n**Parameters**  \n  \n`zoom` [Double](https://docs.microsoft.com/en-us/dotnet/api/System.Double): The zoom factor.  \n  \n`location` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point): The location to focus when zooming.  \n  \n#### ZoomIn()  \n  \nZoom in at the viewports center  \n  \n```csharp  \npublic void ZoomIn();  \n```  \n  \n#### ZoomOut()  \n  \nZoom out at the viewports center  \n  \n```csharp  \npublic void ZoomOut();  \n```  \n  \n### Events  \n  \n#### ViewportUpdated  \n  \nOccurs whenever the viewport updates.  \n  \n```csharp  \npublic event RoutedEventHandler ViewportUpdated;  \n```  \n  \n**Event Type**  \n  \n[RoutedEventHandler](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.RoutedEventHandler)  \n  \n## NodifyEditorGestures Class  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**Inheritance:** [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object) → [NodifyEditorGestures](#nodifyeditorgestures-class)  \n  \n**References:** [EditorGestures](#editorgestures-class), [InputGestureRef](#inputgestureref-class), [SelectionGestures](#selectiongestures-class)  \n  \n```csharp  \npublic class NodifyEditorGestures  \n```  \n  \n### Constructors  \n  \n#### NodifyEditorGestures()  \n  \n```csharp  \npublic NodifyEditorGestures();  \n```  \n  \n### Properties  \n  \n#### CancelAction  \n  \n```csharp  \npublic InputGestureRef CancelAction { get; set; }  \n```  \n  \n**Property Value**  \n  \n[InputGestureRef](#inputgestureref-class)  \n  \n#### Cutting  \n  \n```csharp  \npublic InputGestureRef Cutting { get; set; }  \n```  \n  \n**Property Value**  \n  \n[InputGestureRef](#inputgestureref-class)  \n  \n#### FitToScreen  \n  \n```csharp  \npublic InputGestureRef FitToScreen { get; set; }  \n```  \n  \n**Property Value**  \n  \n[InputGestureRef](#inputgestureref-class)  \n  \n#### Pan  \n  \n```csharp  \npublic InputGestureRef Pan { get; set; }  \n```  \n  \n**Property Value**  \n  \n[InputGestureRef](#inputgestureref-class)  \n  \n#### PanHorizontalModifierKey  \n  \n```csharp  \npublic ModifierKeys PanHorizontalModifierKey { get; set; }  \n```  \n  \n**Property Value**  \n  \n[ModifierKeys](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.ModifierKeys)  \n  \n#### PanVerticalModifierKey  \n  \n```csharp  \npublic ModifierKeys PanVerticalModifierKey { get; set; }  \n```  \n  \n**Property Value**  \n  \n[ModifierKeys](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.ModifierKeys)  \n  \n#### PanWithMouseWheel  \n  \n```csharp  \npublic bool PanWithMouseWheel { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean)  \n  \n#### PushItems  \n  \n```csharp  \npublic InputGestureRef PushItems { get; set; }  \n```  \n  \n**Property Value**  \n  \n[InputGestureRef](#inputgestureref-class)  \n  \n#### ResetViewportLocation  \n  \n```csharp  \npublic InputGestureRef ResetViewportLocation { get; set; }  \n```  \n  \n**Property Value**  \n  \n[InputGestureRef](#inputgestureref-class)  \n  \n#### Selection  \n  \n```csharp  \npublic SelectionGestures Selection { get; set; }  \n```  \n  \n**Property Value**  \n  \n[SelectionGestures](#selectiongestures-class)  \n  \n#### ZoomIn  \n  \n```csharp  \npublic InputGestureRef ZoomIn { get; set; }  \n```  \n  \n**Property Value**  \n  \n[InputGestureRef](#inputgestureref-class)  \n  \n#### ZoomModifierKey  \n  \n```csharp  \npublic ModifierKeys ZoomModifierKey { get; set; }  \n```  \n  \n**Property Value**  \n  \n[ModifierKeys](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.ModifierKeys)  \n  \n#### ZoomOut  \n  \n```csharp  \npublic InputGestureRef ZoomOut { get; set; }  \n```  \n  \n**Property Value**  \n  \n[InputGestureRef](#inputgestureref-class)  \n  \n### Methods  \n  \n#### Apply(NodifyEditorGestures)  \n  \n```csharp  \npublic void Apply(NodifyEditorGestures gestures);  \n```  \n  \n**Parameters**  \n  \n`gestures` [NodifyEditorGestures](#nodifyeditorgestures-class)  \n  \n## PendingConnection Class  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**Inheritance:** [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object) → [DispatcherObject](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Threading.DispatcherObject) → [DependencyObject](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.DependencyObject) → [Visual](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Media.Visual) → [UIElement](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.UIElement) → [FrameworkElement](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.FrameworkElement) → [Control](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Controls.Control) → [ContentControl](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Controls.ContentControl) → [PendingConnection](#pendingconnection-class)  \n  \n**References:** [ConnectionDirection](#connectiondirection-enum), [Connector](#connector-class), [ItemContainer](#itemcontainer-class), [NodifyEditor](#nodifyeditor-class), [PendingConnectionEventArgs](#pendingconnectioneventargs-class), [PendingConnectionEventHandler](#pendingconnectioneventhandler-delegate), [StateNode](#statenode-class)  \n  \nRepresents a pending connection usually started by a [Connector](#connector-class) which invokes the [PendingConnection.CompletedCommand](#pendingconnection-class#completedcommand) when completed.  \n  \n```csharp  \npublic class PendingConnection : ContentControl  \n```  \n  \n### Constructors  \n  \n#### PendingConnection()  \n  \n```csharp  \npublic PendingConnection();  \n```  \n  \n### Properties  \n  \n#### AllowOnlyConnectors  \n  \nIf true will preview and connect only to [Connector](#connector-class)s, otherwise will also enable [ItemContainer](#itemcontainer-class)s.  \n  \n```csharp  \npublic bool AllowOnlyConnectors { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean)  \n  \n#### CompletedCommand  \n  \nGets or sets the command to invoke when the pending connection is completed.\n            Will not be invoked if [NodifyEditor.ConnectionCompletedCommand](#nodifyeditor-class#connectioncompletedcommand) is used.\n            [PendingConnection.Target](#pendingconnection-class#target) will be set to the desired [Connector](#connector-class)'s [FrameworkElement.DataContext](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.FrameworkElement.datacontext) and will also be the command's parameter.  \n  \n```csharp  \npublic ICommand CompletedCommand { get; set; }  \n```  \n  \n**Property Value**  \n  \n[ICommand](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.ICommand)  \n  \n#### Direction  \n  \nGets or sets the direction of this connection.  \n  \n```csharp  \npublic ConnectionDirection Direction { get; set; }  \n```  \n  \n**Property Value**  \n  \n[ConnectionDirection](#connectiondirection-enum)  \n  \n#### Editor  \n  \nGets the [NodifyEditor](#nodifyeditor-class) that owns this [PendingConnection](#pendingconnection-class).  \n  \n```csharp  \nprotected NodifyEditor Editor { get; set; }  \n```  \n  \n**Property Value**  \n  \n[NodifyEditor](#nodifyeditor-class)  \n  \n#### EnablePreview  \n  \n[PendingConnection.PreviewTarget](#pendingconnection-class#previewtarget) will be updated with a potential [Connector](#connector-class)'s [FrameworkElement.DataContext](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.FrameworkElement.datacontext) if this is true.  \n  \n```csharp  \npublic bool EnablePreview { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean)  \n  \n#### EnableSnapping  \n  \nEnables snapping the [PendingConnection.TargetAnchor](#pendingconnection-class#targetanchor) to a possible [PendingConnection.Target](#pendingconnection-class#target) connector.  \n  \n```csharp  \npublic bool EnableSnapping { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean)  \n  \n#### IsVisible  \n  \nGets or sets the visibility of the connection.  \n  \n```csharp  \npublic bool IsVisible { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean)  \n  \n#### PreviewTarget  \n  \nGets or sets the [Connector](#connector-class) or the [ItemContainer](#itemcontainer-class) (if [PendingConnection.AllowOnlyConnectors](#pendingconnection-class#allowonlyconnectors) is false) that we're previewing.  \n  \n```csharp  \npublic object PreviewTarget { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object)  \n  \n#### Source  \n  \nGets or sets the [Connector](#connector-class)'s [FrameworkElement.DataContext](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.FrameworkElement.datacontext) that started this pending connection.  \n  \n```csharp  \npublic object Source { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object)  \n  \n#### SourceAnchor  \n  \nGets or sets the starting point for the connection.  \n  \n```csharp  \npublic Point SourceAnchor { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n#### StartedCommand  \n  \nGets or sets the command to invoke when the pending connection is started.\n            Will not be invoked if [NodifyEditor.ConnectionStartedCommand](#nodifyeditor-class#connectionstartedcommand) is used.\n            [PendingConnection.Source](#pendingconnection-class#source) will be set to the [Connector](#connector-class)'s [FrameworkElement.DataContext](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.FrameworkElement.datacontext) that started this connection and will also be the command's parameter.  \n  \n```csharp  \npublic ICommand StartedCommand { get; set; }  \n```  \n  \n**Property Value**  \n  \n[ICommand](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.ICommand)  \n  \n#### Stroke  \n  \nGets or sets the stroke color of the connection.  \n  \n```csharp  \npublic Brush Stroke { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Brush](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Media.Brush)  \n  \n#### StrokeDashArray  \n  \nGets or sets the pattern of dashes and gaps that is used to outline the connection.  \n  \n```csharp  \npublic DoubleCollection StrokeDashArray { get; set; }  \n```  \n  \n**Property Value**  \n  \n[DoubleCollection](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Media.DoubleCollection)  \n  \n#### StrokeThickness  \n  \nGets or set the connection thickness.  \n  \n```csharp  \npublic double StrokeThickness { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Double](https://docs.microsoft.com/en-us/dotnet/api/System.Double)  \n  \n#### Target  \n  \nGets or sets the [Connector](#connector-class)'s [FrameworkElement.DataContext](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.FrameworkElement.datacontext) (or potentially an [ItemContainer](#itemcontainer-class)'s [FrameworkElement.DataContext](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.FrameworkElement.datacontext) if [PendingConnection.AllowOnlyConnectors](#pendingconnection-class#allowonlyconnectors) is false) that the [PendingConnection.Source](#pendingconnection-class#source) can connect to.\n            Only set when the connection is completed (see [PendingConnection.CompletedCommand](#pendingconnection-class#completedcommand)).  \n  \n```csharp  \npublic object Target { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object)  \n  \n#### TargetAnchor  \n  \nGets or sets the end point for the connection.  \n  \n```csharp  \npublic Point TargetAnchor { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n### Methods  \n  \n#### GetIsOverElement(UIElement)  \n  \n```csharp  \npublic static bool GetIsOverElement(UIElement elem);  \n```  \n  \n**Parameters**  \n  \n`elem` [UIElement](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.UIElement)  \n  \n**Returns**  \n  \n[Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean)  \n  \n#### OnApplyTemplate()  \n  \n```csharp  \npublic override void OnApplyTemplate();  \n```  \n  \n#### OnPendingConnectionCompleted(Object, PendingConnectionEventArgs)  \n  \n```csharp  \nprotected virtual void OnPendingConnectionCompleted(object sender, PendingConnectionEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`sender` [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object)  \n  \n`e` [PendingConnectionEventArgs](#pendingconnectioneventargs-class)  \n  \n#### OnPendingConnectionDrag(Object, PendingConnectionEventArgs)  \n  \n```csharp  \nprotected virtual void OnPendingConnectionDrag(object sender, PendingConnectionEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`sender` [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object)  \n  \n`e` [PendingConnectionEventArgs](#pendingconnectioneventargs-class)  \n  \n#### OnPendingConnectionStarted(Object, PendingConnectionEventArgs)  \n  \n```csharp  \nprotected virtual void OnPendingConnectionStarted(object sender, PendingConnectionEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`sender` [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object)  \n  \n`e` [PendingConnectionEventArgs](#pendingconnectioneventargs-class)  \n  \n#### SetIsOverElement(UIElement, Boolean)  \n  \n```csharp  \npublic static void SetIsOverElement(UIElement elem, bool value);  \n```  \n  \n**Parameters**  \n  \n`elem` [UIElement](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.UIElement)  \n  \n`value` [Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean)  \n  \n## PendingConnectionEventArgs Class  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**Inheritance:** [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object) → [EventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.EventArgs) → [RoutedEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.RoutedEventArgs) → [PendingConnectionEventArgs](#pendingconnectioneventargs-class)  \n  \n**References:** [Connector](#connector-class), [PendingConnection](#pendingconnection-class), [PendingConnectionEventHandler](#pendingconnectioneventhandler-delegate)  \n  \nProvides data for [PendingConnection](#pendingconnection-class) related routed events.  \n  \n```csharp  \npublic class PendingConnectionEventArgs : RoutedEventArgs  \n```  \n  \n### Constructors  \n  \n#### PendingConnectionEventArgs(Object)  \n  \nInitializes a new instance of the [PendingConnectionEventArgs](#pendingconnectioneventargs-class) class using the specified [PendingConnectionEventArgs.SourceConnector](#pendingconnectioneventargs-class#sourceconnector).  \n  \n```csharp  \npublic PendingConnectionEventArgs(object sourceConnector);  \n```  \n  \n**Parameters**  \n  \n`sourceConnector` [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object): The [FrameworkElement.DataContext](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.FrameworkElement.datacontext) of a related [Connector](#connector-class).  \n  \n### Properties  \n  \n#### Anchor  \n  \nGets or sets the [Connector.Anchor](#connector-class#anchor) of the [Connector](#connector-class) that raised this event.  \n  \n```csharp  \npublic Point Anchor { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n#### Canceled  \n  \nGets or sets a value that indicates whether this [PendingConnection](#pendingconnection-class) was cancelled.  \n  \n```csharp  \npublic bool Canceled { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean)  \n  \n#### OffsetX  \n  \nGets or sets the distance from the [PendingConnectionEventArgs.SourceConnector](#pendingconnectioneventargs-class#sourceconnector) in the X axis.  \n  \n```csharp  \npublic double OffsetX { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Double](https://docs.microsoft.com/en-us/dotnet/api/System.Double)  \n  \n#### OffsetY  \n  \nGets or sets the distance from the [PendingConnectionEventArgs.SourceConnector](#pendingconnectioneventargs-class#sourceconnector) in the Y axis.  \n  \n```csharp  \npublic double OffsetY { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Double](https://docs.microsoft.com/en-us/dotnet/api/System.Double)  \n  \n#### SourceConnector  \n  \nGets the [FrameworkElement.DataContext](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.FrameworkElement.datacontext) of the [Connector](#connector-class) that started this [PendingConnection](#pendingconnection-class).  \n  \n```csharp  \npublic object SourceConnector { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object)  \n  \n#### TargetConnector  \n  \nGets or sets the [FrameworkElement.DataContext](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.FrameworkElement.datacontext) of the target [Connector](#connector-class) when the [PendingConnection](#pendingconnection-class) is completed.  \n  \n```csharp  \npublic object TargetConnector { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object)  \n  \n### Methods  \n  \n#### InvokeEventHandler(Delegate, Object)  \n  \n```csharp  \nprotected override void InvokeEventHandler(Delegate genericHandler, object genericTarget);  \n```  \n  \n**Parameters**  \n  \n`genericHandler` [Delegate](https://docs.microsoft.com/en-us/dotnet/api/System.Delegate)  \n  \n`genericTarget` [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object)  \n  \n## PendingConnectionEventHandler Delegate  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**Inheritance:** [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object) → [Delegate](https://docs.microsoft.com/en-us/dotnet/api/System.Delegate) → [MulticastDelegate](https://docs.microsoft.com/en-us/dotnet/api/System.MulticastDelegate) → [PendingConnectionEventHandler](#pendingconnectioneventhandler-delegate)  \n  \n**References:** [Connector](#connector-class), [PendingConnection](#pendingconnection-class), [PendingConnectionEventArgs](#pendingconnectioneventargs-class)  \n  \nRepresents the method that will handle [PendingConnection](#pendingconnection-class) related routed events.  \n  \n```csharp  \npublic delegate void PendingConnectionEventHandler(object sender, PendingConnectionEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`sender` [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object): The object where the event handler is attached.  \n  \n`e` [PendingConnectionEventArgs](#pendingconnectioneventargs-class): The event data.  \n  \n## PreviewLocationChanged Delegate  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**Inheritance:** [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object) → [Delegate](https://docs.microsoft.com/en-us/dotnet/api/System.Delegate) → [MulticastDelegate](https://docs.microsoft.com/en-us/dotnet/api/System.MulticastDelegate) → [PreviewLocationChanged](#previewlocationchanged-delegate)  \n  \n**References:** [ItemContainer](#itemcontainer-class)  \n  \nDelegate used to notify when an [ItemContainer](#itemcontainer-class) is previewing a new location.  \n  \n```csharp  \npublic delegate void PreviewLocationChanged(Point newLocation);  \n```  \n  \n**Parameters**  \n  \n`newLocation` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point): The new location.  \n  \n## ResizeEventArgs Class  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**Inheritance:** [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object) → [EventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.EventArgs) → [RoutedEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.RoutedEventArgs) → [ResizeEventArgs](#resizeeventargs-class)  \n  \n**References:** [ResizeEventHandler](#resizeeventhandler-delegate)  \n  \nProvides data for resize related routed events.  \n  \n```csharp  \npublic class ResizeEventArgs : RoutedEventArgs  \n```  \n  \n### Constructors  \n  \n#### ResizeEventArgs(Size, Size)  \n  \nInitializes a new instance of the [ResizeEventArgs](#resizeeventargs-class) class with the previous and the new [Size](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Size).  \n  \n```csharp  \npublic ResizeEventArgs(Size previousSize, Size newSize);  \n```  \n  \n**Parameters**  \n  \n`previousSize` [Size](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Size): The previous size associated with this event.  \n  \n`newSize` [Size](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Size): The new size associated with this event.  \n  \n### Properties  \n  \n#### NewSize  \n  \nGets the new size of the object.  \n  \n```csharp  \npublic Size NewSize { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Size](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Size)  \n  \n#### PreviousSize  \n  \nGets the previous size of the object.  \n  \n```csharp  \npublic Size PreviousSize { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Size](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Size)  \n  \n### Methods  \n  \n#### InvokeEventHandler(Delegate, Object)  \n  \n```csharp  \nprotected override void InvokeEventHandler(Delegate genericHandler, object genericTarget);  \n```  \n  \n**Parameters**  \n  \n`genericHandler` [Delegate](https://docs.microsoft.com/en-us/dotnet/api/System.Delegate)  \n  \n`genericTarget` [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object)  \n  \n## ResizeEventHandler Delegate  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**Inheritance:** [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object) → [Delegate](https://docs.microsoft.com/en-us/dotnet/api/System.Delegate) → [MulticastDelegate](https://docs.microsoft.com/en-us/dotnet/api/System.MulticastDelegate) → [ResizeEventHandler](#resizeeventhandler-delegate)  \n  \n**References:** [GroupingNode](#groupingnode-class), [ResizeEventArgs](#resizeeventargs-class)  \n  \nRepresents the method that will handle resize related routed events.  \n  \n```csharp  \npublic delegate void ResizeEventHandler(object sender, ResizeEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`sender` [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object): The sender of this event.  \n  \n`e` [ResizeEventArgs](#resizeeventargs-class): The event data.  \n  \n## SelectionGestures Class  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**Inheritance:** [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object) → [SelectionGestures](#selectiongestures-class)  \n  \n**References:** [ConnectionGestures](#connectiongestures-class), [InputGestureRef](#inputgestureref-class), [ItemContainerGestures](#itemcontainergestures-class), [NodifyEditorGestures](#nodifyeditorgestures-class)  \n  \n```csharp  \npublic class SelectionGestures  \n```  \n  \n### Constructors  \n  \n#### SelectionGestures(MouseAction)  \n  \n```csharp  \npublic SelectionGestures(MouseAction mouseAction);  \n```  \n  \n**Parameters**  \n  \n`mouseAction` [MouseAction](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.MouseAction)  \n  \n#### SelectionGestures()  \n  \n```csharp  \npublic SelectionGestures();  \n```  \n  \n### Fields  \n  \n#### None  \n  \n```csharp  \npublic static SelectionGestures None;  \n```  \n  \n**Field Value**  \n  \n[SelectionGestures](#selectiongestures-class)  \n  \n### Properties  \n  \n#### Append  \n  \n```csharp  \npublic InputGestureRef Append { get; set; }  \n```  \n  \n**Property Value**  \n  \n[InputGestureRef](#inputgestureref-class)  \n  \n#### Cancel  \n  \n```csharp  \npublic InputGestureRef Cancel { get; set; }  \n```  \n  \n**Property Value**  \n  \n[InputGestureRef](#inputgestureref-class)  \n  \n#### Invert  \n  \n```csharp  \npublic InputGestureRef Invert { get; set; }  \n```  \n  \n**Property Value**  \n  \n[InputGestureRef](#inputgestureref-class)  \n  \n#### Remove  \n  \n```csharp  \npublic InputGestureRef Remove { get; set; }  \n```  \n  \n**Property Value**  \n  \n[InputGestureRef](#inputgestureref-class)  \n  \n#### Replace  \n  \n```csharp  \npublic InputGestureRef Replace { get; set; }  \n```  \n  \n**Property Value**  \n  \n[InputGestureRef](#inputgestureref-class)  \n  \n#### Select  \n  \n```csharp  \npublic InputGestureRef Select { get; set; }  \n```  \n  \n**Property Value**  \n  \n[InputGestureRef](#inputgestureref-class)  \n  \n### Methods  \n  \n#### Apply(SelectionGestures)  \n  \n```csharp  \npublic void Apply(SelectionGestures gestures);  \n```  \n  \n**Parameters**  \n  \n`gestures` [SelectionGestures](#selectiongestures-class)  \n  \n## SelectionHelper Class  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**Inheritance:** [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object) → [SelectionHelper](#selectionhelper-class)  \n  \n**References:** [EditorSelectingState](#editorselectingstate-class), [ItemContainer](#itemcontainer-class), [NodifyEditor](#nodifyeditor-class), [SelectionType](#selectiontype-enum)  \n  \nHelps with selecting [ItemContainer](#itemcontainer-class)s and updating the [NodifyEditor.SelectedArea](#nodifyeditor-class#selectedarea) and [NodifyEditor.IsSelecting](#nodifyeditor-class#isselecting) properties.  \n  \n```csharp  \npublic sealed class SelectionHelper  \n```  \n  \n### Constructors  \n  \n#### SelectionHelper(NodifyEditor)  \n  \nConstructs a new instance of a [SelectionHelper](#selectionhelper-class).  \n  \n```csharp  \npublic SelectionHelper(NodifyEditor host);  \n```  \n  \n**Parameters**  \n  \n`host` [NodifyEditor](#nodifyeditor-class): The editor to select items from.  \n  \n### Methods  \n  \n#### Abort()  \n  \nAborts the current selection.  \n  \n```csharp  \npublic void Abort();  \n```  \n  \n#### End()  \n  \nCommits the current selection to the editor.  \n  \n```csharp  \npublic void End();  \n```  \n  \n#### Start(Point, SelectionType)  \n  \nAttempts to start a new selection.  \n  \n```csharp  \npublic void Start(Point location, SelectionType selectionType);  \n```  \n  \n**Parameters**  \n  \n`location` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point): The location inside the graph.  \n  \n`selectionType` [SelectionType](#selectiontype-enum): The type of selection.  \n  \n#### Update(Point)  \n  \nUpdate the end location for the selection.  \n  \n```csharp  \npublic void Update(Point endLocation);  \n```  \n  \n**Parameters**  \n  \n`endLocation` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point): An absolute location.  \n  \n## SelectionType Enum  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**References:** [EditorSelectingState](#editorselectingstate-class), [SelectionHelper](#selectionhelper-class)  \n  \n```csharp  \npublic enum SelectionType  \n```  \n  \n### Fields  \n  \n#### Append  \n  \n```csharp  \nAppend = 2;  \n```  \n  \n#### Invert  \n  \n```csharp  \nInvert = 3;  \n```  \n  \n#### Remove  \n  \n```csharp  \nRemove = 1;  \n```  \n  \n#### Replace  \n  \n```csharp  \nReplace = 0;  \n```  \n  \n## StateNode Class  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**Inheritance:** [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object) → [DispatcherObject](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Threading.DispatcherObject) → [DependencyObject](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.DependencyObject) → [Visual](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Media.Visual) → [UIElement](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.UIElement) → [FrameworkElement](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.FrameworkElement) → [Control](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Controls.Control) → [Connector](#connector-class) → [StateNode](#statenode-class)  \n  \n**References:** [PendingConnection](#pendingconnection-class)  \n  \nRepresents a control that acts as a [Connector](#connector-class).  \n  \n```csharp  \npublic class StateNode : Connector  \n```  \n  \n### Constructors  \n  \n#### StateNode()  \n  \n```csharp  \npublic StateNode();  \n```  \n  \n### Fields  \n  \n#### ElementContent  \n  \n```csharp  \nprotected const string ElementContent = \"PART_Content\";  \n```  \n  \n**Field Value**  \n  \n[String](https://docs.microsoft.com/en-us/dotnet/api/System.String)  \n  \n### Properties  \n  \n#### Content  \n  \nGets or sets the data for the control's content.  \n  \n```csharp  \npublic object Content { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object)  \n  \n#### ContentControl  \n  \nGets the [StateNode.ContentControl](#statenode-class#contentcontrol) control of this [StateNode](#statenode-class).  \n  \n```csharp  \nprotected UIElement ContentControl { get; set; }  \n```  \n  \n**Property Value**  \n  \n[UIElement](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.UIElement)  \n  \n#### ContentTemplate  \n  \nGets or sets the template used to display the content of the control's header.  \n  \n```csharp  \npublic DataTemplate ContentTemplate { get; set; }  \n```  \n  \n**Property Value**  \n  \n[DataTemplate](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.DataTemplate)  \n  \n#### CornerRadius  \n  \nGets or sets a value that represents the degree to which the corners of the [StateNode](#statenode-class) are rounded.  \n  \n```csharp  \npublic CornerRadius CornerRadius { get; set; }  \n```  \n  \n**Property Value**  \n  \n[CornerRadius](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.CornerRadius)  \n  \n#### HighlightBrush  \n  \nGets or sets the brush used when the [PendingConnection.IsOverElementProperty](#pendingconnection-class#isoverelementproperty) attached property is true for this [StateNode](#statenode-class).  \n  \n```csharp  \npublic Brush HighlightBrush { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Brush](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Media.Brush)  \n  \n### Methods  \n  \n#### OnApplyTemplate()  \n  \n```csharp  \npublic override void OnApplyTemplate();  \n```  \n  \n#### OnMouseDown(MouseButtonEventArgs)  \n  \n```csharp  \nprotected override void OnMouseDown(MouseButtonEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [MouseButtonEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.MouseButtonEventArgs)  \n  \n#### OnMouseUp(MouseButtonEventArgs)  \n  \n```csharp  \nprotected override void OnMouseUp(MouseButtonEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [MouseButtonEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.MouseButtonEventArgs)  \n  \n## StepConnection Class  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**Inheritance:** [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object) → [DispatcherObject](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Threading.DispatcherObject) → [DependencyObject](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.DependencyObject) → [Visual](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Media.Visual) → [UIElement](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.UIElement) → [FrameworkElement](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.FrameworkElement) → [Shape](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Shapes.Shape) → [BaseConnection](#baseconnection-class) → [LineConnection](#lineconnection-class) → [StepConnection](#stepconnection-class)  \n  \n**References:** [ConnectorPosition](#connectorposition-enum)  \n  \n```csharp  \npublic class StepConnection : LineConnection  \n```  \n  \n### Constructors  \n  \n#### StepConnection()  \n  \n```csharp  \npublic StepConnection();  \n```  \n  \n### Properties  \n  \n#### SourcePosition  \n  \nGets or sets the position of the source connector.  \n  \n```csharp  \npublic ConnectorPosition SourcePosition { get; set; }  \n```  \n  \n**Property Value**  \n  \n[ConnectorPosition](#connectorposition-enum)  \n  \n#### TargetPosition  \n  \nGets or sets the position of the target connector.  \n  \n```csharp  \npublic ConnectorPosition TargetPosition { get; set; }  \n```  \n  \n**Property Value**  \n  \n[ConnectorPosition](#connectorposition-enum)  \n  \n### Methods  \n  \n#### DrawDirectionalArrowsGeometry(StreamGeometryContext, Point, Point)  \n  \n```csharp  \nprotected override void DrawDirectionalArrowsGeometry(StreamGeometryContext context, Point source, Point target);  \n```  \n  \n**Parameters**  \n  \n`context` [StreamGeometryContext](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Media.StreamGeometryContext)  \n  \n`source` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n`target` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n#### DrawLineGeometry(StreamGeometryContext, Point, Point)  \n  \n```csharp  \nprotected override ValueTuple<ValueTuple<Point, Point>, ValueTuple<Point, Point>> DrawLineGeometry(StreamGeometryContext context, Point source, Point target);  \n```  \n  \n**Parameters**  \n  \n`context` [StreamGeometryContext](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Media.StreamGeometryContext)  \n  \n`source` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n`target` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n**Returns**  \n  \n[ValueTuple<ValueTuple<Point, Point>, ValueTuple<Point, Point>>](https://docs.microsoft.com/en-us/dotnet/api/System.ValueTuple)  \n  \n#### GetTextPosition(FormattedText, Point, Point)  \n  \n```csharp  \nprotected override Point GetTextPosition(FormattedText text, Point source, Point target);  \n```  \n  \n**Parameters**  \n  \n`text` [FormattedText](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Media.FormattedText)  \n  \n`source` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n`target` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n**Returns**  \n  \n[Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n## ZoomEventArgs Class  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**Inheritance:** [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object) → [EventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.EventArgs) → [RoutedEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.RoutedEventArgs) → [ZoomEventArgs](#zoomeventargs-class)  \n  \n**References:** [Minimap](#minimap-class), [ZoomEventHandler](#zoomeventhandler-delegate)  \n  \nProvides data for [Minimap.Zoom](#minimap-class#zoom) routed event.  \n  \n```csharp  \npublic class ZoomEventArgs : RoutedEventArgs  \n```  \n  \n### Constructors  \n  \n#### ZoomEventArgs(Double, Point)  \n  \nInitializes a new instance of the [ZoomEventArgs](#zoomeventargs-class) class using the specified [ZoomEventArgs.Zoom](#zoomeventargs-class#zoom) and [ZoomEventArgs.Location](#zoomeventargs-class#location).  \n  \n```csharp  \npublic ZoomEventArgs(double zoom, Point location);  \n```  \n  \n**Parameters**  \n  \n`zoom` [Double](https://docs.microsoft.com/en-us/dotnet/api/System.Double)  \n  \n`location` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n### Properties  \n  \n#### Location  \n  \nGets the location where the editor should zoom in.  \n  \n```csharp  \npublic Point Location { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n#### Zoom  \n  \nGets the zoom amount.  \n  \n```csharp  \npublic double Zoom { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Double](https://docs.microsoft.com/en-us/dotnet/api/System.Double)  \n  \n### Methods  \n  \n#### InvokeEventHandler(Delegate, Object)  \n  \n```csharp  \nprotected override void InvokeEventHandler(Delegate genericHandler, object genericTarget);  \n```  \n  \n**Parameters**  \n  \n`genericHandler` [Delegate](https://docs.microsoft.com/en-us/dotnet/api/System.Delegate)  \n  \n`genericTarget` [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object)  \n  \n## ZoomEventHandler Delegate  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**Inheritance:** [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object) → [Delegate](https://docs.microsoft.com/en-us/dotnet/api/System.Delegate) → [MulticastDelegate](https://docs.microsoft.com/en-us/dotnet/api/System.MulticastDelegate) → [ZoomEventHandler](#zoomeventhandler-delegate)  \n  \n**References:** [Minimap](#minimap-class), [ZoomEventArgs](#zoomeventargs-class)  \n  \nRepresents the method that will handle [Minimap.Zoom](#minimap-class#zoom) routed event.  \n  \n```csharp  \npublic delegate void ZoomEventHandler(object sender, ZoomEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`sender` [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object): The object where the event handler is attached.  \n  \n`e` [ZoomEventArgs](#zoomeventargs-class): The event data.  \n  \n"
  },
  {
    "path": "docs/Connections-Overview.md",
    "content": "Connections are created between two points. The `Source` and `Target` dependency properties are of type `Point` and are usually bound to a connector's `Anchor` point.\n\n## Table of contents\n\n- [Base connection](#base-connection)\n- [Line connection](#line-connection)\n- [Circuit connection](#circuit-connection)\n- [Bezier connection](#connection)\n- [Step connection](#step-connection)\n- [Pending connection](#pending-connection)\n- [Custom connection](#custom-connection)\n\n## Using connections\n\nIn the `NodifyEditor`, you can customize both the default connection and the pending connection by assigning a custom `DataTemplate` to the `ConnectionTemplate` and `PendingConnectionTemplate` properties, respectively.\n\n- `ConnectionTemplate`: defines the appearance of established connections between nodes.\n- `PendingConnectionTemplate`: specifies the visual appearance of connections that are in the process of being created (i.e., while the connection is being dragged but not yet completed).\n\nTo customize these, simply set your desired `DataTemplate` on the `NodifyEditor` instance.\n\n## Base connection\n\nThe base class for all connections provided by the library is `BaseConnection` which derives from `Shape`. There's no restriction to derive from `BaseConnection` when you create a custom connection.\n\nIt exposes two commands with their corresponding events:\n\n- `DisconnectCommand`, respectively `DisconnectEvent` - fired when the connection is clicked while holding `ALT`\n- `SplitCommand`, respectively `SplitEvent` - fired when the connection is double-clicked\n\nThe `Direction` of a connection can have two values:\n\n- `Forward`\n\n![image](https://user-images.githubusercontent.com/12727904/192101918-af9b0da6-ecc8-48f7-bf4d-8f9fdd005153.png)\n![image](https://user-images.githubusercontent.com/12727904/192101959-2cb9a837-1642-4e96-b2ef-eea5502a587f.png)\n\n- `Backward`\n\n![image](https://user-images.githubusercontent.com/12727904/192101941-a00e23db-07ae-49ac-a907-72e35ef67877.png)\n![image](https://user-images.githubusercontent.com/12727904/192101977-1afd69f1-dab0-478e-9c3d-7d601486c289.png)\n\nThe `SourceOffset` and the `TargetOffset` work together with `OffsetMode` and will keep a distance from the anchor point:\n\n![image](https://user-images.githubusercontent.com/12727904/192102096-b20887d5-b7ba-450f-9cf3-7fa4086d9637.png)\n\nConnections also have a `Spacing` which will make the connection break the angle at a certain distance from the `Source` and `Target` points:\n\n- With spacing:\n\n![image](https://user-images.githubusercontent.com/12727904/192102286-9a79da8e-5e87-4f60-9e82-979bfabcd6f3.png)\n\n- Without spacing:\n\n![image](https://user-images.githubusercontent.com/12727904/192102302-4125b44a-dfad-4d9e-9131-efb7c17cefbe.png)\n\nSettings the `ArrowSize` to \"0, 0\" will remove the arrowhead.\n\n## Line connection\n\nA straight line from `Source` to `Target`.\n\n![image](https://user-images.githubusercontent.com/12727904/192115137-d8d2145b-a769-4ee9-b4e0-8a362c94e9e7.png)\n\n## Circuit connection\n\nIt has an `Angle` dependency property to control where it breaks. Angle is in degrees.\n\n![image](https://user-images.githubusercontent.com/12727904/192115226-b0e515b4-5a21-46aa-956a-401f07b7d308.png)\n\n## Connection\n\nA bezier curve between `Source` and `Target`.\n\n![image](https://user-images.githubusercontent.com/12727904/192115259-2fe56a68-b3e4-4f5d-aa5c-5ab83e84a84d.png)\n\n## Step connection\n\nA multi-segment angled wire between `Source` and `Target`. It has two additional parameters: `SourcePosition` and `TargetPosition`.\n\n![image](https://github.com/user-attachments/assets/c63d620e-af34-460e-ad9e-b2d9adb748bf)\n\n## Pending Connection\n\nA pending connection can be created from a connector and dropped on either an `ItemContainer` (if `AllowOnlyConnectors` is false) or a `Connector`.\n\n`Content` of a pending connection can be customized using the `ContentTemplate`. If `EnablePreview` is true, the `PreviewTarget` will be updated with the connector or item container under the mouse cursor or `null` if there's no such element.\n\n![image](https://user-images.githubusercontent.com/12727904/192115698-fbe29101-884f-4cec-9c25-e318701d30b1.png)\n\nThe visibility of pending connections can be controlled using the `IsVisible` dependency property.\n\nConnection snapping to connectors can be enabled using the `EnableSnapping` dependency property.\n\nThe `Source` and the `Target` properties are data contexts of connectors and the `Target` will be updated when the pending connection is completed.\n\nThere's also a `StartedCommand` which takes the `Source` as the parameter, respectively a `CompletedCommand` which takes the `Target` as the parameter.\n\n> [!TIP]\n> Canceling a pending connection is done by releasing the right mouse button.\n\n## Custom connection\n\nIn some cases, the built-in connections may not provide all the features you need, or your application may need to display hundreds of connections simultaneously. While the built-in connections are feature-rich, they may introduce some overhead that could impact performance. In such cases, you can implement a custom connection to better suit your needs.\n\nThere are several ways to implement a custom connection:\n\n- Derive from one of the existing connection classes and override the appropriate methods.\n- Create a custom control or user control that wraps a built-in connection.\n- Implement a custom connection from scratch.\n\n**Considerations for Implementing a Custom Connection from Scratch**\n\n- The cutting line feature requires the connection type to be added to the `NodifyEditor.CuttingConnectionTypes` collection (see [Cuttline Line - Custom Connections](CuttingLine-Overview#custom-connections))\n- For selection functionality, you must set the `nodify:BaseConnection.IsSelectable` attached property to `true` on the root element.\n- To control the selection state, bind to the `nodify:BaseConnection.IsSelected` attached property on the root element.\n\nExample:\n\n```xml\n<Line X1=\"{Binding Source.Anchor.X}\"\n      X2=\"{Binding Target.Anchor.X}\"\n      Y1=\"{Binding Source.Anchor.Y}\"\n      Y2=\"{Binding Target.Anchor.Y}\"\n      StrokeThickness=\"5\"\n      nodify:BaseConnection.IsSelectable=\"True\"\n      nodify:BaseConnection.IsSelected=\"{Binding IsSelected}\">\n    <Line.Style>\n        <Style TargetType=\"Line\">\n            <Setter Property=\"Stroke\"\n                    Value=\"Red\" />\n            <Style.Triggers>\n                <Trigger Property=\"nodify:BaseConnection.IsSelected\"\n                         Value=\"True\">\n                    <Setter Property=\"Stroke\"\n                            Value=\"Green\" />\n                </Trigger>\n                <Trigger Property=\"nodify:BaseConnection.IsSelectable\"\n                         Value=\"True\">\n                    <Setter Property=\"Cursor\"\n                            Value=\"Hand\" />\n                </Trigger>\n            </Style.Triggers>\n        </Style>\n    </Line.Style>\n</Line>\n```\n"
  },
  {
    "path": "docs/Connectors-Overview.md",
    "content": "The connector creates a pending connection by raising the `PendingConnectionStartedEvent` event. Connectors have an `Anchor` dependency property that **must** be bound in order to be able to create connections between them that are updated in real-time when the node's position changes. The `IsConnected` dependency property **must** be set to true in order to receive `Anchor` updates.\n\nALT+Click on a connector fires the `DisconnectCommand`.\n\n## NodeInput and NodeOutput\n\nNode input and node output are implementations of `Connector` with a `Header` that can be used to display text or input boxes by customizing the `HeaderTemplate`. They also expose a `ConnectorTemplate` to customize the connector itself. They are usually used in conjunction with `Node.InputConnectorTemplate` and `Node.OutputConnectorTemplate`.\n\n![image](https://user-images.githubusercontent.com/12727904/192117525-a7e1b309-70d6-4ed7-bcd7-8210dbd680ce.png)\n\n"
  },
  {
    "path": "docs/CuttingLine-Overview.md",
    "content": "## Table of contents\n\n- [Enabling preview](#enabling-preview)\n- [Custom connections](#custom-connections)\n- [Customization](#customization)\n\nThe `CuttingLine` control is a custom control used to remove intersecting connections.\n\nThe default gesture to start cutting is `SHIFT+ALT+LeftClick` and can be configured by setting the `EditorGestures.Editor.Cutting` gesture. The cutting can be canceled by pressing the `Escape` key or the right mouse button which can be configured by setting the `EditorGestures.Editor.CancelAction` gesture.\n\nRelevant commands available in `NodifyEditor`:\n\n- CuttingStartedCommand\n- CuttingCompletedCommand\n- RemoveConnectionCommand - is called for each intersecting connection when the cutting operation is completed\n\n## Enabling preview\n\nFor the connection style to change when intersecting with the cutting line, it's required to set `NodifyEditor.EnableCuttingLinePreview` to `true`.\n\n> [!WARNING]\n> Depending on the number of connections and the complexity of their geometry, this may have a great performance impact.\n\n![cutting](https://github.com/user-attachments/assets/22f705c8-3bf1-466b-8bbd-da007f30deb2)\n\n## Custom connections\n\nTo enable cutting custom connections you must add the connection type to `NodifyEditor.CuttingConnectionTypes`:\n\n```csharp\n// example adding the Line shape to the connection types that can be cut\nNodifyEditor.CuttingConnectionTypes.Add(typeof(System.Windows.Shapes.Line));\n```\n\nThe style of the intersecting custom connection can be customized as follows:\n\n```xml\n<Style TargetType=\"Line\">\n    <Style.Triggers>\n        <Trigger Property=\"nodify:CuttingLine.IsOverElement\" Value=\"True\">\n            <Setter Property=\"Opacity\"\n                    Value=\"0.4\" />\n        </Trigger>\n    </Style.Triggers>\n</Style>\n```\n\n## Customization\n\nThe `CuttingLineStyle` is used to customize the cutting line:\n\n```xml\n<Style x:Key=\"CuttingLineStyle\"\n       TargetType=\"{x:Type nodify:CuttingLine}\"\n       BasedOn=\"{StaticResource {x:Type nodify:CuttingLine}}\">\n    <Setter Property=\"StrokeDashArray\"\n            Value=\"1 1\" />\n    <Setter Property=\"StrokeThickness\"\n            Value=\"2\" />\n</Style>\n\n<nodify:NodifyEditor CuttingLineStyle=\"{StaticResource CuttingLineStyle}\" ... />\n```\n"
  },
  {
    "path": "docs/Documentation.md",
    "content": "# Documentation\n\nWhile the documentation is a work in progress, I encourage you to check out the [example application](https://github.com/miroiu/nodify/tree/master/Examples/Nodify.Playground).\n\n![Example Application](https://i.imgur.com/gpcllLy.png)\n"
  },
  {
    "path": "docs/Editor-Overview.md",
    "content": "## Table of contents\n\n- [Moving the viewport](#panning)\n- [Zooming](#zooming)\n- [Scrolling](#scrolling)\n- [Selecting items](#selecting)\n  - [Selection API](#selection-api)\n- [Snapping to grid](#snapping)\n- [Pushing items](#pushing-items)\n- [Commands](#commands)\n- [Editor API](#editor-api)\n\n## Panning\n\nPanning is done by holding right-click and moving the mouse and can be disabled by setting the `DisablePanning` dependency property to `true`.\n\n> Note: It can be programmatically changed by setting the `ViewportLocation` dependency property.\n\nWhile panning, the `IsPanning` dependency property will be set to `true` and the `ViewportSize`, `ViewportLocation` and `ViewportTransform` dependency properties will be updated.\n\nAutomatic panning is also enabled by default and can be disabled by setting the `DisableAutoPanning` dependency property to `true`. The behavior is to pan the viewport when selecting or dragging a selection or a pending connection near the edges of the viewport.\n\nThe auto panning speed can be changed using the `AutoPanSpeed` dependency property and the distance from the edge to trigger the panning can be changed using the `AutoPanEdgeDistance` dependency property.\n\nPanning can also be used in combination with selecting and zooming while auto panning can be used with both selecting and zooming and also with dragging a selection or a pending connection.\n\nDefault values:\n\n- `DisablePanning`: false\n- `DisableAutoPanning`: false\n- `AutoPanSpeed`: 10 pixels per tick\n- `AutoPanEdgeDistance`: 15 pixels\n- `AutoPanningTickRate`: 1 millisecond\n\n## Zooming\n\nZooming is done by using the mouse wheel or by pressing `CTRL +` to zoom in or `CTRL -` to zoom out and can be disabled by setting the `DisableZooming` dependency property to `true`.\n\n> Note: It can be programmatically changed by setting the `ViewportZoom` dependency property to a value between `MinViewportZoom` and `MaxViewportZoom`.\n\nWhile zooming, the `ViewportLocation`, `ViewportSize` and `ViewportTransform` dependency properties will be updated.\n\nZooming can also be used in combination with panning, dragging a selection, or a pending connection.\n\nDefault values:\n\n- `ViewportZoom`: 1\n- `MinViewportZoom`: 0.1\n- `MaxViewportZoom`: 2\n\n## Scrolling\n\nWrap the `NodifyEditor` inside a `ScrollViewer` to enable scrolling:\n\n```xml\n<ScrollViewer CanContentScroll=\"True\">\n    <nodify:NodifyEditor ... />\n<ScrollViewer>\n```\n\nThe scroll sensitivity can be adjusted by setting the `ScrollIncrement` static field.\n\nDefault values:\n\n- `ScrollIncrement`: `Mouse.MouseWheelDeltaForOneLine` / 2\n\n## Selecting\n\nSelecting items is done by holding the left mouse button and moving the mouse. When a selection operation is in progress, the `IsSelecting` dependency property is set to `true` and the `SelectedArea` dependency property is updated with each move.\n\n> Note: Selected items can also be set programmatically by binding a collection to the `SelectedItems` dependency property.\n\nIf real-time selection is enabled (`EnableRealtimeSelection`: true), the items will be selected and deselected while you resize the selection rectangle. Otherwise, the items contained in the `SelectedArea` will only be selected after the selection operation is finished.\n\nWhen an `ItemContainer` is selected, its `IsSelected` dependency property is set to `true`.\n\nDifferent behavior is used depending on the `ModifierKeys` held when starting a selection:\n\n- `Replace` - no modifier key (default behavior, clears the selected items and starts a new selection)\n- `Append` - shift key (adds selection to the currently selected items)\n- `Remove` - alt key (removes selection from the currently selected items)\n- `Invert` - control key (removes selected items and adds unselected items)\n\nSelecting items can also be used in combination with panning and zooming.\n\nDefault values:\n\n- `EnableRealtimeSelection`: true\n\n### Selection API\n\nThe following methods can be called on a NodifyEditor instance.\n\n- SelectAll\n- UnselectAll\n- SelectArea\n- InvertSelection\n- UnselectArea\n- SelectAllConnections\n- UnselectAllConnections\n\n## Snapping\n\nWhen a selection is moved the `GridCellSize` dependency property will be used to determine where to snap the selected items.\nThe snapping is relative to the position of the selected item and not the virtual grid.\n\nIf the selected items are not snapped to the grid when they are initially created or if the `GridCellSize` is changed at runtime, the final position will be corrected after moving the selection if the `EnableSnappingCorrection` dependency property is `true`.\n\nDefault values:\n\n- `GridCellSize`: 1\n- `EnableSnappingCorrection`: true\n\n## Pushing Items\n\nThe Pushing Items feature allows users to interactively move items within the editor by performing a drag gesture.\n\nTo initiate the push operation, hold `CTRL+SHIFT` and click with the **Left Mouse Button**. During the push items operation, the `IsPushingItems` dependency property is set to `true`, the `PushOrientation` dependency property is initialized with the direction the user is dragging, and the `PushedArea` dependency property updates in real time to reflect the interaction state.\n\nTo cancel the push operation, press the `Escape` key or release `CTRL+SHIFT` and click the **Right Mouse Button**. You can configure the default keybindings by modifying the `EditorGestures.Editor.PushItems` gesture. To prevent cancellation, set `NodifyEditor.AllowPushItemsCancellation` to `false`.\n\nThe visual appearance of the push area can be customized by setting the `PushedAreaStyle` property.\n\n## Commands\n\nThe following `RoutedUICommand`s are found in the `EditorCommands` class:\n\n- `ZoomIn` - `CTRL +` (zoom in relative to the viewport's center)\n- `ZoomOut` - `CTRL -` (zoom out relative to the viewport's center)\n- `SelectAll` - `CTRL A` (select all items)\n- `BringIntoView` - Moves the viewport to the specified location, defaults to [0,0]. (`CommandParameter` is the location of type `Point` or `string`)\n- `Align` - Aligns the selected items using the specified alignment method, defaults to Top. (`CommandParameter` is of type `Alignment` or `string`. Possible alignment: Top, Left, Bottom, Right, Middle, Center)\n- `FitToScreen` - Scales and moves the `Viewport` to display as many items as possible\n\n## Editor API\n\nYou can programmatically call the corresponding methods of these commands on an instance of a `NodifyEditor`.\n\n- FitToScreen\n- BringIntoView\n- ZoomAtPosition\n- ZoomIn\n- ZoomOut\n"
  },
  {
    "path": "docs/FAQ.md",
    "content": "\n#### Q: Why is the connection not following the connectors when I move a node?\n\nA1: You didn't bind the `Anchor` of the connector, or the `Source` and `Target` properties of the connection.\n\nA2: The `Anchor` of the connectors updates only if the `IsConnected` property is set to true.\n\n***\n\n#### Q: Can I change the mouse/key bindings?\n\nA: Yes! You can configure the [editor gestures](https://github.com/miroiu/nodify/blob/master/Nodify/EditorGestures.cs) to your liking.\n\n***\n\n#### Q: Would there be an Avalonia port?\n\nA: There is a fantastic Avalonia port available! You can check it out [here](https://github.com/BAndysc/nodify-avalonia). Huge thanks to [BAndysc](https://github.com/BAndysc) who made this possible.\n\n***\n\n#### Q: Is there a non-MVVM approach to adding nodes and connections?\n\nA: No.\n\n***\n\n#### Q: How can I set the selected nodes to be always on top?\n\nA: https://github.com/miroiu/nodify/issues/57\n\n***\n\n#### Q: How can I change the size of a node?\n\nA: https://github.com/miroiu/nodify/issues/55\n\n***\n\n#### Q: How can I limit the X and Y panning?\n\nA: https://github.com/miroiu/nodify/issues/53\n\n***\n\n#### Q: The viewport is lagging. How can I fix it?\n\nA: https://github.com/miroiu/nodify/issues/60\n\n***\n\n#### Q: How can I drag nodes from a toolbox into the editor?\n\nA: https://github.com/miroiu/nodify/issues/81\n\n***\n\n#### Q: How can I create a custom connection?\n\nA: https://github.com/miroiu/nodify/issues/121\n"
  },
  {
    "path": "docs/Getting-Started.md",
    "content": "It is _important_ to understand how components are named and what's their role in the visual tree of the editor to understand the code and the documentation.\n\n## Hierarchy and terminology\n\nThe root component is an [editor](Editor-Overview) which holds [nodes](Nodes-Overview) and [connections](Connections-Overview) together with a few additional UI elements such as a [selection rectangle](Editor-Overview#selecting) and a [pending connection](Connections-Overview#pending-connection) in order to make the editor interactive.\n\nNodes are containers for [connectors](Connectors-Overview) or the node itself can be a connector (e.g. [State Node](Nodes-Overview#4-the-statenode-control)).\n\nConnectors can create pending connections that can become real connections when completed.\n\n_A picture is worth a thousand words_\n\n![nodes-hierarchy](https://user-images.githubusercontent.com/12727904/192028123-e2847f29-6517-4731-8672-f5d8356dead0.png)\n\n## Content Layers\n\nYou may wonder how a node can be a connector itself and still behave like a normal node. The editor contains three big layers which help solve this problem:\n\n1. The items layer (`NodifyEditor.ItemsSource`) - here, each control is wrapped inside an [Item Container](ItemContainer-Overview) making it selectable, draggable, etc and it is possible to have any control rendered (e.g a connector, a text block).\n2. The connections layer (`NodifyEditor.Connections`) - this is where all the [connections](Connections-Overview) coexist and are rendered behind the items layer by default\n3. The decorators layer (`NodifyEditor.Decorators`) - here, each control is given a location inside the graph\n\nHaving those layers separated enables the possibility of asynchronously loading each one of them.\n\n## Using an existing theme\n\nMerge one of the following themes into your resource dictionary in `App.xaml`:\n\n- Dark theme (default theme if not specified):\n\n```xml\n<ResourceDictionary Source=\"pack://application:,,,/Nodify;component/Themes/Dark.xaml\" />\n```\n\n- Light theme:\n\n```xml\n<ResourceDictionary Source=\"pack://application:,,,/Nodify;component/Themes/Light.xaml\" />\n```\n\n- Nodify theme:\n\n```xml\n<ResourceDictionary Source=\"pack://application:,,,/Nodify;component/Themes/Nodify.xaml\" />\n```\n\n## A minimal example\n\nImport the `nodify` namespace: `xmlns:nodify=\"https://miroiu.github.io/nodify\"` or `xmlns:nodify=\"clr-namespace:Nodify;assembly=Nodify\"` in your file and create an instance of the editor `<nodify:NodifyEditor />`. If you start the application, you will see an empty space where you can create a selection rectangle.\n\n> Tip: Drag the selection rectangle near the edge of the editor area and the screen will automatically move in that direction.\n\n### Adding nodes\n\nNow we're going to display a few nodes. Let's create the viewmodels and bind them to the view.\n\n```csharp\npublic class NodeViewModel\n{\n    public string Title { get; set; }\n}\n\npublic class EditorViewModel\n{\n    public ObservableCollection<NodeViewModel> Nodes { get; } = new ObservableCollection<NodeViewModel>();\n\n    public EditorViewModel()\n    {\n        Nodes.Add(new NodeViewModel { Title = \"Welcome\" });\n    }\n}\n```\n\nThe view model can be of any shape, but the view for the node is generated by the `ItemTemplate`. (The same result would be achieved by having the DataTemplate inside NodifyEditor.Resources)\n\n```xml\n<nodify:NodifyEditor ItemsSource=\"{Binding Nodes}\">\n    <nodify:NodifyEditor.DataContext>\n        <local:EditorViewModel />\n    </nodify:NodifyEditor.DataContext>\n\n    <nodify:NodifyEditor.ItemTemplate>\n        <DataTemplate DataType=\"{x:Type local:NodeViewModel}\">\n            <nodify:Node Header=\"{Binding Title}\" />\n        </DataTemplate>\n    </nodify:NodifyEditor.ItemTemplate>\n\n</nodify:NodifyEditor>\n```\n\nNotice how we bind the `Header` of the `Node` to display the `Title`. For more node types and customization please check out the [Nodes overview](Nodes-Overview).\n\n### Connecting nodes\n\nAlright, now let's add more nodes and connect them together. First, we need a representation of a connector and some collections on the node to store our connectors.\n\n```csharp\npublic class ConnectorViewModel\n{\n    public string Title { get; set; }\n}\n\npublic class NodeViewModel\n{\n    public string Title { get; set; }\n\n    public ObservableCollection<ConnectorViewModel> Input { get; set; } = new ObservableCollection<ConnectorViewModel>();\n    public ObservableCollection<ConnectorViewModel> Output { get; set; } = new ObservableCollection<ConnectorViewModel>();\n}\n\npublic class EditorViewModel\n{\n    public ObservableCollection<NodeViewModel> Nodes { get; } = new ObservableCollection<NodeViewModel>();\n\n    public EditorViewModel()\n    {\n        Nodes.Add(new NodeViewModel\n        {\n            Title = \"Welcome\",\n            Input = new ObservableCollection<ConnectorViewModel>\n            {\n                new ConnectorViewModel\n                {\n                    Title = \"In\"\n                }\n            },\n            Output = new ObservableCollection<ConnectorViewModel>\n            {\n                new ConnectorViewModel\n                {\n                    Title = \"Out\"\n                }\n            }\n        });\n    }\n}\n```\n\nAnd bind them to the view. (We used the built-in `NodeInput` and `NodeOutput` for the view, but there are [other connectors](Connectors-Overview) too. Or you can create your own, depending on your needs.)\n\n```xml\n<nodify:Node Header=\"{Binding Title}\"\n             Input=\"{Binding Input}\"\n             Output=\"{Binding Output}\">\n  <nodify:Node.InputConnectorTemplate>\n      <DataTemplate DataType=\"{x:Type local:ConnectorViewModel}\">\n          <nodify:NodeInput Header=\"{Binding Title}\" />\n      </DataTemplate>\n  </nodify:Node.InputConnectorTemplate>\n\n  <nodify:Node.OutputConnectorTemplate>\n      <DataTemplate DataType=\"{x:Type local:ConnectorViewModel}\">\n          <nodify:NodeOutput Header=\"{Binding Title}\" />\n      </DataTemplate>\n  </nodify:Node.OutputConnectorTemplate>\n</nodify:Node>\n```\n\nThe `Node` control supports `Input` and `Output` connectors and the way you customize these is by overwriting the default template for the `InputConnectorTemplate`, respectively the `OutputConnectorTemplate`.\n\nClicking and dragging a wire from the `Input` or `Output` connector will create a [pending connection](Connections-Overview#pending-connection) that we can transform into a real connection.\n\n**The most complicated part of Nodify is how you bind the connections to their connectors.** Let's create the ViewModel for the connection and add a list of connections in the `EditorViewModel`.\n\n```csharp\npublic class ConnectionViewModel\n{\n    public ConnectorViewModel Source { get; set; }\n    public ConnectorViewModel Target { get; set; }\n}\n\npublic class EditorViewModel\n{\n    public ObservableCollection<NodeViewModel> Nodes { get; } = new ObservableCollection<NodeViewModel>();\n    public ObservableCollection<ConnectionViewModel> Connections { get; } = new ObservableCollection<ConnectionViewModel>();\n\n    public EditorViewModel()\n    {\n        var welcome = new NodeViewModel\n        {\n            Title = \"Welcome\",\n            Input = new ObservableCollection<ConnectorViewModel>\n            {\n                new ConnectorViewModel\n                {\n                    Title = \"In\"\n                }\n            },\n            Output = new ObservableCollection<ConnectorViewModel>\n            {\n                new ConnectorViewModel\n                {\n                    Title = \"Out\"\n                }\n            }\n        };\n\n        var nodify = new NodeViewModel\n        {\n            Title = \"To Nodify\",\n            Input = new ObservableCollection<ConnectorViewModel>\n            {\n                new ConnectorViewModel\n                {\n                    Title = \"In\"\n                }\n            }\n        };\n\n        Nodes.Add(welcome);\n        Nodes.Add(nodify);\n\n        Connections.Add(new ConnectionViewModel\n        {\n            Source = welcome.Output[0],\n            Target = nodify.Input[0]\n        });\n    }\n}\n```\n\nThen update the `ConnectorViewModel` to have an `Anchor` point that the connection can attach to. (This needs to be reactive, so we're gonna implement INotifyPropertyChanged in the view model).\n\n> Note: The `Point` type must be from System.Windows.\n\n```csharp\npublic class ConnectorViewModel : INotifyPropertyChanged\n{\n    private Point _anchor;\n    public Point Anchor\n    {\n        set\n        {\n            _anchor = value;\n            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Anchor)));\n        }\n        get => _anchor;\n    }\n\n    public string Title { get; set; }\n\n    public event PropertyChangedEventHandler PropertyChanged;\n}\n```\n\nBind the `Anchor` to the connector's view as `Mode=OneWayToSource`. And also set the `IsConnected` to `True` to receive `Anchor` updates.\n\n```xml\n<nodify:Node.InputConnectorTemplate>\n    <DataTemplate DataType=\"{x:Type local:ConnectorViewModel}\">\n        <nodify:NodeInput Header=\"{Binding Title}\"\n                          IsConnected=\"True\"\n                          Anchor=\"{Binding Anchor, Mode=OneWayToSource}\" />\n    </DataTemplate>\n</nodify:Node.InputConnectorTemplate>\n\n<nodify:Node.OutputConnectorTemplate>\n    <DataTemplate DataType=\"{x:Type local:ConnectorViewModel}\">\n        <nodify:NodeOutput Header=\"{Binding Title}\"\n                           IsConnected=\"True\"\n                           Anchor=\"{Binding Anchor, Mode=OneWayToSource}\"  />\n    </DataTemplate>\n</nodify:Node.OutputConnectorTemplate>\n```\n\nAnd bind the connections to the view and let them use our `ConnectorViewModel`'s `Anchor` in the `ConnectionTemplate`. For more customization, please refer to [connections overview](Connections-Overview).\n\n```xml\n<nodify:NodifyEditor ItemsSource=\"{Binding Nodes}\"\n                     Connections=\"{Binding Connections}\">\n    ...\n    <nodify:NodifyEditor.ConnectionTemplate>\n        <DataTemplate DataType=\"{x:Type local:ConnectionViewModel}\">\n            <nodify:LineConnection Source=\"{Binding Source.Anchor}\"\n                                   Target=\"{Binding Target.Anchor}\" />\n        </DataTemplate>\n    </nodify:NodifyEditor.ConnectionTemplate>\n    ...\n```\n\nIf you start the application now, you'll see that there is a connection and if you drag the nodes around, it will follow them.\n\nNow let's add the `IsConnected` property to the `ConnectorViewModel` so we can set it whenever it is truly connected or not. And update the `ConnectionViewModel` to connect them automatically on construction.\n\n```csharp\npublic class ConnectorViewModel : INotifyPropertyChanged\n{\n    private Point _anchor;\n    public Point Anchor\n    {\n        set\n        {\n            _anchor = value;\n            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Anchor)));\n        }\n        get => _anchor;\n    }\n\n    private bool _isConnected;\n    public bool IsConnected\n    {\n        set\n        {\n            _isConnected = value;\n            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IsConnected)));\n        }\n        get => _isConnected;\n    }\n\n    public string Title { get; set; }\n\n    public event PropertyChangedEventHandler PropertyChanged;\n}\n\npublic class ConnectionViewModel\n{\n    public ConnectionViewModel(ConnectorViewModel source, ConnectorViewModel target)\n    {\n        Source = source;\n        Target = target;\n\n        Source.IsConnected = true;\n        Target.IsConnected = true;\n    }\n\n    public ConnectorViewModel Source { get; }\n    public ConnectorViewModel Target { get; }\n}\n```\n\nAnd don't forget to bind it in the connector template.\n\n```xml\nIsConnected=\"{Binding IsConnected}\"\n```\n\n### Materializing pending connections\n\nThe [PendingConnection](Connections-Overview#pending-connection) starts from a `Source` and will be completed when dropped on a `Target`. The source is _always_ a connector, and the target can be a [Connector](Connectors-Overview), an [ItemContainer](ItemContainer-Overview) or `null` otherwise. We will only care about other connectors for now. When the connection starts, the `StartedCommand` is executed which receives the `Source` as the parameter. When the connection completes, the `CompletedCommand` is executed which receives the `Target` as the parameter.\n\nTo create the commands we need an implementation of `ICommand`. I will use the following generic implementation:\n\n```csharp\npublic class DelegateCommand<T> : ICommand\n{\n    private readonly Action<T> _action;\n    private readonly Func<T, bool>? _condition;\n\n    public event EventHandler? CanExecuteChanged;\n\n    public DelegateCommand(Action<T> action, Func<T, bool>? executeCondition = default)\n    {\n        _action = action ?? throw new ArgumentNullException(nameof(action));\n        _condition = executeCondition;\n    }\n\n    public bool CanExecute(object? parameter)\n    {\n        if (parameter is T value)\n        {\n            return _condition?.Invoke(value) ?? true;\n        }\n\n        return _condition?.Invoke(default!) ?? true;\n    }\n\n    public void Execute(object? parameter)\n    {\n        if (parameter is T value)\n        {\n            _action(value);\n        }\n        else\n        {\n            _action(default!);\n        }\n    }\n\n    public void RaiseCanExecuteChanged()\n        => CanExecuteChanged?.Invoke(this, new EventArgs());\n}\n```\n\nLet's implement the pending connection view model and add it to the `EditorViewModel`.\n\n```csharp\npublic class PendingConnectionViewModel\n{\n    private readonly EditorViewModel _editor;\n    private ConnectorViewModel _source;\n\n    public PendingConnectionViewModel(EditorViewModel editor)\n    {\n        _editor = editor;\n        StartCommand = new DelegateCommand<ConnectorViewModel>(source => _source = source);\n        FinishCommand = new DelegateCommand<ConnectorViewModel>(target =>\n        {\n            if (target != null)\n                _editor.Connect(_source, target);\n        });\n    }\n\n    public ICommand StartCommand { get; }\n    public ICommand FinishCommand { get; }\n}\n\npublic class EditorViewModel\n{\n    public PendingConnectionViewModel PendingConnection { get; }\n\n    ...\n\n    public EditorViewModel()\n    {\n        PendingConnection = new PendingConnectionViewModel(this);\n        ...\n    }\n\n    ...\n\n    public void Connect(ConnectorViewModel source, ConnectorViewModel target)\n    {\n        Connections.Add(new ConnectionViewModel(source, target));\n    }\n}\n```\n\nAnd bind it to the view.\n\n```xml\n<nodify:NodifyEditor PendingConnection=\"{Binding PendingConnection}\">\n...\n    <nodify:NodifyEditor.PendingConnectionTemplate>\n        <DataTemplate DataType=\"{x:Type local:PendingConnectionViewModel}\">\n            <nodify:PendingConnection StartedCommand=\"{Binding StartCommand}\"\n                                      CompletedCommand=\"{Binding FinishCommand}\"\n                                      AllowOnlyConnectors=\"True\" />\n        </DataTemplate>\n    </nodify:NodifyEditor.PendingConnectionTemplate>\n...\n</nodify:NodifyEditor>\n```\n\nThat's all. You should be able to create connections between connectors now.\n\n### Removing connections\n\nTo remove connections you just have to listen for a disconnect event from the connector itself or from the editor and remove the connection that has the connector as the source or target. To keep it simple, we're gonna implement the `DisconnectConnectorCommand` for the `NodifyEditor`. Let's add it to the `EditorViewModel` first.\n\n```csharp\npublic class EditorViewModel\n{\n    public ICommand DisconnectConnectorCommand { get; }\n\n    ...\n\n    public EditorViewModel()\n    {\n        DisconnectConnectorCommand = new DelegateCommand<ConnectorViewModel>(connector =>\n        {\n            var connection = Connections.First(x => x.Source == connector || x.Target == connector);\n            connection.Source.IsConnected = false;  // This is not correct if there are multiple connections to the same connector\n            connection.Target.IsConnected = false;\n            Connections.Remove(connection);\n        });\n\n        ...\n    }\n}\n```\n\nNow we have to bind the command to the editor view.\n\n```xml\n<nodify:NodifyEditor ItemsSource=\"{Binding Nodes}\"\n                     Connections=\"{Binding Connections}\"\n                     PendingConnection=\"{Binding PendingConnection}\"\n                     DisconnectConnectorCommand=\"{Binding DisconnectConnectorCommand}\">\n  ...\n\n</nodify:NodifyEditor>\n```\n\n### Controlling node location\n\nAs you can see, the nodes are always in the top left corner of the screen. That's because they are at location (0, 0) inside the graph. Let's change that!\n\nAdd a `Location` property of type `System.Windows.Point` in the `NodeViewModel` that raises PropertyChanged events.\n\n```csharp\npublic class NodeViewModel : INotifyPropertyChanged\n{\n    private Point _location;\n    public Point Location\n    {\n        set\n        {\n            _location = value;\n            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Location)));\n        }\n        get => _location;\n    }\n\n    public event PropertyChangedEventHandler PropertyChanged;\n\n    ...\n}\n```\n\nAnd bind it to the view.\n\n```xml\n<nodify:NodifyEditor ItemsSource=\"{Binding Nodes}\"\n                     Connections=\"{Binding Connections}\"\n                     PendingConnection=\"{Binding PendingConnection}\">\n\n    <nodify:NodifyEditor.ItemContainerStyle>\n        <Style TargetType=\"{x:Type nodify:ItemContainer}\">\n            <Setter Property=\"Location\"\n                    Value=\"{Binding Location}\" />\n        </Style>\n    </nodify:NodifyEditor.ItemContainerStyle>\n\n    ...\n\n</nodify:NodifyEditor>\n```\n\n> Note: I used the `ItemContainerStyle` to bind the location of the node. Please check out the [ItemContainer overview](ItemContainer-Overview) for more info.\n\nNow you can set the location of the nodes when constructed.\n\n## Drawing a grid\n\nDrawing a simple grid is just a matter of creating a grid brush, applying the editor transform to it, and using the brush as the `Background` of the editor.\n\nBecause the grid we are drawing is made of lines and is not filled, the `Background` of the editor will have some transparency, meaning that we'll see the background color of the control below. To solve this, wrap the editor in a `Grid` and set its `Background` or set the `Background` of the `Window`.\n\nUse the `ViewportTransform` dependency property to have the grid move with the view.\n\n> Note: The example uses static resources which are provided by the selected theme in `App.xaml`.\n\n```xml\n<Window x:Class=\"MyProject.MainWindow\"\n        xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n        xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n        xmlns:d=\"http://schemas.microsoft.com/expression/blend/2008\"\n        xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\"\n        xmlns:nodify=\"https://miroiu.github.io/nodify\"\n        mc:Ignorable=\"d\">\n\n    <Window.Resources>\n        <GeometryDrawing x:Key=\"SmallGridGeometry\"\n                        Geometry=\"M0,0 L0,1 0.03,1 0.03,0.03 1,0.03 1,0 Z\"\n                        Brush=\"{StaticResource NodifyEditor.SelectionRectangleBackgroundBrush}\" />\n\n        <GeometryDrawing x:Key=\"LargeGridGeometry\"\n                        Geometry=\"M0,0 L0,1 0.015,1 0.015,0.015 1,0.015 1,0 Z\"\n                        Brush=\"{StaticResource NodifyEditor.SelectionRectangleBackgroundBrush}\" />\n\n        <DrawingBrush x:Key=\"SmallGridLinesDrawingBrush\"\n                    TileMode=\"Tile\"\n                    ViewportUnits=\"Absolute\"\n                    Viewport=\"0 0 20 20\"\n                    Transform=\"{Binding ViewportTransform, ElementName=Editor}\"\n                    Drawing=\"{StaticResource SmallGridGeometry}\" />\n\n        <DrawingBrush x:Key=\"LargeGridLinesDrawingBrush\"\n                    TileMode=\"Tile\"\n                    ViewportUnits=\"Absolute\"\n                    Opacity=\"0.5\"\n                    Viewport=\"0 0 100 100\"\n                    Transform=\"{Binding ViewportTransform, ElementName=Editor}\"\n                    Drawing=\"{StaticResource LargeGridGeometry}\" />\n    </Window.Resources>\n\n    <Grid Background=\"{StaticResource NodifyEditor.BackgroundBrush}\">\n        <nodify:NodifyEditor x:Name=\"Editor\" Background=\"{StaticResource SmallGridLinesDrawingBrush}\" />\n\n        <Grid Background=\"{StaticResource LargeGridLinesDrawingBrush}\"\n              Panel.ZIndex=\"-2\" />\n    </Grid>\n</Window>\n```\n\n> Tip: Right-click and drag the screen around to move the view and use the mouse wheel to zoom in and out.\n"
  },
  {
    "path": "docs/Home.md",
    "content": "![editor-interaction](https://user-images.githubusercontent.com/12727904/192004838-ec6dd997-5e64-4c01-940c-1cd1b8d27837.gif)\n\n# Welcome to Nodify!\n\nNodify is a WPF node-based [editor control](Editor-Overview) featuring a collection of [nodes](Nodes-Overview), [connections](Connections-Overview), and [connectors](Connectors-Overview) components aiming to simplify the process of building node-based tools.\n\nIt is inspired by Unreal Engine's [Blueprints Visual Scripting](https://docs.unrealengine.com/en-US/ProgrammingAndScripting/Blueprints/index.html) system but focuses only on the user interface and user interaction part. Unlike Blueprints, Nodify is a general-purpose library that offers a node graph editor component that can be embedded in any WPF application.\n\nThe graph editor is an infinite area where you can place and move nodes around, select and drag groups of nodes, connect and disconnect nodes or connectors, zoom in and out, and automatically move the screen when dragging a node or a wire near the edges and much more.\n\nNodify is feature-rich and optimized for interaction with hundreds of nodes at once, and... it is built from the ground up to work with MVVM.\n\n### Requirements\n\n![IDE](https://img.shields.io/static/v1?label=%20&message=VS%202019%20or%20greater&color=informational&style=for-the-badge&logo=visual-studio)\n![C#](https://img.shields.io/static/v1?label=%20&message=8.0&color=239120&style=for-the-badge&logo=c-sharp)\n![.NET](https://img.shields.io/static/v1?label=%20&message=Framework%204.7.2%20to%20NET%206&color=5C2D91&style=for-the-badge&logo=.net)\n\n### Install Nodify from Nuget\n\n[![Download Package](https://img.shields.io/nuget/v/Nodify?label=Download&logo=nuget&style=for-the-badge)](https://www.nuget.org/packages/Nodify/)\n\n```xml\nInstall-Package Nodify\n```\n\n### Application examples\n\n- [Playground](https://github.com/miroiu/nodify/tree/master/Examples/Nodify.Playground)\n- [Shapes](https://github.com/miroiu/nodify/tree/master/Examples/Nodify.Shapes)\n- [State machine](https://github.com/miroiu/nodify/tree/master/Examples/Nodify.StateMachine)\n- [Calculator](https://github.com/miroiu/nodify/tree/master/Examples/Nodify.Calculator)\n\n[![IDE](https://img.shields.io/static/v1?label=%20&message=GET%20STARTED&color=9cf&style=for-the-badge)](Getting-Started)\n"
  },
  {
    "path": "docs/ItemContainer-Overview.md",
    "content": "The ```ItemContainer``` is the **most important** piece of an editor. It is a content control that wraps every other control that is generated by the ```NodifyEditor```'s ```ItemsSource``` and has a `Location` in graph coordinates. \n\n## Table of contents\n - [Selecting](#selecting)\n   - [API](#selection-api)\n - [Moving](#moving-and-dragging)\n\n## Selecting\n\nSelecting an ```ItemContainer``` is done by releasing the left mouse button over the bounding box of the container. The bounding box is calculated using the ```DesiredSizeForSelection``` dependency property if specified, otherwise using the ```RenderSize```. \n\n> Note: A container can be selected programmatically by setting the ```IsSelected``` dependency property to ```true```.\n\nThe container will also be selected if the left mouse button is not released and the mouse is moved, which will clear the current selection and start a dragging operation on the selected container.\n\nSelection can also be done by releasing the right mouse button on the container which will clear the current selection and select the target container.\n\nDifferent behavior is used depending on the ```ModifierKeys``` held when selecting a container:\n- ```Replace``` - no modifier key (default behavior, clears the selected items and selects the container)\n- ```Append``` - shift key (adds the container to the selected items)\n- ```Invert``` - control key (toggles the container's ```IsSelected``` dependency property)\n\nAn ```ItemContainer``` can only be selected if the ```IsSelectable``` dependency property is ```true```.\n\nSelecting or unselecting a container will fire the ```Selected``` event, respectively the ```Unselected``` event, and will set the ```IsSelected``` dependency property to the new value.\n\nIf the default style is not overridden, the container's border will use the ```SelectedBrush``` dependency property.\n\nDefault values:\n- ```IsSelectable```: true\n- ```IsSelected```: false\n\n### Selection API\n\n* IsSelectableInArea\n\n## Moving and dragging\n\nA dragging operation can be started by holding the left mouse button on an ```ItemContainer``` and moving the mouse.\nAn ```ItemContainer``` can be dragged only if its ```IsDraggable``` dependency property is set to ```true```.\n\n> Note: ```ItemContainer```s can be programmatically moved by setting their ```Location``` dependency property.\n\nDragging an ```ItemContainer``` fires a sequence of events that are handled by the ```NodifyEditor``` and applies to all the selected items:\n- ```DragStarted``` - will set ```IsPreviewingLocation``` dependency property to ```true```;\n- ```DragDelta``` - will be fired until the dragging operation is finished or canceled and will move all the selected items with it;\n- ```DragCompleted``` - will finish or cancel the dragging operation and set the ```Location``` dependency property to the final position and ```IsPreviewingLocation``` to ```false```.\n\n> Note: A dragging operation can be canceled by releasing the right mouse button if ```AllowDraggingCancellation``` is set to ```true```.\n\nDefault values:\n- ```IsDraggable```: true\n- ```AllowDraggingCancellation```: true\n"
  },
  {
    "path": "docs/Minimap-Overview.md",
    "content": "## Table of contents\n\n- [Moving the viewport](#panning)\n- [Zooming](#zooming)\n- [Customization](#customization)\n\nThe `Minimap` control is a custom control designed to provide a synchronized and miniature view of items in a `NodifyEditor`. It inherits from `ItemsControl` and displays items through the `ItemsSource` property. Each item is wrapped in a `MinimapItem` container that requires the `Location`, `Width`, and `Height` properties to be set in the `ItemContainerStyle`.\n\n> [!TIP]\n> For real-time movement of nodes inside the minimap, it's required to set `NodifyEditor.EnableDraggingContainersOptimizations` to `false`.\n\nThe control also displays a viewport rectangle that represents the visible area in the editor and requires the `ViewportLocation` and `ViewportSize` properties to be set.\n\n```xml\n<nodify:Minimap\n    ItemsSource=\"{Binding ItemsSource, ElementName=Editor}\"\n    ViewportLocation=\"{Binding ViewportLocation, ElementName=Editor}\"\n    ViewportSize=\"{Binding ViewportSize, ElementName=Editor}\"\n    Zoom=\"OnMinimapZoom\">\n    <nodify:Minimap.ItemContainerStyle>\n        <Style TargetType=\"nodify:MinimapItem\">\n            <Setter Property=\"Location\" Value=\"{Binding MyItemLocation}\" />\n        </Style>\n    </nodify:Minimap.ItemContainerStyle>\n</nodify:Minimap>\n```\n\n```csharp\nprivate void OnMinimapZoom(object sender, ZoomEventArgs e)\n{\n    Editor.ZoomAtPosition(e.Zoom, e.Location);\n}\n```\n\n> [!IMPORTANT]\n> The `Width` and `Height` should be constrained by the parent container or set to constant values on the `Minimap` to prevent resizing to fit the content.\n\n## Panning\n\nPanning is done by holding click and dragging and can be disabled by setting the `IsReadOnly` property to `true`. The `ViewportLocation` is updated during panning, therefore it must be a two-way binding (binds two ways by default).\n\nThe panning gesture can be configured by setting `EditorGestures.Mappings.Minimap.DragViewport` to the desired gesture.\n\n## Zooming\n\nZooming is done by scrolling the mouse wheel and can be disabled by setting the `IsReadOnly` property to `true` or by not handling the `Zoom` event.\n\nThe zooming modifier key can be configured by setting `EditorGestures.Mappings.Minimap.ZoomModifierKey` to the desired value.\n\n## Customization\n\nThe `ViewportStyle` is used to customize the viewport rectangle.\n\n```xml\n<Style x:Key=\"MyViewportStyle\" TargetType=\"Rectangle\">\n    <Setter Property=\"Fill\" Value=\"Transparent\"/>\n    <Setter Property=\"Stroke\" Value=\"White\"/>\n    <Setter Property=\"StrokeThickness\" Value=\"3\"/>\n</Style>\n\n<nodify:Minimap ViewportStyle=\"{StaticResource MyViewportStyle}\" ... />\n```\n\nThe `MaxViewportOffset` property is used to restrict how far the viewport can be moved away from the items when [panning](#panning).\n\nThe `ResizeToViewport` property changes the resizing behavior of the minimap.\nIf `true`, the minimap will resize to always display the viewport alongside the items.\n\n![scale-with-viewport](https://github.com/user-attachments/assets/7a8887bf-f3f4-44d7-8311-6d9ba7869d78)\n\nIf `false`, the minimap will resize to only include the items, allowing the viewport to go out of bounds.\n\n![viewport-out-of-bounds](https://github.com/user-attachments/assets/5d3b388e-8e40-4bfe-af3b-4c12fb47548d)\n"
  },
  {
    "path": "docs/Nodes-Overview.md",
    "content": "Nodes are the building blocks of a node editor. They are wrapped in [`ItemContainer`s](ItemContainer-Overview) and can be any custom control. (e.g. a TextBlock)\n\nThe following nodes are part of the library:\n\n### 1. The ```Node``` control\nThis type of node supports both ```Input``` and ```Output``` connectors and can be moved around.\n\n```xml\n<nodify:NodifyEditor xmlns:sys=\"clr-namespace:System;assembly=mscorlib\">\n  <nodify:NodifyEditor.ItemsSource>\n    <CompositeCollection>\n        <nodify:Node Header=\"My Node\">\n            <nodify:Node.Input>\n                <CompositeCollection>\n                    <sys:String>In 0</sys:String>\n                    <sys:String>In 1</sys:String>\n                </CompositeCollection>\n            </nodify:Node.Input>\n            <nodify:Node.Output>\n                <CompositeCollection>\n                    <sys:String>Out 0</sys:String>\n                    <sys:String>Out 1</sys:String>\n                </CompositeCollection>\n            </nodify:Node.Output>\n            <nodify:Node.InputConnectorTemplate>\n                <DataTemplate>\n                    <nodify:NodeInput Header=\"{Binding}\" />\n                </DataTemplate>\n            </nodify:Node.InputConnectorTemplate>\n            <nodify:Node.OutputConnectorTemplate>\n                <DataTemplate>\n                    <nodify:NodeOutput Header=\"{Binding}\" />\n                </DataTemplate>\n            </nodify:Node.OutputConnectorTemplate>\n        </nodify:Node>\n    </CompositeCollection>\n  </nodify:NodifyEditor.ItemsSource>\n</nodify:NodifyEditor>\n```\n\nThe `Header` of the node can be customized using the `HeaderTemplate`. Respectively, the `Footer` of the node can be customized using the `FooterTemplate`.\n\nEach item in the `Input` collection can be customized using the `InputConnectorTemplate`. Respectively the `Output` can be customized using the `OutputConnectorTemplate`.\n\n![Node](https://i.imgur.com/VwAYlX3.gif)\n\n### 2. The ```GroupingNode``` control\n\nThis type of node can be resized and will move nodes that are inside it if dragged by the ```Header```.\n\nIf the ```SwitchMovementModeModifierKey``` (**Shift** key by default) is held, it will move without moving its children.\n\n```xml\n<nodify:NodifyEditor>\n    <nodify:NodifyEditor.ItemsSource>\n        <CompositeCollection>\n            <nodify:GroupingNode Header=\"Grouping node\"\n                            Width=\"300\"\n                            Height=\"250\" />            \n            <nodify:Node Header=\"My node\" />\n            <nodify:Node Header=\"My other node\" />\n        </CompositeCollection>\n    </nodify:NodifyEditor.ItemsSource>\n</nodify:NodifyEditor>\n```\n\nThe `Header` of the node can be customized using the `HeaderTemplate`. Respectively the `Content` of the node can be customized using the `ContentTemplate`.\n\nThe size of the node can be set programmatically by changing the `ActualSize` dependency property value. \n\n#### Default values\n\n* CanResize: true\n* MovementMode: Grouped\n\n#### Commands\n\n* ResizeCompleted\n* ResizeStarted\n\n![Grouping Node](https://i.imgur.com/HYxt2cs.gif)\n\n### 3. The ```KnotNode``` control\n\nThis type of control can be used to reroute ```Connection```s as it supports only one ```Connector```. \n\nThe `Content` of the node can be customized using the `ContentTemplate`. \n\n```xml\n<nodify:NodifyEditor>\n    <nodify:NodifyEditor.ItemsSource>\n        <CompositeCollection>\n            <nodify:KnotNode />\n        </CompositeCollection>\n    </nodify:NodifyEditor.ItemsSource>\n</nodify:NodifyEditor>\n```\n\n![Knot Node](https://i.imgur.com/fMrEqY1.gif)\n\n### 4. The ```StateNode``` control\n\nThis type of node is a ```Connector``` itself, meaning that it will raise ```PendingConnection``` events on interaction. Because it inherits from `Connector`, you need to bind the `Anchor` property and `IsConnected` to the corresponding state. (if IsConnected is set to false, the connections won't update)\n\n```xml\n<nodify:NodifyEditor>\n    <nodify:NodifyEditor.ItemsSource>\n        <CompositeCollection>\n            <nodify:StateNode Content=\"My node\" />\n        </CompositeCollection>\n    </nodify:NodifyEditor.ItemsSource>\n</nodify:NodifyEditor>\n```\n\nThe `Content` of the node can be customized using the `ContentTemplate`.\n\n![State Node](https://i.imgur.com/FrI2epL.gif)"
  },
  {
    "path": "docs/_Sidebar.md",
    "content": "| [English](https://github.com/WYihei/nodify/wiki/Home) | [简体中文](https://github.com/WYihei/nodify/wiki/主页) |\n| ----------------------------------------------------- | ------------------------------------------------------ |\n\n---\n\n## [Home](Home)\n\n[Getting Started](Getting-Started)\n\n- [Hierarchy and terminology](Getting-Started#hierarchy-and-terminology)\n- [Content layers](Getting-Started#content-layers)\n- [Creating an editor](Getting-Started#creating-an-editor)\n- [Using a theme](Getting-Started#using-an-existing-theme)\n- [Minimal example](Getting-Started#a-minimal-example)\n- [Drawing a grid](Getting-Started#drawing-a-grid)\n\n[Editor overview](Editor-Overview)\n\n- [Moving the viewport](Editor-Overview#panning)\n- [Zooming](Editor-Overview#zooming)\n- [Scrolling](Editor-Overview#scrolling)\n- [Selecting items](Editor-Overview#selecting)\n- [Pushing items](Editor-Overview#pushing-items)\n- [Snapping to grid](Editor-Overview#snapping)\n- [Commands](Editor-Overview#commands)\n\n[ItemContainer overview](ItemContainer-Overview)\n\n- [Selecting](ItemContainer-Overview#selecting)\n- [Moving](ItemContainer-Overview#moving-and-dragging)\n\n[Nodes overview](Nodes-Overview)\n\n- [The node](Nodes-Overview#1-the-node-control)\n- [The grouping node](Nodes-Overview#2-the-groupingnode-control)\n- [The knot node](Nodes-Overview#3-the-knotnode-control)\n- [The state node](Nodes-Overview#4-the-statenode-control)\n\n[Connections overview](Connections-Overview)\n\n- [Base connection](Connections-Overview#base-connection)\n- [Bezier connection](Connections-Overview#connection)\n- [Line connection](Connections-Overview#line-connection)\n- [Circuit connection](Connections-Overview#circuit-connection)\n- [Step connection](Connections-Overview#step-connection)\n- [Pending connection](Connections-Overview#pending-connection)\n\n[Connectors overview](Connectors-Overview)\n\n- [NodeInput and NodeOutput](Connectors-Overview#nodeinput-and-nodeoutput)\n\n[CuttingLine overview](CuttingLine-Overview)\n\n- [Enabling preview](CuttingLine-Overview#enabling-preview)\n- [Custom connections](CuttingLine-Overview#custom-connections)\n- [Customization](CuttingLine-Overview#customization)\n\n[Minimap overview](Minimap-Overview)\n\n- [Moving the viewport](Minimap-Overview#panning)\n- [Zooming](Minimap-Overview#zooming)\n- [Customization](Minimap-Overview#customization)\n\n[API Reference](API-Reference)\n\n[(FAQ) Frequently asked questions](FAQ)\n"
  },
  {
    "path": "docs/localization/zh-CN/API-Reference.md",
    "content": "- [Alignment Enum](#alignment-enum)  \n- [AllGestures Class](#allgestures-class)  \n- [AnyGesture Class](#anygesture-class)  \n- [ArrowHeadEnds Enum](#arrowheadends-enum)  \n- [ArrowHeadShape Enum](#arrowheadshape-enum)  \n- [BaseConnection Class](#baseconnection-class)  \n- [BoxValue Class](#boxvalue-class)  \n- [CircuitConnection Class](#circuitconnection-class)  \n- [Connection Class](#connection-class)  \n- [ConnectionDirection Enum](#connectiondirection-enum)  \n- [ConnectionEventArgs Class](#connectioneventargs-class)  \n- [ConnectionEventHandler Delegate](#connectioneventhandler-delegate)  \n- [ConnectionGestures Class](#connectiongestures-class)  \n- [ConnectionOffsetMode Enum](#connectionoffsetmode-enum)  \n- [Connector Class](#connector-class)  \n- [ConnectorEventArgs Class](#connectoreventargs-class)  \n- [ConnectorEventHandler Delegate](#connectoreventhandler-delegate)  \n- [ConnectorGestures Class](#connectorgestures-class)  \n- [ConnectorPosition Enum](#connectorposition-enum)  \n- [ContainerDefaultState Class](#containerdefaultstate-class)  \n- [ContainerDraggingState Class](#containerdraggingstate-class)  \n- [ContainerState Class](#containerstate-class)  \n- [CuttingLine Class](#cuttingline-class)  \n- [DecoratorContainer Class](#decoratorcontainer-class)  \n- [EditorCommands Class](#editorcommands-class)  \n- [EditorCuttingState Class](#editorcuttingstate-class)  \n- [EditorDefaultState Class](#editordefaultstate-class)  \n- [EditorGestures Class](#editorgestures-class)  \n- [EditorPanningState Class](#editorpanningstate-class)  \n- [EditorPushingItemsState Class](#editorpushingitemsstate-class)  \n- [EditorSelectingState Class](#editorselectingstate-class)  \n- [EditorState Class](#editorstate-class)  \n- [GeneratedInternalTypeHelper Class](#generatedinternaltypehelper-class)  \n- [GroupingMovementMode Enum](#groupingmovementmode-enum)  \n- [GroupingNode Class](#groupingnode-class)  \n- [GroupingNodeGestures Class](#groupingnodegestures-class)  \n- [INodifyCanvasItem Interface](#inodifycanvasitem-interface)  \n- [InputGestureRef Class](#inputgestureref-class)  \n- [ItemContainer Class](#itemcontainer-class)  \n- [ItemContainerGestures Class](#itemcontainergestures-class)  \n- [KnotNode Class](#knotnode-class)  \n- [LineConnection Class](#lineconnection-class)  \n- [Match Enum](#match-enum)  \n- [Minimap Class](#minimap-class)  \n- [MinimapGestures Class](#minimapgestures-class)  \n- [MinimapItem Class](#minimapitem-class)  \n- [MultiGesture Class](#multigesture-class)  \n- [Node Class](#node-class)  \n- [NodeInput Class](#nodeinput-class)  \n- [NodeOutput Class](#nodeoutput-class)  \n- [NodifyCanvas Class](#nodifycanvas-class)  \n- [NodifyEditor Class](#nodifyeditor-class)  \n- [NodifyEditorGestures Class](#nodifyeditorgestures-class)  \n- [PendingConnection Class](#pendingconnection-class)  \n- [PendingConnectionEventArgs Class](#pendingconnectioneventargs-class)  \n- [PendingConnectionEventHandler Delegate](#pendingconnectioneventhandler-delegate)  \n- [PreviewLocationChanged Delegate](#previewlocationchanged-delegate)  \n- [ResizeEventArgs Class](#resizeeventargs-class)  \n- [ResizeEventHandler Delegate](#resizeeventhandler-delegate)  \n- [SelectionGestures Class](#selectiongestures-class)  \n- [SelectionHelper Class](#selectionhelper-class)  \n- [SelectionType Enum](#selectiontype-enum)  \n- [StateNode Class](#statenode-class)  \n- [StepConnection Class](#stepconnection-class)  \n- [ZoomEventArgs Class](#zoomeventargs-class)  \n- [ZoomEventHandler Delegate](#zoomeventhandler-delegate)  \n  \n  \n## Alignment Enum  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n```csharp  \npublic enum Alignment  \n```  \n  \n### Fields  \n  \n#### Bottom  \n  \n```csharp  \nBottom = 2;  \n```  \n  \n#### Center  \n  \n```csharp  \nCenter = 5;  \n```  \n  \n#### Left  \n  \n```csharp  \nLeft = 1;  \n```  \n  \n#### Middle  \n  \n```csharp  \nMiddle = 4;  \n```  \n  \n#### Right  \n  \n```csharp  \nRight = 3;  \n```  \n  \n#### Top  \n  \n```csharp  \nTop = 0;  \n```  \n  \n## AllGestures Class  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**Inheritance:** [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object) → [InputGesture](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.InputGesture) → [MultiGesture](#multigesture-class) → [AllGestures](#allgestures-class)  \n  \n```csharp  \npublic sealed class AllGestures : MultiGesture  \n```  \n  \n### Constructors  \n  \n#### AllGestures(InputGesture[])  \n  \n```csharp  \npublic AllGestures(InputGesture[] gestures);  \n```  \n  \n**Parameters**  \n  \n`gestures` [InputGesture[]](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.InputGesture[])  \n  \n## AnyGesture Class  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**Inheritance:** [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object) → [InputGesture](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.InputGesture) → [MultiGesture](#multigesture-class) → [AnyGesture](#anygesture-class)  \n  \n```csharp  \npublic sealed class AnyGesture : MultiGesture  \n```  \n  \n### Constructors  \n  \n#### AnyGesture(InputGesture[])  \n  \n```csharp  \npublic AnyGesture(InputGesture[] gestures);  \n```  \n  \n**Parameters**  \n  \n`gestures` [InputGesture[]](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.InputGesture[])  \n  \n## ArrowHeadEnds Enum  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**References:** [BaseConnection](#baseconnection-class)  \n  \nThe end at which the arrow head is drawn.  \n  \n```csharp  \npublic enum ArrowHeadEnds  \n```  \n  \n### Fields  \n  \n#### Both  \n  \nArrow heads at both ends.  \n  \n```csharp  \nBoth = 2;  \n```  \n  \n#### End  \n  \nArrow head at end.  \n  \n```csharp  \nEnd = 1;  \n```  \n  \n#### None  \n  \nNo arrow head.  \n  \n```csharp  \nNone = 3;  \n```  \n  \n#### Start  \n  \nArrow head at start.  \n  \n```csharp  \nStart = 0;  \n```  \n  \n## ArrowHeadShape Enum  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**References:** [BaseConnection](#baseconnection-class)  \n  \nThe shape of the arrowhead.  \n  \n```csharp  \npublic enum ArrowHeadShape  \n```  \n  \n### Fields  \n  \n#### Arrowhead  \n  \nThe default arrowhead.  \n  \n```csharp  \nArrowhead = 0;  \n```  \n  \n#### Ellipse  \n  \nAn ellipse.  \n  \n```csharp  \nEllipse = 1;  \n```  \n  \n#### Rectangle  \n  \nA rectangle.  \n  \n```csharp  \nRectangle = 2;  \n```  \n  \n## BaseConnection Class  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**Inheritance:** [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object) → [DispatcherObject](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Threading.DispatcherObject) → [DependencyObject](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.DependencyObject) → [Visual](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Media.Visual) → [UIElement](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.UIElement) → [FrameworkElement](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.FrameworkElement) → [Shape](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Shapes.Shape) → [BaseConnection](#baseconnection-class)  \n  \n**Derived:** [LineConnection](#lineconnection-class), [Connection](#connection-class)  \n  \n**References:** [ArrowHeadEnds](#arrowheadends-enum), [ArrowHeadShape](#arrowheadshape-enum), [ConnectionDirection](#connectiondirection-enum), [ConnectionEventArgs](#connectioneventargs-class), [ConnectionEventHandler](#connectioneventhandler-delegate), [ConnectionOffsetMode](#connectionoffsetmode-enum), [CuttingLine](#cuttingline-class), [NodifyEditor](#nodifyeditor-class)  \n  \nRepresents the base class for shapes that are drawn from a [BaseConnection.Source](#baseconnection-class#source) point to a [BaseConnection.Target](#baseconnection-class#target) point.  \n  \n```csharp  \npublic abstract class BaseConnection : Shape  \n```  \n  \n### Constructors  \n  \n#### BaseConnection()  \n  \n```csharp  \nprotected BaseConnection();  \n```  \n  \n### Fields  \n  \n#### ZeroVector  \n  \nGets a vector that has its coordinates set to 0.  \n  \n```csharp  \nprotected static Vector ZeroVector;  \n```  \n  \n**Field Value**  \n  \n[Vector](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Vector)  \n  \n### Properties  \n  \n#### ArrowEnds  \n  \nGets or sets the arrowhead ends.  \n  \n```csharp  \npublic ArrowHeadEnds ArrowEnds { get; set; }  \n```  \n  \n**Property Value**  \n  \n[ArrowHeadEnds](#arrowheadends-enum)  \n  \n#### ArrowShape  \n  \nGets or sets the arrowhead ends.  \n  \n```csharp  \npublic ArrowHeadShape ArrowShape { get; set; }  \n```  \n  \n**Property Value**  \n  \n[ArrowHeadShape](#arrowheadshape-enum)  \n  \n#### ArrowSize  \n  \nGets or sets the size of the arrow head.  \n  \n```csharp  \npublic Size ArrowSize { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Size](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Size)  \n  \n#### DefiningGeometry  \n  \n```csharp  \nprotected override Geometry DefiningGeometry { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Geometry](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Media.Geometry)  \n  \n#### Direction  \n  \nGets or sets the direction in which this connection is flowing.  \n  \n```csharp  \npublic ConnectionDirection Direction { get; set; }  \n```  \n  \n**Property Value**  \n  \n[ConnectionDirection](#connectiondirection-enum)  \n  \n#### DirectionalArrowsAnimationDuration  \n  \nGets or sets the duration in seconds of a directional arrow flowing from [BaseConnection.Source](#baseconnection-class#source) to [BaseConnection.Target](#baseconnection-class#target).  \n  \n```csharp  \npublic double DirectionalArrowsAnimationDuration { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Double](https://docs.microsoft.com/en-us/dotnet/api/System.Double)  \n  \n#### DirectionalArrowsCount  \n  \nGets or sets the number of arrows to be drawn on the line in the direction of the connection (see [BaseConnection.Direction](#baseconnection-class#direction)).  \n  \n```csharp  \npublic uint DirectionalArrowsCount { get; set; }  \n```  \n  \n**Property Value**  \n  \n[UInt32](https://docs.microsoft.com/en-us/dotnet/api/System.UInt32)  \n  \n#### DirectionalArrowsOffset  \n  \nGets or sets the offset of the arrows drawn by the [BaseConnection.DirectionalArrowsCount](#baseconnection-class#directionalarrowscount) (value is clamped between 0 and 1).  \n  \n```csharp  \npublic double DirectionalArrowsOffset { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Double](https://docs.microsoft.com/en-us/dotnet/api/System.Double)  \n  \n#### DisconnectCommand  \n  \nRemoves this connection. Triggered by Nodify.EditorGestures.ConnectionGestures.Disconnect gesture.\n            Parameter is the location where the disconnect ocurred.  \n  \n```csharp  \npublic ICommand DisconnectCommand { get; set; }  \n```  \n  \n**Property Value**  \n  \n[ICommand](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.ICommand)  \n  \n#### FontFamily  \n  \n```csharp  \npublic FontFamily FontFamily { get; set; }  \n```  \n  \n**Property Value**  \n  \n[FontFamily](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Media.FontFamily)  \n  \n#### FontSize  \n  \n```csharp  \npublic double FontSize { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Double](https://docs.microsoft.com/en-us/dotnet/api/System.Double)  \n  \n#### FontStretch  \n  \n```csharp  \npublic FontStretch FontStretch { get; set; }  \n```  \n  \n**Property Value**  \n  \n[FontStretch](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.FontStretch)  \n  \n#### FontStyle  \n  \n```csharp  \npublic FontStyle FontStyle { get; set; }  \n```  \n  \n**Property Value**  \n  \n[FontStyle](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.FontStyle)  \n  \n#### FontWeight  \n  \n```csharp  \npublic FontWeight FontWeight { get; set; }  \n```  \n  \n**Property Value**  \n  \n[FontWeight](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.FontWeight)  \n  \n#### Foreground  \n  \nThe brush used to render the [BaseConnection.Text](#baseconnection-class#text).  \n  \n```csharp  \npublic Brush Foreground { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Brush](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Media.Brush)  \n  \n#### IsAnimatingDirectionalArrows  \n  \nGets or sets whether the directional arrows should be flowing through the connection wire.  \n  \n```csharp  \npublic bool IsAnimatingDirectionalArrows { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean)  \n  \n#### OutlineBrush  \n  \nThe brush used to render the outline.  \n  \n```csharp  \npublic Brush OutlineBrush { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Brush](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Media.Brush)  \n  \n#### OutlineThickness  \n  \nThe thickness of the outline.  \n  \n```csharp  \npublic double OutlineThickness { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Double](https://docs.microsoft.com/en-us/dotnet/api/System.Double)  \n  \n#### PrioritizeBaseConnectionForSelection  \n  \nWhether to prioritize controls of type [BaseConnection](#baseconnection-class) inside custom connections (connection wrappers) \n            when setting the [BaseConnection.IsSelectableProperty](#baseconnection-class#isselectableproperty) and [BaseConnection.IsSelectedProperty](#baseconnection-class#isselectedproperty) attached properties.  \n  \n```csharp  \npublic static bool PrioritizeBaseConnectionForSelection { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean)  \n  \n#### Source  \n  \nGets or sets the start point of this connection.  \n  \n```csharp  \npublic Point Source { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n#### SourceOffset  \n  \nGets or sets the offset from the [BaseConnection.Source](#baseconnection-class#source) point.  \n  \n```csharp  \npublic Size SourceOffset { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Size](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Size)  \n  \n#### SourceOffsetMode  \n  \nGets or sets the [ConnectionOffsetMode](#connectionoffsetmode-enum) to apply to the [BaseConnection.Source](#baseconnection-class#source) when drawing the connection.  \n  \n```csharp  \npublic ConnectionOffsetMode SourceOffsetMode { get; set; }  \n```  \n  \n**Property Value**  \n  \n[ConnectionOffsetMode](#connectionoffsetmode-enum)  \n  \n#### SourceOrientation  \n  \nGets or sets the orientation in which this connection is flowing.  \n  \n```csharp  \npublic Orientation SourceOrientation { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Orientation](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Controls.Orientation)  \n  \n#### Spacing  \n  \nThe distance between the start point and the where the angle breaks.  \n  \n```csharp  \npublic double Spacing { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Double](https://docs.microsoft.com/en-us/dotnet/api/System.Double)  \n  \n#### SplitCommand  \n  \nSplits the connection. Triggered by Nodify.EditorGestures.ConnectionGestures.Split gesture.\n            Parameter is the location where the splitting ocurred.  \n  \n```csharp  \npublic ICommand SplitCommand { get; set; }  \n```  \n  \n**Property Value**  \n  \n[ICommand](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.ICommand)  \n  \n#### Target  \n  \nGets or sets the end point of this connection.  \n  \n```csharp  \npublic Point Target { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n#### TargetOffset  \n  \nGets or sets the offset from the [BaseConnection.Target](#baseconnection-class#target) point.  \n  \n```csharp  \npublic Size TargetOffset { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Size](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Size)  \n  \n#### TargetOffsetMode  \n  \nGets or sets the [ConnectionOffsetMode](#connectionoffsetmode-enum) to apply to the [BaseConnection.Target](#baseconnection-class#target) when drawing the connection.  \n  \n```csharp  \npublic ConnectionOffsetMode TargetOffsetMode { get; set; }  \n```  \n  \n**Property Value**  \n  \n[ConnectionOffsetMode](#connectionoffsetmode-enum)  \n  \n#### TargetOrientation  \n  \nGets or sets the orientation in which this connection is flowing.  \n  \n```csharp  \npublic Orientation TargetOrientation { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Orientation](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Controls.Orientation)  \n  \n#### Text  \n  \nGets or sets the text contents of the [BaseConnection](#baseconnection-class).  \n  \n```csharp  \npublic string Text { get; set; }  \n```  \n  \n**Property Value**  \n  \n[String](https://docs.microsoft.com/en-us/dotnet/api/System.String)  \n  \n### Methods  \n  \n#### DrawArrowGeometry(StreamGeometryContext, Point, Point, ConnectionDirection, ArrowHeadShape, Orientation)  \n  \n```csharp  \nprotected virtual void DrawArrowGeometry(StreamGeometryContext context, Point source, Point target, ConnectionDirection arrowDirection = 0, ArrowHeadShape shape = 0, Orientation orientation = 0);  \n```  \n  \n**Parameters**  \n  \n`context` [StreamGeometryContext](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Media.StreamGeometryContext)  \n  \n`source` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n`target` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n`arrowDirection` [ConnectionDirection](#connectiondirection-enum)  \n  \n`shape` [ArrowHeadShape](#arrowheadshape-enum)  \n  \n`orientation` [Orientation](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Controls.Orientation)  \n  \n#### DrawDefaultArrowhead(StreamGeometryContext, Point, Point, ConnectionDirection, Orientation)  \n  \n```csharp  \nprotected virtual void DrawDefaultArrowhead(StreamGeometryContext context, Point source, Point target, ConnectionDirection arrowDirection = 0, Orientation orientation = 0);  \n```  \n  \n**Parameters**  \n  \n`context` [StreamGeometryContext](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Media.StreamGeometryContext)  \n  \n`source` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n`target` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n`arrowDirection` [ConnectionDirection](#connectiondirection-enum)  \n  \n`orientation` [Orientation](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Controls.Orientation)  \n  \n#### DrawDirectionalArrowheadGeometry(StreamGeometryContext, Vector, Point)  \n  \n```csharp  \nprotected virtual void DrawDirectionalArrowheadGeometry(StreamGeometryContext context, Vector direction, Point location);  \n```  \n  \n**Parameters**  \n  \n`context` [StreamGeometryContext](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Media.StreamGeometryContext)  \n  \n`direction` [Vector](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Vector)  \n  \n`location` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n#### DrawDirectionalArrowsGeometry(StreamGeometryContext, Point, Point)  \n  \n```csharp  \nprotected virtual void DrawDirectionalArrowsGeometry(StreamGeometryContext context, Point source, Point target);  \n```  \n  \n**Parameters**  \n  \n`context` [StreamGeometryContext](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Media.StreamGeometryContext)  \n  \n`source` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n`target` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n#### DrawEllipseArrowhead(StreamGeometryContext, Point, Point, ConnectionDirection, Orientation)  \n  \n```csharp  \nprotected virtual void DrawEllipseArrowhead(StreamGeometryContext context, Point source, Point target, ConnectionDirection arrowDirection = 0, Orientation orientation = 0);  \n```  \n  \n**Parameters**  \n  \n`context` [StreamGeometryContext](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Media.StreamGeometryContext)  \n  \n`source` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n`target` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n`arrowDirection` [ConnectionDirection](#connectiondirection-enum)  \n  \n`orientation` [Orientation](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Controls.Orientation)  \n  \n#### DrawLineGeometry(StreamGeometryContext, Point, Point)  \n  \n```csharp  \nprotected virtual ValueTuple<ValueTuple<Point, Point>, ValueTuple<Point, Point>> DrawLineGeometry(StreamGeometryContext context, Point source, Point target);  \n```  \n  \n**Parameters**  \n  \n`context` [StreamGeometryContext](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Media.StreamGeometryContext)  \n  \n`source` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n`target` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n**Returns**  \n  \n[ValueTuple<ValueTuple<Point, Point>, ValueTuple<Point, Point>>](https://docs.microsoft.com/en-us/dotnet/api/System.ValueTuple)  \n  \n#### DrawRectangleArrowhead(StreamGeometryContext, Point, Point, ConnectionDirection, Orientation)  \n  \n```csharp  \nprotected virtual void DrawRectangleArrowhead(StreamGeometryContext context, Point source, Point target, ConnectionDirection arrowDirection = 0, Orientation orientation = 0);  \n```  \n  \n**Parameters**  \n  \n`context` [StreamGeometryContext](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Media.StreamGeometryContext)  \n  \n`source` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n`target` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n`arrowDirection` [ConnectionDirection](#connectiondirection-enum)  \n  \n`orientation` [Orientation](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Controls.Orientation)  \n  \n#### GetIsSelectable(UIElement)  \n  \n```csharp  \npublic static bool GetIsSelectable(UIElement elem);  \n```  \n  \n**Parameters**  \n  \n`elem` [UIElement](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.UIElement)  \n  \n**Returns**  \n  \n[Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean)  \n  \n#### GetIsSelected(UIElement)  \n  \n```csharp  \npublic static bool GetIsSelected(UIElement elem);  \n```  \n  \n**Parameters**  \n  \n`elem` [UIElement](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.UIElement)  \n  \n**Returns**  \n  \n[Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean)  \n  \n#### GetOffset()  \n  \nGets the resulting offset after applying the [BaseConnection.SourceOffsetMode](#baseconnection-class#sourceoffsetmode).  \n  \n```csharp  \nprotected virtual ValueTuple<Vector, Vector> GetOffset();  \n```  \n  \n**Returns**  \n  \n[ValueTuple<Vector, Vector>](https://docs.microsoft.com/en-us/dotnet/api/System.ValueTuple)  \n  \n#### GetTextPosition(FormattedText, Point, Point)  \n  \n```csharp  \nprotected virtual Point GetTextPosition(FormattedText text, Point source, Point target);  \n```  \n  \n**Parameters**  \n  \n`text` [FormattedText](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Media.FormattedText)  \n  \n`source` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n`target` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n**Returns**  \n  \n[Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n#### OnMouseDown(MouseButtonEventArgs)  \n  \n```csharp  \nprotected override void OnMouseDown(MouseButtonEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [MouseButtonEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.MouseButtonEventArgs)  \n  \n#### OnMouseUp(MouseButtonEventArgs)  \n  \n```csharp  \nprotected override void OnMouseUp(MouseButtonEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [MouseButtonEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.MouseButtonEventArgs)  \n  \n#### OnRender(DrawingContext)  \n  \n```csharp  \nprotected override void OnRender(DrawingContext drawingContext);  \n```  \n  \n**Parameters**  \n  \n`drawingContext` [DrawingContext](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Media.DrawingContext)  \n  \n#### SetIsSelectable(UIElement, Boolean)  \n  \n```csharp  \npublic static void SetIsSelectable(UIElement elem, bool value);  \n```  \n  \n**Parameters**  \n  \n`elem` [UIElement](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.UIElement)  \n  \n`value` [Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean)  \n  \n#### SetIsSelected(UIElement, Boolean)  \n  \n```csharp  \npublic static void SetIsSelected(UIElement elem, bool value);  \n```  \n  \n**Parameters**  \n  \n`elem` [UIElement](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.UIElement)  \n  \n`value` [Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean)  \n  \n#### StartAnimation(Double)  \n  \nStarts animating the directional arrows.  \n  \n```csharp  \npublic void StartAnimation(double duration = 1.5d);  \n```  \n  \n**Parameters**  \n  \n`duration` [Double](https://docs.microsoft.com/en-us/dotnet/api/System.Double): The duration for moving an arrowhead from [BaseConnection.Source](#baseconnection-class#source) to [BaseConnection.Target](#baseconnection-class#target).  \n  \n#### StopAnimation()  \n  \nStops the animation started by Nodify.BaseConnection.StartAnimation(System.Double)  \n  \n```csharp  \npublic void StopAnimation();  \n```  \n  \n### Events  \n  \n#### Disconnect  \n  \nTriggered by the Nodify.EditorGestures.ConnectionGestures.Disconnect gesture.  \n  \n```csharp  \npublic event ConnectionEventHandler Disconnect;  \n```  \n  \n**Event Type**  \n  \n[ConnectionEventHandler](#connectioneventhandler-delegate)  \n  \n#### Split  \n  \nTriggered by the Nodify.EditorGestures.ConnectionGestures.Split gesture.  \n  \n```csharp  \npublic event ConnectionEventHandler Split;  \n```  \n  \n**Event Type**  \n  \n[ConnectionEventHandler](#connectioneventhandler-delegate)  \n  \n## BoxValue Class  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**Inheritance:** [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object) → [BoxValue](#boxvalue-class)  \n  \n```csharp  \npublic static class BoxValue  \n```  \n  \n### Fields  \n  \n#### ArrowSize  \n  \n```csharp  \npublic static object ArrowSize;  \n```  \n  \n**Field Value**  \n  \n[Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object)  \n  \n#### ConnectionOffset  \n  \n```csharp  \npublic static object ConnectionOffset;  \n```  \n  \n**Field Value**  \n  \n[Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object)  \n  \n#### Double0  \n  \n```csharp  \npublic static object Double0;  \n```  \n  \n**Field Value**  \n  \n[Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object)  \n  \n#### Double1  \n  \n```csharp  \npublic static object Double1;  \n```  \n  \n**Field Value**  \n  \n[Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object)  \n  \n#### Double1000  \n  \n```csharp  \npublic static object Double1000;  \n```  \n  \n**Field Value**  \n  \n[Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object)  \n  \n#### Double2  \n  \n```csharp  \npublic static object Double2;  \n```  \n  \n**Field Value**  \n  \n[Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object)  \n  \n#### Double45  \n  \n```csharp  \npublic static object Double45;  \n```  \n  \n**Field Value**  \n  \n[Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object)  \n  \n#### Double5  \n  \n```csharp  \npublic static object Double5;  \n```  \n  \n**Field Value**  \n  \n[Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object)  \n  \n#### DoubleHalf  \n  \n```csharp  \npublic static object DoubleHalf;  \n```  \n  \n**Field Value**  \n  \n[Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object)  \n  \n#### False  \n  \n```csharp  \npublic static object False;  \n```  \n  \n**Field Value**  \n  \n[Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object)  \n  \n#### Int0  \n  \n```csharp  \npublic static object Int0;  \n```  \n  \n**Field Value**  \n  \n[Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object)  \n  \n#### Int1  \n  \n```csharp  \npublic static object Int1;  \n```  \n  \n**Field Value**  \n  \n[Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object)  \n  \n#### Point  \n  \n```csharp  \npublic static object Point;  \n```  \n  \n**Field Value**  \n  \n[Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object)  \n  \n#### Rect  \n  \n```csharp  \npublic static object Rect;  \n```  \n  \n**Field Value**  \n  \n[Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object)  \n  \n#### Size  \n  \n```csharp  \npublic static object Size;  \n```  \n  \n**Field Value**  \n  \n[Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object)  \n  \n#### Thickness2  \n  \n```csharp  \npublic static object Thickness2;  \n```  \n  \n**Field Value**  \n  \n[Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object)  \n  \n#### True  \n  \n```csharp  \npublic static object True;  \n```  \n  \n**Field Value**  \n  \n[Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object)  \n  \n#### UInt0  \n  \n```csharp  \npublic static object UInt0;  \n```  \n  \n**Field Value**  \n  \n[Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object)  \n  \n#### UInt1  \n  \n```csharp  \npublic static object UInt1;  \n```  \n  \n**Field Value**  \n  \n[Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object)  \n  \n## CircuitConnection Class  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**Inheritance:** [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object) → [DispatcherObject](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Threading.DispatcherObject) → [DependencyObject](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.DependencyObject) → [Visual](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Media.Visual) → [UIElement](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.UIElement) → [FrameworkElement](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.FrameworkElement) → [Shape](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Shapes.Shape) → [BaseConnection](#baseconnection-class) → [LineConnection](#lineconnection-class) → [CircuitConnection](#circuitconnection-class)  \n  \nRepresents a line that is controlled by an angle.  \n  \n```csharp  \npublic class CircuitConnection : LineConnection  \n```  \n  \n### Constructors  \n  \n#### CircuitConnection()  \n  \n```csharp  \npublic CircuitConnection();  \n```  \n  \n### Fields  \n  \n#### Degrees  \n  \n```csharp  \nprotected const double Degrees = 0.017453292519943295d;  \n```  \n  \n**Field Value**  \n  \n[Double](https://docs.microsoft.com/en-us/dotnet/api/System.Double)  \n  \n### Properties  \n  \n#### Angle  \n  \nThe angle of the connection in degrees.  \n  \n```csharp  \npublic double Angle { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Double](https://docs.microsoft.com/en-us/dotnet/api/System.Double)  \n  \n### Methods  \n  \n#### DrawDirectionalArrowsGeometry(StreamGeometryContext, Point, Point)  \n  \n```csharp  \nprotected override void DrawDirectionalArrowsGeometry(StreamGeometryContext context, Point source, Point target);  \n```  \n  \n**Parameters**  \n  \n`context` [StreamGeometryContext](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Media.StreamGeometryContext)  \n  \n`source` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n`target` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n#### DrawLineGeometry(StreamGeometryContext, Point, Point)  \n  \n```csharp  \nprotected override ValueTuple<ValueTuple<Point, Point>, ValueTuple<Point, Point>> DrawLineGeometry(StreamGeometryContext context, Point source, Point target);  \n```  \n  \n**Parameters**  \n  \n`context` [StreamGeometryContext](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Media.StreamGeometryContext)  \n  \n`source` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n`target` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n**Returns**  \n  \n[ValueTuple<ValueTuple<Point, Point>, ValueTuple<Point, Point>>](https://docs.microsoft.com/en-us/dotnet/api/System.ValueTuple)  \n  \n#### GetTextPosition(FormattedText, Point, Point)  \n  \n```csharp  \nprotected override Point GetTextPosition(FormattedText text, Point source, Point target);  \n```  \n  \n**Parameters**  \n  \n`text` [FormattedText](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Media.FormattedText)  \n  \n`source` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n`target` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n**Returns**  \n  \n[Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n## Connection Class  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**Inheritance:** [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object) → [DispatcherObject](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Threading.DispatcherObject) → [DependencyObject](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.DependencyObject) → [Visual](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Media.Visual) → [UIElement](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.UIElement) → [FrameworkElement](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.FrameworkElement) → [Shape](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Shapes.Shape) → [BaseConnection](#baseconnection-class) → [Connection](#connection-class)  \n  \n**References:** [Connector](#connector-class), [NodifyEditor](#nodifyeditor-class)  \n  \nRepresents a cubic bezier curve.  \n  \n```csharp  \npublic class Connection : BaseConnection  \n```  \n  \n### Constructors  \n  \n#### Connection()  \n  \n```csharp  \npublic Connection();  \n```  \n  \n### Methods  \n  \n#### DrawDirectionalArrowsGeometry(StreamGeometryContext, Point, Point)  \n  \n```csharp  \nprotected override void DrawDirectionalArrowsGeometry(StreamGeometryContext context, Point source, Point target);  \n```  \n  \n**Parameters**  \n  \n`context` [StreamGeometryContext](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Media.StreamGeometryContext)  \n  \n`source` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n`target` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n#### DrawLineGeometry(StreamGeometryContext, Point, Point)  \n  \n```csharp  \nprotected override ValueTuple<ValueTuple<Point, Point>, ValueTuple<Point, Point>> DrawLineGeometry(StreamGeometryContext context, Point source, Point target);  \n```  \n  \n**Parameters**  \n  \n`context` [StreamGeometryContext](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Media.StreamGeometryContext)  \n  \n`source` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n`target` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n**Returns**  \n  \n[ValueTuple<ValueTuple<Point, Point>, ValueTuple<Point, Point>>](https://docs.microsoft.com/en-us/dotnet/api/System.ValueTuple)  \n  \n#### GetTextPosition(FormattedText, Point, Point)  \n  \n```csharp  \nprotected override Point GetTextPosition(FormattedText text, Point source, Point target);  \n```  \n  \n**Parameters**  \n  \n`text` [FormattedText](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Media.FormattedText)  \n  \n`source` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n`target` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n**Returns**  \n  \n[Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n#### InterpolateCubicBezier(Point, Point, Point, Point, Double)  \n  \n```csharp  \nprotected static Point InterpolateCubicBezier(Point P0, Point P1, Point P2, Point P3, double t);  \n```  \n  \n**Parameters**  \n  \n`P0` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n`P1` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n`P2` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n`P3` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n`t` [Double](https://docs.microsoft.com/en-us/dotnet/api/System.Double)  \n  \n**Returns**  \n  \n[Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n## ConnectionDirection Enum  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**References:** [BaseConnection](#baseconnection-class), [LineConnection](#lineconnection-class), [PendingConnection](#pendingconnection-class)  \n  \nThe direction in which a connection is oriented.  \n  \n```csharp  \npublic enum ConnectionDirection  \n```  \n  \n### Fields  \n  \n#### Backward  \n  \nFrom [BaseConnection.Target](#baseconnection-class#target) to [BaseConnection.Source](#baseconnection-class#source).  \n  \n```csharp  \nBackward = 1;  \n```  \n  \n#### Forward  \n  \nFrom [BaseConnection.Source](#baseconnection-class#source) to [BaseConnection.Target](#baseconnection-class#target).  \n  \n```csharp  \nForward = 0;  \n```  \n  \n## ConnectionEventArgs Class  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**Inheritance:** [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object) → [EventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.EventArgs) → [RoutedEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.RoutedEventArgs) → [ConnectionEventArgs](#connectioneventargs-class)  \n  \n**References:** [BaseConnection](#baseconnection-class), [ConnectionEventHandler](#connectioneventhandler-delegate)  \n  \nProvides data for [BaseConnection](#baseconnection-class) related routed events.  \n  \n```csharp  \npublic class ConnectionEventArgs : RoutedEventArgs  \n```  \n  \n### Constructors  \n  \n#### ConnectionEventArgs(Object)  \n  \nInitializes a new instance of the [ConnectionEventArgs](#connectioneventargs-class) class using the specified [ConnectionEventArgs.Connection](#connectioneventargs-class#connection).  \n  \n```csharp  \npublic ConnectionEventArgs(object connection);  \n```  \n  \n**Parameters**  \n  \n`connection` [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object): The [FrameworkElement.DataContext](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.FrameworkElement.datacontext) of a related [BaseConnection](#baseconnection-class).  \n  \n### Properties  \n  \n#### Connection  \n  \nGets the [FrameworkElement.DataContext](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.FrameworkElement.datacontext) of the [BaseConnection](#baseconnection-class) associated with this event.  \n  \n```csharp  \npublic object Connection { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object)  \n  \n#### SplitLocation  \n  \nGets or sets the location where the connection should be split.  \n  \n```csharp  \npublic Point SplitLocation { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n### Methods  \n  \n#### InvokeEventHandler(Delegate, Object)  \n  \n```csharp  \nprotected override void InvokeEventHandler(Delegate genericHandler, object genericTarget);  \n```  \n  \n**Parameters**  \n  \n`genericHandler` [Delegate](https://docs.microsoft.com/en-us/dotnet/api/System.Delegate)  \n  \n`genericTarget` [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object)  \n  \n## ConnectionEventHandler Delegate  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**Inheritance:** [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object) → [Delegate](https://docs.microsoft.com/en-us/dotnet/api/System.Delegate) → [MulticastDelegate](https://docs.microsoft.com/en-us/dotnet/api/System.MulticastDelegate) → [ConnectionEventHandler](#connectioneventhandler-delegate)  \n  \n**References:** [BaseConnection](#baseconnection-class), [ConnectionEventArgs](#connectioneventargs-class)  \n  \nRepresents the method that will handle [BaseConnection](#baseconnection-class) related routed events.  \n  \n```csharp  \npublic delegate void ConnectionEventHandler(object sender, ConnectionEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`sender` [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object): The object where the event handler is attached.  \n  \n`e` [ConnectionEventArgs](#connectioneventargs-class): The event data.  \n  \n## ConnectionGestures Class  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**Inheritance:** [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object) → [ConnectionGestures](#connectiongestures-class)  \n  \n**References:** [EditorGestures](#editorgestures-class), [InputGestureRef](#inputgestureref-class), [SelectionGestures](#selectiongestures-class)  \n  \n```csharp  \npublic class ConnectionGestures  \n```  \n  \n### Constructors  \n  \n#### ConnectionGestures()  \n  \n```csharp  \npublic ConnectionGestures();  \n```  \n  \n### Properties  \n  \n#### Disconnect  \n  \n```csharp  \npublic InputGestureRef Disconnect { get; set; }  \n```  \n  \n**Property Value**  \n  \n[InputGestureRef](#inputgestureref-class)  \n  \n#### Selection  \n  \n```csharp  \npublic SelectionGestures Selection { get; set; }  \n```  \n  \n**Property Value**  \n  \n[SelectionGestures](#selectiongestures-class)  \n  \n#### Split  \n  \n```csharp  \npublic InputGestureRef Split { get; set; }  \n```  \n  \n**Property Value**  \n  \n[InputGestureRef](#inputgestureref-class)  \n  \n### Methods  \n  \n#### Apply(ConnectionGestures)  \n  \n```csharp  \npublic void Apply(ConnectionGestures gestures);  \n```  \n  \n**Parameters**  \n  \n`gestures` [ConnectionGestures](#connectiongestures-class)  \n  \n## ConnectionOffsetMode Enum  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**References:** [BaseConnection](#baseconnection-class)  \n  \nSpecifies the offset type that can be applied to a [BaseConnection](#baseconnection-class) using the [BaseConnection.SourceOffset](#baseconnection-class#sourceoffset) and the [BaseConnection.TargetOffset](#baseconnection-class#targetoffset) values.  \n  \n```csharp  \npublic enum ConnectionOffsetMode  \n```  \n  \n### Fields  \n  \n#### Circle  \n  \nThe offset is applied in a circle around the point.  \n  \n```csharp  \nCircle = 1;  \n```  \n  \n#### Edge  \n  \nThe offset is applied in a rectangle shape around the point, perpendicular to the edges.  \n  \n```csharp  \nEdge = 3;  \n```  \n  \n#### None  \n  \nNo offset applied.  \n  \n```csharp  \nNone = 0;  \n```  \n  \n#### Rectangle  \n  \nThe offset is applied in a rectangle shape around the point.  \n  \n```csharp  \nRectangle = 2;  \n```  \n  \n#### Static  \n  \nThe offset is applied as a fixed margin.  \n  \n```csharp  \nStatic = 4;  \n```  \n  \n## Connector Class  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**Inheritance:** [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object) → [DispatcherObject](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Threading.DispatcherObject) → [DependencyObject](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.DependencyObject) → [Visual](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Media.Visual) → [UIElement](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.UIElement) → [FrameworkElement](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.FrameworkElement) → [Control](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Controls.Control) → [Connector](#connector-class)  \n  \n**Derived:** [NodeInput](#nodeinput-class), [NodeOutput](#nodeoutput-class), [StateNode](#statenode-class)  \n  \n**References:** [Connection](#connection-class), [ConnectorEventArgs](#connectoreventargs-class), [ConnectorEventHandler](#connectoreventhandler-delegate), [ItemContainer](#itemcontainer-class), [KnotNode](#knotnode-class), [Node](#node-class), [NodifyEditor](#nodifyeditor-class), [PendingConnection](#pendingconnection-class), [PendingConnectionEventArgs](#pendingconnectioneventargs-class), [PendingConnectionEventHandler](#pendingconnectioneventhandler-delegate)  \n  \nRepresents a connector control that can start and complete a [PendingConnection](#pendingconnection-class).\n            Has a [Connector.ElementConnector](#connector-class#elementconnector) that the [Connector.Anchor](#connector-class#anchor) is calculated from for the [PendingConnection](#pendingconnection-class). Center of this control is used if missing.  \n  \n```csharp  \npublic class Connector : Control  \n```  \n  \n### Constructors  \n  \n#### Connector()  \n  \n```csharp  \npublic Connector();  \n```  \n  \n### Fields  \n  \n#### ElementConnector  \n  \n```csharp  \nprotected const string ElementConnector = \"PART_Connector\";  \n```  \n  \n**Field Value**  \n  \n[String](https://docs.microsoft.com/en-us/dotnet/api/System.String)  \n  \n#### EnableOptimizations  \n  \nGets or sets if [Connector](#connector-class)s should enable optimizations based on [Connector.OptimizeSafeZone](#connector-class#optimizesafezone) and [Connector.OptimizeMinimumSelectedItems](#connector-class#optimizeminimumselecteditems).  \n  \n```csharp  \npublic static bool EnableOptimizations;  \n```  \n  \n**Field Value**  \n  \n[Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean)  \n  \n#### OptimizeMinimumSelectedItems  \n  \nGets or sets the minimum selected items needed to trigger optimizations when outside of the [Connector.OptimizeSafeZone](#connector-class#optimizesafezone).  \n  \n```csharp  \npublic static uint OptimizeMinimumSelectedItems;  \n```  \n  \n**Field Value**  \n  \n[UInt32](https://docs.microsoft.com/en-us/dotnet/api/System.UInt32)  \n  \n#### OptimizeSafeZone  \n  \nGets or sets the safe zone outside the editor's viewport that will not trigger optimizations.  \n  \n```csharp  \npublic static double OptimizeSafeZone;  \n```  \n  \n**Field Value**  \n  \n[Double](https://docs.microsoft.com/en-us/dotnet/api/System.Double)  \n  \n### Properties  \n  \n#### AllowPendingConnectionCancellation  \n  \nGets or sets whether cancelling a pending connection is allowed.  \n  \n```csharp  \npublic static bool AllowPendingConnectionCancellation { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean)  \n  \n#### Anchor  \n  \nGets the location where [Connection](#connection-class)s can be attached to. \n            Bind with System.Windows.Data.BindingMode.OneWayToSource  \n  \n```csharp  \npublic Point Anchor { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n#### Container  \n  \nGets the [ItemContainer](#itemcontainer-class) that contains this [Connector](#connector-class).  \n  \n```csharp  \nprotected ItemContainer Container { get; set; }  \n```  \n  \n**Property Value**  \n  \n[ItemContainer](#itemcontainer-class)  \n  \n#### DisconnectCommand  \n  \nInvoked if the [Connector.Disconnect](#connector-class#disconnect) event is not handled.\n            Parameter is the [FrameworkElement.DataContext](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.FrameworkElement.datacontext) of this control.  \n  \n```csharp  \npublic ICommand DisconnectCommand { get; set; }  \n```  \n  \n**Property Value**  \n  \n[ICommand](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.ICommand)  \n  \n#### EnableStickyConnections  \n  \nGets or sets whether the connection should be completed in two steps.  \n  \n```csharp  \npublic static bool EnableStickyConnections { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean)  \n  \n#### IsConnected  \n  \nIf this is set to false, the [Connector.Disconnect](#connector-class#disconnect) event will not be invoked and the connector will stop updating its [Connector.Anchor](#connector-class#anchor) when moved, resized etc.  \n  \n```csharp  \npublic bool IsConnected { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean)  \n  \n#### IsPendingConnection  \n  \nGets a value that indicates whether a [PendingConnection](#pendingconnection-class) is in progress for this [Connector](#connector-class).  \n  \n```csharp  \npublic bool IsPendingConnection { get; protected set; }  \n```  \n  \n**Property Value**  \n  \n[Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean)  \n  \n#### Thumb  \n  \nGets the [FrameworkElement](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.FrameworkElement) used to calculate the [Connector.Anchor](#connector-class#anchor).  \n  \n```csharp  \nprotected FrameworkElement Thumb { get; set; }  \n```  \n  \n**Property Value**  \n  \n[FrameworkElement](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.FrameworkElement)  \n  \n### Methods  \n  \n#### OnApplyTemplate()  \n  \n```csharp  \npublic override void OnApplyTemplate();  \n```  \n  \n#### OnConnectorDrag(Vector)  \n  \n```csharp  \nprotected virtual void OnConnectorDrag(Vector offset);  \n```  \n  \n**Parameters**  \n  \n`offset` [Vector](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Vector)  \n  \n#### OnConnectorDragCompleted(Boolean)  \n  \n```csharp  \nprotected virtual void OnConnectorDragCompleted(bool cancel = false);  \n```  \n  \n**Parameters**  \n  \n`cancel` [Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean)  \n  \n#### OnConnectorDragStarted()  \n  \n```csharp  \nprotected virtual void OnConnectorDragStarted();  \n```  \n  \n#### OnDisconnect()  \n  \n```csharp  \nprotected virtual void OnDisconnect();  \n```  \n  \n#### OnKeyUp(KeyEventArgs)  \n  \n```csharp  \nprotected override void OnKeyUp(KeyEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [KeyEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.KeyEventArgs)  \n  \n#### OnLostMouseCapture(MouseEventArgs)  \n  \n```csharp  \nprotected override void OnLostMouseCapture(MouseEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [MouseEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.MouseEventArgs)  \n  \n#### OnMouseDown(MouseButtonEventArgs)  \n  \n```csharp  \nprotected override void OnMouseDown(MouseButtonEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [MouseButtonEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.MouseButtonEventArgs)  \n  \n#### OnMouseMove(MouseEventArgs)  \n  \n```csharp  \nprotected override void OnMouseMove(MouseEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [MouseEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.MouseEventArgs)  \n  \n#### OnMouseUp(MouseButtonEventArgs)  \n  \n```csharp  \nprotected override void OnMouseUp(MouseButtonEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [MouseButtonEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.MouseButtonEventArgs)  \n  \n#### OnRenderSizeChanged(SizeChangedInfo)  \n  \n```csharp  \nprotected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo);  \n```  \n  \n**Parameters**  \n  \n`sizeInfo` [SizeChangedInfo](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.SizeChangedInfo)  \n  \n#### UpdateAnchor(Point)  \n  \nUpdates the [Connector.Anchor](#connector-class#anchor) relative to a location. (usually [Connector.Container](#connector-class#container)'s location)  \n  \n```csharp  \nprotected void UpdateAnchor(Point location);  \n```  \n  \n**Parameters**  \n  \n`location` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point): The relative location  \n  \n#### UpdateAnchor()  \n  \nUpdates the [Connector.Anchor](#connector-class#anchor) based on [Connector.Container](#connector-class#container)'s location.  \n  \n```csharp  \npublic void UpdateAnchor();  \n```  \n  \n#### UpdateAnchorOptimized(Point)  \n  \nUpdates the [Connector.Anchor](#connector-class#anchor) and applies optimizations if needed based on [Connector.EnableOptimizations](#connector-class#enableoptimizations) flag  \n  \n```csharp  \nprotected void UpdateAnchorOptimized(Point location);  \n```  \n  \n**Parameters**  \n  \n`location` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n### Events  \n  \n#### Disconnect  \n  \nTriggered by the Nodify.EditorGestures.ConnectorGestures.Disconnect gesture.  \n  \n```csharp  \npublic event ConnectorEventHandler Disconnect;  \n```  \n  \n**Event Type**  \n  \n[ConnectorEventHandler](#connectoreventhandler-delegate)  \n  \n#### PendingConnectionCompleted  \n  \nTriggered by the Nodify.EditorGestures.ConnectorGestures.Connect gesture.  \n  \n```csharp  \npublic event PendingConnectionEventHandler PendingConnectionCompleted;  \n```  \n  \n**Event Type**  \n  \n[PendingConnectionEventHandler](#pendingconnectioneventhandler-delegate)  \n  \n#### PendingConnectionDrag  \n  \nOccurs when the mouse is changing position and the [Connector](#connector-class) has mouse capture.  \n  \n```csharp  \npublic event PendingConnectionEventHandler PendingConnectionDrag;  \n```  \n  \n**Event Type**  \n  \n[PendingConnectionEventHandler](#pendingconnectioneventhandler-delegate)  \n  \n#### PendingConnectionStarted  \n  \nTriggered by the Nodify.EditorGestures.ConnectorGestures.Connect gesture.  \n  \n```csharp  \npublic event PendingConnectionEventHandler PendingConnectionStarted;  \n```  \n  \n**Event Type**  \n  \n[PendingConnectionEventHandler](#pendingconnectioneventhandler-delegate)  \n  \n## ConnectorEventArgs Class  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**Inheritance:** [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object) → [EventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.EventArgs) → [RoutedEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.RoutedEventArgs) → [ConnectorEventArgs](#connectoreventargs-class)  \n  \n**References:** [Connector](#connector-class), [ConnectorEventHandler](#connectoreventhandler-delegate)  \n  \nProvides data for [Connector](#connector-class) related routed events.  \n  \n```csharp  \npublic class ConnectorEventArgs : RoutedEventArgs  \n```  \n  \n### Constructors  \n  \n#### ConnectorEventArgs(Object)  \n  \nInitializes a new instance of the [ConnectorEventArgs](#connectoreventargs-class) class using the specified [ConnectorEventArgs.Connector](#connectoreventargs-class#connector).  \n  \n```csharp  \npublic ConnectorEventArgs(object connector);  \n```  \n  \n**Parameters**  \n  \n`connector` [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object): The [FrameworkElement.DataContext](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.FrameworkElement.datacontext) of a related [Connector](#connector-class).  \n  \n### Properties  \n  \n#### Anchor  \n  \nGets or sets the [Connector.Anchor](#connector-class#anchor) of the [Connector](#connector-class) associated with this event.  \n  \n```csharp  \npublic Point Anchor { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n#### Connector  \n  \nGets the [FrameworkElement.DataContext](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.FrameworkElement.datacontext) of the [Connector](#connector-class) associated with this event.  \n  \n```csharp  \npublic object Connector { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object)  \n  \n### Methods  \n  \n#### InvokeEventHandler(Delegate, Object)  \n  \n```csharp  \nprotected override void InvokeEventHandler(Delegate genericHandler, object genericTarget);  \n```  \n  \n**Parameters**  \n  \n`genericHandler` [Delegate](https://docs.microsoft.com/en-us/dotnet/api/System.Delegate)  \n  \n`genericTarget` [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object)  \n  \n## ConnectorEventHandler Delegate  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**Inheritance:** [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object) → [Delegate](https://docs.microsoft.com/en-us/dotnet/api/System.Delegate) → [MulticastDelegate](https://docs.microsoft.com/en-us/dotnet/api/System.MulticastDelegate) → [ConnectorEventHandler](#connectoreventhandler-delegate)  \n  \n**References:** [Connector](#connector-class), [ConnectorEventArgs](#connectoreventargs-class)  \n  \nRepresents the method that will handle [Connector](#connector-class) related routed events.  \n  \n```csharp  \npublic delegate void ConnectorEventHandler(object sender, ConnectorEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`sender` [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object): The object where the event handler is attached.  \n  \n`e` [ConnectorEventArgs](#connectoreventargs-class): The event data.  \n  \n## ConnectorGestures Class  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**Inheritance:** [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object) → [ConnectorGestures](#connectorgestures-class)  \n  \n**References:** [EditorGestures](#editorgestures-class), [InputGestureRef](#inputgestureref-class)  \n  \n```csharp  \npublic class ConnectorGestures  \n```  \n  \n### Constructors  \n  \n#### ConnectorGestures()  \n  \n```csharp  \npublic ConnectorGestures();  \n```  \n  \n### Properties  \n  \n#### CancelAction  \n  \n```csharp  \npublic InputGestureRef CancelAction { get; set; }  \n```  \n  \n**Property Value**  \n  \n[InputGestureRef](#inputgestureref-class)  \n  \n#### Connect  \n  \n```csharp  \npublic InputGestureRef Connect { get; set; }  \n```  \n  \n**Property Value**  \n  \n[InputGestureRef](#inputgestureref-class)  \n  \n#### Disconnect  \n  \n```csharp  \npublic InputGestureRef Disconnect { get; set; }  \n```  \n  \n**Property Value**  \n  \n[InputGestureRef](#inputgestureref-class)  \n  \n### Methods  \n  \n#### Apply(ConnectorGestures)  \n  \n```csharp  \npublic void Apply(ConnectorGestures gestures);  \n```  \n  \n**Parameters**  \n  \n`gestures` [ConnectorGestures](#connectorgestures-class)  \n  \n## ConnectorPosition Enum  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**References:** [StepConnection](#stepconnection-class)  \n  \n```csharp  \npublic enum ConnectorPosition  \n```  \n  \n### Fields  \n  \n#### Bottom  \n  \n```csharp  \nBottom = 2;  \n```  \n  \n#### Left  \n  \n```csharp  \nLeft = 1;  \n```  \n  \n#### Right  \n  \n```csharp  \nRight = 3;  \n```  \n  \n#### Top  \n  \n```csharp  \nTop = 0;  \n```  \n  \n## ContainerDefaultState Class  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**Inheritance:** [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object) → [ContainerState](#containerstate-class) → [ContainerDefaultState](#containerdefaultstate-class)  \n  \n**References:** [ItemContainer](#itemcontainer-class)  \n  \nThe default state of the [ItemContainer](#itemcontainer-class).  \n  \n```csharp  \npublic class ContainerDefaultState : ContainerState  \n```  \n  \n### Constructors  \n  \n#### ContainerDefaultState(ItemContainer)  \n  \nCreates a new instance of the [ContainerDefaultState](#containerdefaultstate-class).  \n  \n```csharp  \npublic ContainerDefaultState(ItemContainer container);  \n```  \n  \n**Parameters**  \n  \n`container` [ItemContainer](#itemcontainer-class): The owner of the state.  \n  \n### Methods  \n  \n#### HandleMouseDown(MouseButtonEventArgs)  \n  \n```csharp  \npublic override void HandleMouseDown(MouseButtonEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [MouseButtonEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.MouseButtonEventArgs)  \n  \n#### HandleMouseMove(MouseEventArgs)  \n  \n```csharp  \npublic override void HandleMouseMove(MouseEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [MouseEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.MouseEventArgs)  \n  \n#### HandleMouseUp(MouseButtonEventArgs)  \n  \n```csharp  \npublic override void HandleMouseUp(MouseButtonEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [MouseButtonEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.MouseButtonEventArgs)  \n  \n#### ReEnter(ContainerState)  \n  \n```csharp  \npublic override void ReEnter(ContainerState from);  \n```  \n  \n**Parameters**  \n  \n`from` [ContainerState](#containerstate-class)  \n  \n## ContainerDraggingState Class  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**Inheritance:** [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object) → [ContainerState](#containerstate-class) → [ContainerDraggingState](#containerdraggingstate-class)  \n  \n**References:** [ItemContainer](#itemcontainer-class)  \n  \nDragging state of the container.  \n  \n```csharp  \npublic class ContainerDraggingState : ContainerState  \n```  \n  \n### Constructors  \n  \n#### ContainerDraggingState(ItemContainer)  \n  \nConstructs an instance of the [ContainerDraggingState](#containerdraggingstate-class) state.  \n  \n```csharp  \npublic ContainerDraggingState(ItemContainer container);  \n```  \n  \n**Parameters**  \n  \n`container` [ItemContainer](#itemcontainer-class): The owner of the state.  \n  \n### Properties  \n  \n#### Canceled  \n  \n```csharp  \npublic bool Canceled { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean)  \n  \n### Methods  \n  \n#### Enter(ContainerState)  \n  \n```csharp  \npublic override void Enter(ContainerState from);  \n```  \n  \n**Parameters**  \n  \n`from` [ContainerState](#containerstate-class)  \n  \n#### Exit()  \n  \n```csharp  \npublic override void Exit();  \n```  \n  \n#### HandleKeyUp(KeyEventArgs)  \n  \n```csharp  \npublic override void HandleKeyUp(KeyEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [KeyEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.KeyEventArgs)  \n  \n#### HandleMouseMove(MouseEventArgs)  \n  \n```csharp  \npublic override void HandleMouseMove(MouseEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [MouseEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.MouseEventArgs)  \n  \n#### HandleMouseUp(MouseButtonEventArgs)  \n  \n```csharp  \npublic override void HandleMouseUp(MouseButtonEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [MouseButtonEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.MouseButtonEventArgs)  \n  \n## ContainerState Class  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**Inheritance:** [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object) → [ContainerState](#containerstate-class)  \n  \n**Derived:** [ContainerDefaultState](#containerdefaultstate-class), [ContainerDraggingState](#containerdraggingstate-class)  \n  \n**References:** [ContainerDefaultState](#containerdefaultstate-class), [ContainerDraggingState](#containerdraggingstate-class), [ItemContainer](#itemcontainer-class), [NodifyEditor](#nodifyeditor-class)  \n  \nThe base class for container states.  \n  \n```csharp  \npublic abstract class ContainerState  \n```  \n  \n### Constructors  \n  \n#### ContainerState(ItemContainer)  \n  \nConstructs a new [ContainerState](#containerstate-class).  \n  \n```csharp  \npublic ContainerState(ItemContainer container);  \n```  \n  \n**Parameters**  \n  \n`container` [ItemContainer](#itemcontainer-class): The owner of the state.  \n  \n### Properties  \n  \n#### Container  \n  \nThe owner of the state.  \n  \n```csharp  \nprotected ItemContainer Container { get; set; }  \n```  \n  \n**Property Value**  \n  \n[ItemContainer](#itemcontainer-class)  \n  \n#### Editor  \n  \nThe owner of the state.  \n  \n```csharp  \nprotected NodifyEditor Editor { get; set; }  \n```  \n  \n**Property Value**  \n  \n[NodifyEditor](#nodifyeditor-class)  \n  \n### Methods  \n  \n#### Enter(ContainerState)  \n  \nCalled when Nodify.ItemContainer.PushState(Nodify.ContainerState) or Nodify.ItemContainer.PopState is called.  \n  \n```csharp  \npublic virtual void Enter(ContainerState from);  \n```  \n  \n**Parameters**  \n  \n`from` [ContainerState](#containerstate-class): The state we enter from (is null for root state).  \n  \n#### Exit()  \n  \nCalled when Nodify.ItemContainer.PopState is called.  \n  \n```csharp  \npublic virtual void Exit();  \n```  \n  \n#### HandleKeyDown(KeyEventArgs)  \n  \n```csharp  \npublic virtual void HandleKeyDown(KeyEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [KeyEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.KeyEventArgs)  \n  \n#### HandleKeyUp(KeyEventArgs)  \n  \n```csharp  \npublic virtual void HandleKeyUp(KeyEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [KeyEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.KeyEventArgs)  \n  \n#### HandleMouseDown(MouseButtonEventArgs)  \n  \n```csharp  \npublic virtual void HandleMouseDown(MouseButtonEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [MouseButtonEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.MouseButtonEventArgs)  \n  \n#### HandleMouseMove(MouseEventArgs)  \n  \n```csharp  \npublic virtual void HandleMouseMove(MouseEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [MouseEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.MouseEventArgs)  \n  \n#### HandleMouseUp(MouseButtonEventArgs)  \n  \n```csharp  \npublic virtual void HandleMouseUp(MouseButtonEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [MouseButtonEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.MouseButtonEventArgs)  \n  \n#### HandleMouseWheel(MouseWheelEventArgs)  \n  \n```csharp  \npublic virtual void HandleMouseWheel(MouseWheelEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [MouseWheelEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.MouseWheelEventArgs)  \n  \n#### PopState()  \n  \nPops the current state from the stack.  \n  \n```csharp  \npublic virtual void PopState();  \n```  \n  \n#### PushState(ContainerState)  \n  \nPushes a new state into the stack.  \n  \n```csharp  \npublic virtual void PushState(ContainerState newState);  \n```  \n  \n**Parameters**  \n  \n`newState` [ContainerState](#containerstate-class): The new state.  \n  \n#### ReEnter(ContainerState)  \n  \nCalled when Nodify.ItemContainer.PopState is called.  \n  \n```csharp  \npublic virtual void ReEnter(ContainerState from);  \n```  \n  \n**Parameters**  \n  \n`from` [ContainerState](#containerstate-class): The state we re-enter from.  \n  \n## CuttingLine Class  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**Inheritance:** [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object) → [DispatcherObject](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Threading.DispatcherObject) → [DependencyObject](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.DependencyObject) → [Visual](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Media.Visual) → [UIElement](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.UIElement) → [FrameworkElement](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.FrameworkElement) → [Shape](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Shapes.Shape) → [CuttingLine](#cuttingline-class)  \n  \n**References:** [BaseConnection](#baseconnection-class), [NodifyEditor](#nodifyeditor-class)  \n  \n```csharp  \npublic class CuttingLine : Shape  \n```  \n  \n### Constructors  \n  \n#### CuttingLine()  \n  \n```csharp  \npublic CuttingLine();  \n```  \n  \n### Properties  \n  \n#### AllowCuttingCancellation  \n  \nGets or sets whether cancelling a cutting operation is allowed.  \n  \n```csharp  \npublic static bool AllowCuttingCancellation { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean)  \n  \n#### DefiningGeometry  \n  \n```csharp  \nprotected override Geometry DefiningGeometry { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Geometry](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Media.Geometry)  \n  \n#### EndPoint  \n  \nGets or sets the end point.  \n  \n```csharp  \npublic Point EndPoint { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n#### StartPoint  \n  \nGets or sets the start point.  \n  \n```csharp  \npublic Point StartPoint { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n### Methods  \n  \n#### GetIsOverElement(UIElement)  \n  \n```csharp  \npublic static bool GetIsOverElement(UIElement elem);  \n```  \n  \n**Parameters**  \n  \n`elem` [UIElement](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.UIElement)  \n  \n**Returns**  \n  \n[Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean)  \n  \n#### OnRender(DrawingContext)  \n  \n```csharp  \nprotected override void OnRender(DrawingContext drawingContext);  \n```  \n  \n**Parameters**  \n  \n`drawingContext` [DrawingContext](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Media.DrawingContext)  \n  \n#### SetIsOverElement(UIElement, Boolean)  \n  \n```csharp  \npublic static void SetIsOverElement(UIElement elem, bool value);  \n```  \n  \n**Parameters**  \n  \n`elem` [UIElement](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.UIElement)  \n  \n`value` [Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean)  \n  \n## DecoratorContainer Class  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**Inheritance:** [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object) → [DispatcherObject](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Threading.DispatcherObject) → [DependencyObject](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.DependencyObject) → [Visual](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Media.Visual) → [UIElement](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.UIElement) → [FrameworkElement](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.FrameworkElement) → [Control](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Controls.Control) → [ContentControl](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Controls.ContentControl) → [DecoratorContainer](#decoratorcontainer-class)  \n  \n**Implements:** [INodifyCanvasItem](#inodifycanvasitem-interface)  \n  \n**References:** [NodifyEditor](#nodifyeditor-class)  \n  \nThe container for all the items generated from the [NodifyEditor.Decorators](#nodifyeditor-class#decorators) collection.  \n  \n```csharp  \npublic class DecoratorContainer : ContentControl, INodifyCanvasItem  \n```  \n  \n### Constructors  \n  \n#### DecoratorContainer()  \n  \n```csharp  \npublic DecoratorContainer();  \n```  \n  \n### Properties  \n  \n#### ActualSize  \n  \nGets the actual size of this [DecoratorContainer](#decoratorcontainer-class).  \n  \n```csharp  \npublic Size ActualSize { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Size](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Size)  \n  \n#### Location  \n  \nGets or sets the location of this [DecoratorContainer](#decoratorcontainer-class) inside the NodifyEditor.DecoratorsHost.  \n  \n```csharp  \npublic virtual Point Location { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n### Methods  \n  \n#### OnLocationChanged()  \n  \nRaises the [DecoratorContainer.LocationChangedEvent](#decoratorcontainer-class#locationchangedevent).  \n  \n```csharp  \nprotected void OnLocationChanged();  \n```  \n  \n#### OnRenderSizeChanged(SizeChangedInfo)  \n  \n```csharp  \nprotected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo);  \n```  \n  \n**Parameters**  \n  \n`sizeInfo` [SizeChangedInfo](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.SizeChangedInfo)  \n  \n### Events  \n  \n#### LocationChanged  \n  \nOccurs when the [DecoratorContainer.Location](#decoratorcontainer-class#location) of this [DecoratorContainer](#decoratorcontainer-class) is changed.  \n  \n```csharp  \npublic event RoutedEventHandler LocationChanged;  \n```  \n  \n**Event Type**  \n  \n[RoutedEventHandler](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.RoutedEventHandler)  \n  \n## EditorCommands Class  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**Inheritance:** [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object) → [EditorCommands](#editorcommands-class)  \n  \n**References:** [InputGestureRef](#inputgestureref-class), [ItemContainer](#itemcontainer-class), [NodifyEditor](#nodifyeditor-class)  \n  \n```csharp  \npublic static class EditorCommands  \n```  \n  \n### Properties  \n  \n#### Align  \n  \nAligns [NodifyEditor.SelectedItems](#nodifyeditor-class#selecteditems) using the specified alignment method.\n            Parameter is of type Nodify.EditorCommands.Alignment or a string that can be converted to an alignment.  \n  \n```csharp  \npublic static RoutedUICommand Align { get; set; }  \n```  \n  \n**Property Value**  \n  \n[RoutedUICommand](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.RoutedUICommand)  \n  \n#### BringIntoView  \n  \nMoves the [NodifyEditor.ViewportLocation](#nodifyeditor-class#viewportlocation) to the specified location.\n            Parameter is a [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point) or a string that can be converted to a point.  \n  \n```csharp  \npublic static RoutedUICommand BringIntoView { get; set; }  \n```  \n  \n**Property Value**  \n  \n[RoutedUICommand](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.RoutedUICommand)  \n  \n#### FitToScreen  \n  \nScales the editor's viewport to fit all the [ItemContainer](#itemcontainer-class)s if that's possible.  \n  \n```csharp  \npublic static RoutedUICommand FitToScreen { get; set; }  \n```  \n  \n**Property Value**  \n  \n[RoutedUICommand](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.RoutedUICommand)  \n  \n#### SelectAll  \n  \nSelect all [ItemContainer](#itemcontainer-class)s in the [NodifyEditor](#nodifyeditor-class).  \n  \n```csharp  \npublic static RoutedUICommand SelectAll { get; set; }  \n```  \n  \n**Property Value**  \n  \n[RoutedUICommand](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.RoutedUICommand)  \n  \n#### ZoomIn  \n  \nZoom in relative to the editor's viewport center.  \n  \n```csharp  \npublic static RoutedUICommand ZoomIn { get; set; }  \n```  \n  \n**Property Value**  \n  \n[RoutedUICommand](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.RoutedUICommand)  \n  \n#### ZoomOut  \n  \nZoom out relative to the editor's viewport center.  \n  \n```csharp  \npublic static RoutedUICommand ZoomOut { get; set; }  \n```  \n  \n**Property Value**  \n  \n[RoutedUICommand](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.RoutedUICommand)  \n  \n## EditorCuttingState Class  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**Inheritance:** [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object) → [EditorState](#editorstate-class) → [EditorCuttingState](#editorcuttingstate-class)  \n  \n**References:** [NodifyEditor](#nodifyeditor-class)  \n  \n```csharp  \npublic class EditorCuttingState : EditorState  \n```  \n  \n### Constructors  \n  \n#### EditorCuttingState(NodifyEditor)  \n  \n```csharp  \npublic EditorCuttingState(NodifyEditor editor);  \n```  \n  \n**Parameters**  \n  \n`editor` [NodifyEditor](#nodifyeditor-class)  \n  \n### Properties  \n  \n#### Canceled  \n  \n```csharp  \npublic bool Canceled { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean)  \n  \n### Methods  \n  \n#### Enter(EditorState)  \n  \n```csharp  \npublic override void Enter(EditorState from);  \n```  \n  \n**Parameters**  \n  \n`from` [EditorState](#editorstate-class)  \n  \n#### Exit()  \n  \n```csharp  \npublic override void Exit();  \n```  \n  \n#### HandleKeyUp(KeyEventArgs)  \n  \n```csharp  \npublic override void HandleKeyUp(KeyEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [KeyEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.KeyEventArgs)  \n  \n#### HandleMouseMove(MouseEventArgs)  \n  \n```csharp  \npublic override void HandleMouseMove(MouseEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [MouseEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.MouseEventArgs)  \n  \n#### HandleMouseUp(MouseButtonEventArgs)  \n  \n```csharp  \npublic override void HandleMouseUp(MouseButtonEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [MouseButtonEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.MouseButtonEventArgs)  \n  \n## EditorDefaultState Class  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**Inheritance:** [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object) → [EditorState](#editorstate-class) → [EditorDefaultState](#editordefaultstate-class)  \n  \n**References:** [NodifyEditor](#nodifyeditor-class)  \n  \nThe default state of the editor.\n              Default State\n              \t- mouse left down  \t-> Selecting State\n              \t- mouse right down  -> Panning State\n              Selecting State\n              \t- mouse left up \t-> Default State\n              \t- mouse right down \t-> Panning State\n              Panning State\n              \t- mouse right up\t-> previous state (Selecting State or Default State)\n              \t- mouse left up\t\t-> Panning State  \n  \n```csharp  \npublic class EditorDefaultState : EditorState  \n```  \n  \n### Constructors  \n  \n#### EditorDefaultState(NodifyEditor)  \n  \nConstructs an instance of the [EditorDefaultState](#editordefaultstate-class) state.  \n  \n```csharp  \npublic EditorDefaultState(NodifyEditor editor);  \n```  \n  \n**Parameters**  \n  \n`editor` [NodifyEditor](#nodifyeditor-class): The owner of the state.  \n  \n### Methods  \n  \n#### HandleMouseDown(MouseButtonEventArgs)  \n  \n```csharp  \npublic override void HandleMouseDown(MouseButtonEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [MouseButtonEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.MouseButtonEventArgs)  \n  \n#### HandleMouseWheel(MouseWheelEventArgs)  \n  \n```csharp  \npublic override void HandleMouseWheel(MouseWheelEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [MouseWheelEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.MouseWheelEventArgs)  \n  \n## EditorGestures Class  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**Inheritance:** [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object) → [EditorGestures](#editorgestures-class)  \n  \n**References:** [ConnectionGestures](#connectiongestures-class), [ConnectorGestures](#connectorgestures-class), [GroupingNodeGestures](#groupingnodegestures-class), [ItemContainerGestures](#itemcontainergestures-class), [MinimapGestures](#minimapgestures-class), [NodifyEditor](#nodifyeditor-class), [NodifyEditorGestures](#nodifyeditorgestures-class)  \n  \nGestures used by built-in controls inside the [NodifyEditor](#nodifyeditor-class).  \n  \n```csharp  \npublic class EditorGestures  \n```  \n  \n### Constructors  \n  \n#### EditorGestures()  \n  \n```csharp  \npublic EditorGestures();  \n```  \n  \n### Fields  \n  \n#### Mappings  \n  \n```csharp  \npublic static EditorGestures Mappings;  \n```  \n  \n**Field Value**  \n  \n[EditorGestures](#editorgestures-class)  \n  \n### Properties  \n  \n#### Connection  \n  \nGestures for the connection.  \n  \n```csharp  \npublic ConnectionGestures Connection { get; set; }  \n```  \n  \n**Property Value**  \n  \n[ConnectionGestures](#connectiongestures-class)  \n  \n#### Connector  \n  \nGestures for the connector.  \n  \n```csharp  \npublic ConnectorGestures Connector { get; set; }  \n```  \n  \n**Property Value**  \n  \n[ConnectorGestures](#connectorgestures-class)  \n  \n#### Editor  \n  \nGestures for the editor.  \n  \n```csharp  \npublic NodifyEditorGestures Editor { get; set; }  \n```  \n  \n**Property Value**  \n  \n[NodifyEditorGestures](#nodifyeditorgestures-class)  \n  \n#### GroupingNode  \n  \nGestures for the grouping node.  \n  \n```csharp  \npublic GroupingNodeGestures GroupingNode { get; set; }  \n```  \n  \n**Property Value**  \n  \n[GroupingNodeGestures](#groupingnodegestures-class)  \n  \n#### ItemContainer  \n  \nGestures for the item container.  \n  \n```csharp  \npublic ItemContainerGestures ItemContainer { get; set; }  \n```  \n  \n**Property Value**  \n  \n[ItemContainerGestures](#itemcontainergestures-class)  \n  \n#### Minimap  \n  \nGestures for the minimap.  \n  \n```csharp  \npublic MinimapGestures Minimap { get; set; }  \n```  \n  \n**Property Value**  \n  \n[MinimapGestures](#minimapgestures-class)  \n  \n### Methods  \n  \n#### Apply(EditorGestures)  \n  \nCopies from the specified gestures.  \n  \n```csharp  \npublic void Apply(EditorGestures gestures);  \n```  \n  \n**Parameters**  \n  \n`gestures` [EditorGestures](#editorgestures-class): The gestures to copy.  \n  \n## EditorPanningState Class  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**Inheritance:** [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object) → [EditorState](#editorstate-class) → [EditorPanningState](#editorpanningstate-class)  \n  \n**References:** [NodifyEditor](#nodifyeditor-class)  \n  \nThe panning state of the editor.  \n  \n```csharp  \npublic class EditorPanningState : EditorState  \n```  \n  \n### Constructors  \n  \n#### EditorPanningState(NodifyEditor)  \n  \nConstructs an instance of the [EditorPanningState](#editorpanningstate-class) state.  \n  \n```csharp  \npublic EditorPanningState(NodifyEditor editor);  \n```  \n  \n**Parameters**  \n  \n`editor` [NodifyEditor](#nodifyeditor-class): The owner of the state.  \n  \n### Methods  \n  \n#### Enter(EditorState)  \n  \n```csharp  \npublic override void Enter(EditorState from);  \n```  \n  \n**Parameters**  \n  \n`from` [EditorState](#editorstate-class)  \n  \n#### Exit()  \n  \n```csharp  \npublic override void Exit();  \n```  \n  \n#### HandleMouseMove(MouseEventArgs)  \n  \n```csharp  \npublic override void HandleMouseMove(MouseEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [MouseEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.MouseEventArgs)  \n  \n#### HandleMouseUp(MouseButtonEventArgs)  \n  \n```csharp  \npublic override void HandleMouseUp(MouseButtonEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [MouseButtonEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.MouseButtonEventArgs)  \n  \n## EditorPushingItemsState Class  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**Inheritance:** [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object) → [EditorState](#editorstate-class) → [EditorPushingItemsState](#editorpushingitemsstate-class)  \n  \n**References:** [NodifyEditor](#nodifyeditor-class)  \n  \n```csharp  \npublic class EditorPushingItemsState : EditorState  \n```  \n  \n### Constructors  \n  \n#### EditorPushingItemsState(NodifyEditor)  \n  \n```csharp  \npublic EditorPushingItemsState(NodifyEditor editor);  \n```  \n  \n**Parameters**  \n  \n`editor` [NodifyEditor](#nodifyeditor-class)  \n  \n### Properties  \n  \n#### Canceled  \n  \n```csharp  \npublic bool Canceled { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean)  \n  \n### Methods  \n  \n#### Enter(EditorState)  \n  \n```csharp  \npublic override void Enter(EditorState from);  \n```  \n  \n**Parameters**  \n  \n`from` [EditorState](#editorstate-class)  \n  \n#### Exit()  \n  \n```csharp  \npublic override void Exit();  \n```  \n  \n#### HandleKeyUp(KeyEventArgs)  \n  \n```csharp  \npublic override void HandleKeyUp(KeyEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [KeyEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.KeyEventArgs)  \n  \n#### HandleMouseMove(MouseEventArgs)  \n  \n```csharp  \npublic override void HandleMouseMove(MouseEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [MouseEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.MouseEventArgs)  \n  \n#### HandleMouseUp(MouseButtonEventArgs)  \n  \n```csharp  \npublic override void HandleMouseUp(MouseButtonEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [MouseButtonEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.MouseButtonEventArgs)  \n  \n## EditorSelectingState Class  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**Inheritance:** [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object) → [EditorState](#editorstate-class) → [EditorSelectingState](#editorselectingstate-class)  \n  \n**References:** [NodifyEditor](#nodifyeditor-class), [SelectionHelper](#selectionhelper-class), [SelectionType](#selectiontype-enum)  \n  \nThe selecting state of the editor.  \n  \n```csharp  \npublic class EditorSelectingState : EditorState  \n```  \n  \n### Constructors  \n  \n#### EditorSelectingState(NodifyEditor, SelectionType)  \n  \nConstructs an instance of the [EditorSelectingState](#editorselectingstate-class) state.  \n  \n```csharp  \npublic EditorSelectingState(NodifyEditor editor, SelectionType type);  \n```  \n  \n**Parameters**  \n  \n`editor` [NodifyEditor](#nodifyeditor-class): The owner of the state.  \n  \n`type` [SelectionType](#selectiontype-enum): The selection strategy.  \n  \n### Properties  \n  \n#### Selection  \n  \nThe selection helper.  \n  \n```csharp  \nprotected SelectionHelper Selection { get; set; }  \n```  \n  \n**Property Value**  \n  \n[SelectionHelper](#selectionhelper-class)  \n  \n### Methods  \n  \n#### Enter(EditorState)  \n  \n```csharp  \npublic override void Enter(EditorState from);  \n```  \n  \n**Parameters**  \n  \n`from` [EditorState](#editorstate-class)  \n  \n#### Exit()  \n  \n```csharp  \npublic override void Exit();  \n```  \n  \n#### HandleAutoPanning(MouseEventArgs)  \n  \n```csharp  \npublic override void HandleAutoPanning(MouseEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [MouseEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.MouseEventArgs)  \n  \n#### HandleKeyUp(KeyEventArgs)  \n  \n```csharp  \npublic override void HandleKeyUp(KeyEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [KeyEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.KeyEventArgs)  \n  \n#### HandleMouseDown(MouseButtonEventArgs)  \n  \n```csharp  \npublic override void HandleMouseDown(MouseButtonEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [MouseButtonEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.MouseButtonEventArgs)  \n  \n#### HandleMouseMove(MouseEventArgs)  \n  \n```csharp  \npublic override void HandleMouseMove(MouseEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [MouseEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.MouseEventArgs)  \n  \n#### HandleMouseUp(MouseButtonEventArgs)  \n  \n```csharp  \npublic override void HandleMouseUp(MouseButtonEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [MouseButtonEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.MouseButtonEventArgs)  \n  \n## EditorState Class  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**Inheritance:** [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object) → [EditorState](#editorstate-class)  \n  \n**Derived:** [EditorCuttingState](#editorcuttingstate-class), [EditorDefaultState](#editordefaultstate-class), [EditorPanningState](#editorpanningstate-class), [EditorPushingItemsState](#editorpushingitemsstate-class), [EditorSelectingState](#editorselectingstate-class)  \n  \n**References:** [EditorCuttingState](#editorcuttingstate-class), [EditorPanningState](#editorpanningstate-class), [EditorPushingItemsState](#editorpushingitemsstate-class), [EditorSelectingState](#editorselectingstate-class), [NodifyEditor](#nodifyeditor-class)  \n  \nThe base class for editor states.  \n  \n```csharp  \npublic abstract class EditorState  \n```  \n  \n### Constructors  \n  \n#### EditorState(NodifyEditor)  \n  \nConstructs a new [EditorState](#editorstate-class).  \n  \n```csharp  \npublic EditorState(NodifyEditor editor);  \n```  \n  \n**Parameters**  \n  \n`editor` [NodifyEditor](#nodifyeditor-class): The owner of the state.  \n  \n### Properties  \n  \n#### Editor  \n  \nThe owner of the state.  \n  \n```csharp  \nprotected NodifyEditor Editor { get; set; }  \n```  \n  \n**Property Value**  \n  \n[NodifyEditor](#nodifyeditor-class)  \n  \n### Methods  \n  \n#### Enter(EditorState)  \n  \nCalled when Nodify.NodifyEditor.PushState(Nodify.EditorState) is called.  \n  \n```csharp  \npublic virtual void Enter(EditorState from);  \n```  \n  \n**Parameters**  \n  \n`from` [EditorState](#editorstate-class): The state we enter from (is null for root state).  \n  \n#### Exit()  \n  \nCalled when Nodify.NodifyEditor.PopState is called.  \n  \n```csharp  \npublic virtual void Exit();  \n```  \n  \n#### HandleAutoPanning(MouseEventArgs)  \n  \nHandles auto panning when mouse is outside the editor.  \n  \n```csharp  \npublic virtual void HandleAutoPanning(MouseEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [MouseEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.MouseEventArgs): The [MouseEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.MouseEventArgs) that contains the event data.  \n  \n#### HandleKeyDown(KeyEventArgs)  \n  \n```csharp  \npublic virtual void HandleKeyDown(KeyEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [KeyEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.KeyEventArgs)  \n  \n#### HandleKeyUp(KeyEventArgs)  \n  \n```csharp  \npublic virtual void HandleKeyUp(KeyEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [KeyEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.KeyEventArgs)  \n  \n#### HandleMouseDown(MouseButtonEventArgs)  \n  \n```csharp  \npublic virtual void HandleMouseDown(MouseButtonEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [MouseButtonEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.MouseButtonEventArgs)  \n  \n#### HandleMouseMove(MouseEventArgs)  \n  \n```csharp  \npublic virtual void HandleMouseMove(MouseEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [MouseEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.MouseEventArgs)  \n  \n#### HandleMouseUp(MouseButtonEventArgs)  \n  \n```csharp  \npublic virtual void HandleMouseUp(MouseButtonEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [MouseButtonEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.MouseButtonEventArgs)  \n  \n#### HandleMouseWheel(MouseWheelEventArgs)  \n  \n```csharp  \npublic virtual void HandleMouseWheel(MouseWheelEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [MouseWheelEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.MouseWheelEventArgs)  \n  \n#### PopState()  \n  \nPops the current state from the stack.  \n  \n```csharp  \npublic virtual void PopState();  \n```  \n  \n#### PushState(EditorState)  \n  \nPushes a new state into the stack.  \n  \n```csharp  \npublic virtual void PushState(EditorState newState);  \n```  \n  \n**Parameters**  \n  \n`newState` [EditorState](#editorstate-class): The new state.  \n  \n#### ReEnter(EditorState)  \n  \nCalled when Nodify.NodifyEditor.PopState is called.  \n  \n```csharp  \npublic virtual void ReEnter(EditorState from);  \n```  \n  \n**Parameters**  \n  \n`from` [EditorState](#editorstate-class): The state we re-enter from.  \n  \n## GeneratedInternalTypeHelper Class  \n  \n**Namespace:** XamlGeneratedNamespace  \n  \n**Assembly:** Nodify  \n  \n**Inheritance:** [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object) → [InternalTypeHelper](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Markup.InternalTypeHelper) → [GeneratedInternalTypeHelper](#generatedinternaltypehelper-class)  \n  \nGeneratedInternalTypeHelper  \n  \n```csharp  \npublic sealed class GeneratedInternalTypeHelper : InternalTypeHelper  \n```  \n  \n### Constructors  \n  \n#### GeneratedInternalTypeHelper()  \n  \n```csharp  \npublic GeneratedInternalTypeHelper();  \n```  \n  \n### Methods  \n  \n#### AddEventHandler(EventInfo, Object, Delegate)  \n  \nAddEventHandler  \n  \n```csharp  \nprotected override void AddEventHandler(EventInfo eventInfo, object target, Delegate handler);  \n```  \n  \n**Parameters**  \n  \n`eventInfo` [EventInfo](https://docs.microsoft.com/en-us/dotnet/api/System.Reflection.EventInfo)  \n  \n`target` [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object)  \n  \n`handler` [Delegate](https://docs.microsoft.com/en-us/dotnet/api/System.Delegate)  \n  \n#### CreateDelegate(Type, Object, String)  \n  \nCreateDelegate  \n  \n```csharp  \nprotected override Delegate CreateDelegate(Type delegateType, object target, string handler);  \n```  \n  \n**Parameters**  \n  \n`delegateType` [Type](https://docs.microsoft.com/en-us/dotnet/api/System.Type)  \n  \n`target` [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object)  \n  \n`handler` [String](https://docs.microsoft.com/en-us/dotnet/api/System.String)  \n  \n**Returns**  \n  \n[Delegate](https://docs.microsoft.com/en-us/dotnet/api/System.Delegate)  \n  \n#### CreateInstance(Type, CultureInfo)  \n  \nCreateInstance  \n  \n```csharp  \nprotected override object CreateInstance(Type type, CultureInfo culture);  \n```  \n  \n**Parameters**  \n  \n`type` [Type](https://docs.microsoft.com/en-us/dotnet/api/System.Type)  \n  \n`culture` [CultureInfo](https://docs.microsoft.com/en-us/dotnet/api/System.Globalization.CultureInfo)  \n  \n**Returns**  \n  \n[Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object)  \n  \n#### GetPropertyValue(PropertyInfo, Object, CultureInfo)  \n  \nGetPropertyValue  \n  \n```csharp  \nprotected override object GetPropertyValue(PropertyInfo propertyInfo, object target, CultureInfo culture);  \n```  \n  \n**Parameters**  \n  \n`propertyInfo` [PropertyInfo](https://docs.microsoft.com/en-us/dotnet/api/System.Reflection.PropertyInfo)  \n  \n`target` [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object)  \n  \n`culture` [CultureInfo](https://docs.microsoft.com/en-us/dotnet/api/System.Globalization.CultureInfo)  \n  \n**Returns**  \n  \n[Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object)  \n  \n#### SetPropertyValue(PropertyInfo, Object, Object, CultureInfo)  \n  \nSetPropertyValue  \n  \n```csharp  \nprotected override void SetPropertyValue(PropertyInfo propertyInfo, object target, object value, CultureInfo culture);  \n```  \n  \n**Parameters**  \n  \n`propertyInfo` [PropertyInfo](https://docs.microsoft.com/en-us/dotnet/api/System.Reflection.PropertyInfo)  \n  \n`target` [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object)  \n  \n`value` [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object)  \n  \n`culture` [CultureInfo](https://docs.microsoft.com/en-us/dotnet/api/System.Globalization.CultureInfo)  \n  \n## GroupingMovementMode Enum  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**References:** [GroupingNode](#groupingnode-class)  \n  \nSpecifies the possible movement modes of a [GroupingNode](#groupingnode-class).  \n  \n```csharp  \npublic enum GroupingMovementMode  \n```  \n  \n### Fields  \n  \n#### Group  \n  \nThe [GroupingNode](#groupingnode-class) will move its content when moved.  \n  \n```csharp  \nGroup = 0;  \n```  \n  \n#### Self  \n  \nThe [GroupingNode](#groupingnode-class) will not move its content when moved.  \n  \n```csharp  \nSelf = 1;  \n```  \n  \n## GroupingNode Class  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**Inheritance:** [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object) → [DispatcherObject](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Threading.DispatcherObject) → [DependencyObject](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.DependencyObject) → [Visual](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Media.Visual) → [UIElement](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.UIElement) → [FrameworkElement](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.FrameworkElement) → [Control](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Controls.Control) → [ContentControl](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Controls.ContentControl) → [HeaderedContentControl](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Controls.HeaderedContentControl) → [GroupingNode](#groupingnode-class)  \n  \n**References:** [GroupingMovementMode](#groupingmovementmode-enum), [ItemContainer](#itemcontainer-class), [NodifyEditor](#nodifyeditor-class), [ResizeEventHandler](#resizeeventhandler-delegate)  \n  \nDefines a panel with a header that groups [ItemContainer](#itemcontainer-class)s inside it and can be resized.  \n  \n```csharp  \npublic class GroupingNode : HeaderedContentControl  \n```  \n  \n### Constructors  \n  \n#### GroupingNode()  \n  \nInitializes a new instance of the [GroupingNode](#groupingnode-class) class.  \n  \n```csharp  \npublic GroupingNode();  \n```  \n  \n### Fields  \n  \n#### ContentControl  \n  \nGets the [ContentControl](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Controls.ContentControl) control of this [GroupingNode](#groupingnode-class).  \n  \n```csharp  \nprotected FrameworkElement ContentControl;  \n```  \n  \n**Field Value**  \n  \n[FrameworkElement](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.FrameworkElement)  \n  \n#### ElementContent  \n  \n```csharp  \nprotected const string ElementContent = \"PART_Content\";  \n```  \n  \n**Field Value**  \n  \n[String](https://docs.microsoft.com/en-us/dotnet/api/System.String)  \n  \n#### ElementHeader  \n  \n```csharp  \nprotected const string ElementHeader = \"PART_Header\";  \n```  \n  \n**Field Value**  \n  \n[String](https://docs.microsoft.com/en-us/dotnet/api/System.String)  \n  \n#### ElementResizeThumb  \n  \n```csharp  \nprotected const string ElementResizeThumb = \"PART_ResizeThumb\";  \n```  \n  \n**Field Value**  \n  \n[String](https://docs.microsoft.com/en-us/dotnet/api/System.String)  \n  \n#### GroupMovementBoxed  \n  \n```csharp  \nprotected static object GroupMovementBoxed;  \n```  \n  \n**Field Value**  \n  \n[Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object)  \n  \n#### HeaderControl  \n  \nGets the [HeaderedContentControl.Header](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Controls.HeaderedContentControl.header) control of this [GroupingNode](#groupingnode-class).  \n  \n```csharp  \nprotected FrameworkElement HeaderControl;  \n```  \n  \n**Field Value**  \n  \n[FrameworkElement](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.FrameworkElement)  \n  \n#### ResizeThumb  \n  \nGets the [FrameworkElement](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.FrameworkElement) used to resize this [GroupingNode](#groupingnode-class).  \n  \n```csharp  \nprotected FrameworkElement ResizeThumb;  \n```  \n  \n**Field Value**  \n  \n[FrameworkElement](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.FrameworkElement)  \n  \n### Properties  \n  \n#### ActualSize  \n  \nGets or sets the actual size of this [GroupingNode](#groupingnode-class).  \n  \n```csharp  \npublic Size ActualSize { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Size](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Size)  \n  \n#### CanResize  \n  \nGets or sets a value that indicates whether this [GroupingNode](#groupingnode-class) can be resized.  \n  \n```csharp  \npublic bool CanResize { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean)  \n  \n#### Container  \n  \nGets the [NodifyEditor](#nodifyeditor-class) that owns this [GroupingNode.Container](#groupingnode-class#container).  \n  \n```csharp  \nprotected ItemContainer Container { get; set; }  \n```  \n  \n**Property Value**  \n  \n[ItemContainer](#itemcontainer-class)  \n  \n#### Editor  \n  \nGets the [NodifyEditor](#nodifyeditor-class) that owns this [GroupingNode](#groupingnode-class).  \n  \n```csharp  \nprotected NodifyEditor Editor { get; set; }  \n```  \n  \n**Property Value**  \n  \n[NodifyEditor](#nodifyeditor-class)  \n  \n#### HeaderBrush  \n  \nGets or sets the brush used for the background of the [HeaderedContentControl.Header](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Controls.HeaderedContentControl.header) of this [GroupingNode](#groupingnode-class).  \n  \n```csharp  \npublic Brush HeaderBrush { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Brush](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Media.Brush)  \n  \n#### MovementMode  \n  \nGets or sets the default movement mode which can be temporarily changed by holding the SwitchMovementModeModifierKey while dragging by the header.  \n  \n```csharp  \npublic GroupingMovementMode MovementMode { get; set; }  \n```  \n  \n**Property Value**  \n  \n[GroupingMovementMode](#groupingmovementmode-enum)  \n  \n#### ResizeCompletedCommand  \n  \nInvoked when the [GroupingNode.ResizeCompleted](#groupingnode-class#resizecompleted) event is not handled.\n            Parameter is the [ItemContainer.ActualSize](#itemcontainer-class#actualsize) of the container.  \n  \n```csharp  \npublic ICommand ResizeCompletedCommand { get; set; }  \n```  \n  \n**Property Value**  \n  \n[ICommand](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.ICommand)  \n  \n#### ResizeStartedCommand  \n  \nInvoked when the [GroupingNode.ResizeStarted](#groupingnode-class#resizestarted) event is not handled.\n            Parameter is the [ItemContainer.ActualSize](#itemcontainer-class#actualsize) of the container.  \n  \n```csharp  \npublic ICommand ResizeStartedCommand { get; set; }  \n```  \n  \n**Property Value**  \n  \n[ICommand](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.ICommand)  \n  \n### Methods  \n  \n#### OnApplyTemplate()  \n  \n```csharp  \npublic override void OnApplyTemplate();  \n```  \n  \n### Events  \n  \n#### ResizeCompleted  \n  \nOccurs when the node finished resizing.  \n  \n```csharp  \npublic event ResizeEventHandler ResizeCompleted;  \n```  \n  \n**Event Type**  \n  \n[ResizeEventHandler](#resizeeventhandler-delegate)  \n  \n#### ResizeStarted  \n  \nOccurs when the node started resizing.  \n  \n```csharp  \npublic event ResizeEventHandler ResizeStarted;  \n```  \n  \n**Event Type**  \n  \n[ResizeEventHandler](#resizeeventhandler-delegate)  \n  \n## GroupingNodeGestures Class  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**Inheritance:** [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object) → [GroupingNodeGestures](#groupingnodegestures-class)  \n  \n**References:** [EditorGestures](#editorgestures-class)  \n  \n```csharp  \npublic class GroupingNodeGestures  \n```  \n  \n### Constructors  \n  \n#### GroupingNodeGestures()  \n  \n```csharp  \npublic GroupingNodeGestures();  \n```  \n  \n### Properties  \n  \n#### SwitchMovementMode  \n  \n```csharp  \npublic ModifierKeys SwitchMovementMode { get; set; }  \n```  \n  \n**Property Value**  \n  \n[ModifierKeys](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.ModifierKeys)  \n  \n### Methods  \n  \n#### Apply(GroupingNodeGestures)  \n  \n```csharp  \npublic void Apply(GroupingNodeGestures gestures);  \n```  \n  \n**Parameters**  \n  \n`gestures` [GroupingNodeGestures](#groupingnodegestures-class)  \n  \n## INodifyCanvasItem Interface  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**Inheritance:** [INodifyCanvasItem](#inodifycanvasitem-interface)  \n  \n**Derived:** [DecoratorContainer](#decoratorcontainer-class), [ItemContainer](#itemcontainer-class)  \n  \n**References:** [NodifyCanvas](#nodifycanvas-class)  \n  \nInterface for items inside a [NodifyCanvas](#nodifycanvas-class).  \n  \n```csharp  \npublic abstract interface INodifyCanvasItem  \n```  \n  \n### Properties  \n  \n#### DesiredSize  \n  \nThe desired size of the item.  \n  \n```csharp  \npublic virtual Size DesiredSize { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Size](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Size)  \n  \n#### Location  \n  \nThe location of the item.  \n  \n```csharp  \npublic virtual Point Location { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n### Methods  \n  \n#### Arrange(Rect)  \n  \n```csharp  \npublic virtual void Arrange(Rect rect);  \n```  \n  \n**Parameters**  \n  \n`rect` [Rect](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Rect)  \n  \n## InputGestureRef Class  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**Inheritance:** [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object) → [InputGesture](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.InputGesture) → [InputGestureRef](#inputgestureref-class)  \n  \n**References:** [ConnectionGestures](#connectiongestures-class), [ConnectorGestures](#connectorgestures-class), [EditorCommands](#editorcommands-class), [ItemContainerGestures](#itemcontainergestures-class), [MinimapGestures](#minimapgestures-class), [NodifyEditorGestures](#nodifyeditorgestures-class), [SelectionGestures](#selectiongestures-class)  \n  \nAn input gesture that allows changing its logic at runtime without changing its reference.\n            Useful for classes that capture the object reference without the posibility of updating it. (e.g. [EditorCommands](#editorcommands-class))  \n  \n```csharp  \npublic sealed class InputGestureRef : InputGesture  \n```  \n  \n### Properties  \n  \n#### Value  \n  \nThe referenced gesture.  \n  \n```csharp  \npublic InputGesture Value { get; set; }  \n```  \n  \n**Property Value**  \n  \n[InputGesture](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.InputGesture)  \n  \n### Methods  \n  \n#### Matches(Object, InputEventArgs)  \n  \n```csharp  \npublic override bool Matches(object targetElement, InputEventArgs inputEventArgs);  \n```  \n  \n**Parameters**  \n  \n`targetElement` [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object)  \n  \n`inputEventArgs` [InputEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.InputEventArgs)  \n  \n**Returns**  \n  \n[Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean)  \n  \n## ItemContainer Class  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**Inheritance:** [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object) → [DispatcherObject](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Threading.DispatcherObject) → [DependencyObject](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.DependencyObject) → [Visual](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Media.Visual) → [UIElement](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.UIElement) → [FrameworkElement](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.FrameworkElement) → [Control](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Controls.Control) → [ContentControl](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Controls.ContentControl) → [ItemContainer](#itemcontainer-class)  \n  \n**Implements:** [INodifyCanvasItem](#inodifycanvasitem-interface)  \n  \n**References:** [Connector](#connector-class), [ContainerDefaultState](#containerdefaultstate-class), [ContainerDraggingState](#containerdraggingstate-class), [ContainerState](#containerstate-class), [EditorCommands](#editorcommands-class), [GroupingNode](#groupingnode-class), [NodifyEditor](#nodifyeditor-class), [PendingConnection](#pendingconnection-class), [PreviewLocationChanged](#previewlocationchanged-delegate), [SelectionHelper](#selectionhelper-class)  \n  \nThe container for all the items generated by the [ItemsControl.ItemsSource](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Controls.ItemsControl.itemssource) of the [NodifyEditor](#nodifyeditor-class).  \n  \n```csharp  \npublic class ItemContainer : ContentControl, INodifyCanvasItem  \n```  \n  \n### Constructors  \n  \n#### ItemContainer(NodifyEditor)  \n  \nConstructs an instance of an [ItemContainer](#itemcontainer-class) in the specified [NodifyEditor](#nodifyeditor-class).  \n  \n```csharp  \npublic ItemContainer(NodifyEditor editor);  \n```  \n  \n**Parameters**  \n  \n`editor` [NodifyEditor](#nodifyeditor-class)  \n  \n### Fields  \n  \n#### IsPreviewingLocationPropertyKey  \n  \n```csharp  \npublic static DependencyPropertyKey IsPreviewingLocationPropertyKey;  \n```  \n  \n**Field Value**  \n  \n[DependencyPropertyKey](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.DependencyPropertyKey)  \n  \n#### IsPreviewingSelectionPropertyKey  \n  \n```csharp  \npublic static DependencyPropertyKey IsPreviewingSelectionPropertyKey;  \n```  \n  \n**Field Value**  \n  \n[DependencyPropertyKey](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.DependencyPropertyKey)  \n  \n### Properties  \n  \n#### ActualSize  \n  \nGets the actual size of this [ItemContainer](#itemcontainer-class).  \n  \n```csharp  \npublic Size ActualSize { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Size](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Size)  \n  \n#### AllowDraggingCancellation  \n  \nGets or sets whether cancelling a dragging operation is allowed.  \n  \n```csharp  \npublic static bool AllowDraggingCancellation { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean)  \n  \n#### DesiredSizeForSelection  \n  \nOverrides the size to check against when calculating if this [ItemContainer](#itemcontainer-class) can be part of the current [NodifyEditor.SelectedArea](#nodifyeditor-class#selectedarea).\n            Defaults to [UIElement.RenderSize](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.UIElement.rendersize).  \n  \n```csharp  \npublic Size? DesiredSizeForSelection { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Size?](https://docs.microsoft.com/en-us/dotnet/api/System.Nullable)  \n  \n#### Editor  \n  \nThe [NodifyEditor](#nodifyeditor-class) that owns this [ItemContainer](#itemcontainer-class).  \n  \n```csharp  \npublic NodifyEditor Editor { get; set; }  \n```  \n  \n**Property Value**  \n  \n[NodifyEditor](#nodifyeditor-class)  \n  \n#### HighlightBrush  \n  \nGets or sets the brush used when the [PendingConnection.IsOverElementProperty](#pendingconnection-class#isoverelementproperty) attached property is true for this [ItemContainer](#itemcontainer-class).  \n  \n```csharp  \npublic Brush HighlightBrush { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Brush](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Media.Brush)  \n  \n#### IsDraggable  \n  \nGets or sets whether this [ItemContainer](#itemcontainer-class) can be dragged.  \n  \n```csharp  \npublic bool IsDraggable { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean)  \n  \n#### IsPreviewingLocation  \n  \nGets a value indicating whether this [ItemContainer](#itemcontainer-class) is previewing a new location but didn't logically move there.  \n  \n```csharp  \npublic bool IsPreviewingLocation { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean)  \n  \n#### IsPreviewingSelection  \n  \nGets a value indicating whether this [ItemContainer](#itemcontainer-class) is about to change its [ItemContainer.IsSelected](#itemcontainer-class#isselected) state.  \n  \n```csharp  \npublic Boolean? IsPreviewingSelection { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Boolean?](https://docs.microsoft.com/en-us/dotnet/api/System.Nullable)  \n  \n#### IsSelectable  \n  \nGets or sets whether this [ItemContainer](#itemcontainer-class) can be selected.  \n  \n```csharp  \npublic bool IsSelectable { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean)  \n  \n#### IsSelected  \n  \nGets or sets a value that indicates whether this [ItemContainer](#itemcontainer-class) is selected.\n            Can only be set if [ItemContainer.IsSelectable](#itemcontainer-class#isselectable) is true.  \n  \n```csharp  \npublic bool IsSelected { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean)  \n  \n#### Location  \n  \nGets or sets the location of this [ItemContainer](#itemcontainer-class) inside the [NodifyEditor](#nodifyeditor-class) in graph space coordinates.  \n  \n```csharp  \npublic virtual Point Location { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n#### SelectedBorderThickness  \n  \nGets or sets the border thickness used when [ItemContainer.IsSelected](#itemcontainer-class#isselected) or [ItemContainer.IsPreviewingSelection](#itemcontainer-class#ispreviewingselection) is true.  \n  \n```csharp  \npublic Thickness SelectedBorderThickness { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Thickness](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Thickness)  \n  \n#### SelectedBrush  \n  \nGets or sets the brush used when [ItemContainer.IsSelected](#itemcontainer-class#isselected) or [ItemContainer.IsPreviewingSelection](#itemcontainer-class#ispreviewingselection) is true.  \n  \n```csharp  \npublic Brush SelectedBrush { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Brush](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Media.Brush)  \n  \n#### SelectedMargin  \n  \nThe calculated margin when the container is selected or previewing selection.  \n  \n```csharp  \npublic Thickness SelectedMargin { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Thickness](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Thickness)  \n  \n#### State  \n  \nThe current state of the container.  \n  \n```csharp  \npublic ContainerState State { get; set; }  \n```  \n  \n**Property Value**  \n  \n[ContainerState](#containerstate-class)  \n  \n### Methods  \n  \n#### GetInitialState()  \n  \nCreates the initial state of the container.  \n  \n```csharp  \nprotected virtual ContainerState GetInitialState();  \n```  \n  \n**Returns**  \n  \n[ContainerState](#containerstate-class): The initial state.  \n  \n#### IsSelectableInArea(Rect, Boolean)  \n  \nChecks if area contains or intersects with this [ItemContainer](#itemcontainer-class) taking into consideration the [ItemContainer.DesiredSizeForSelection](#itemcontainer-class#desiredsizeforselection).  \n  \n```csharp  \npublic virtual bool IsSelectableInArea(Rect area, bool isContained);  \n```  \n  \n**Parameters**  \n  \n`area` [Rect](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Rect): The area to check if contains or intersects this [ItemContainer](#itemcontainer-class).  \n  \n`isContained` [Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean): If true will check if area contains this, otherwise will check if area intersects with this.  \n  \n**Returns**  \n  \n[Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean): True if area contains or intersects this [ItemContainer](#itemcontainer-class).  \n  \n#### IsSelectableLocation(Point)  \n  \nChecks if position is selectable.  \n  \n```csharp  \nprotected virtual bool IsSelectableLocation(Point position);  \n```  \n  \n**Parameters**  \n  \n`position` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point): A position relative to this [ItemContainer](#itemcontainer-class).  \n  \n**Returns**  \n  \n[Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean): True if position is selectable.  \n  \n#### OnApplyTemplate()  \n  \n```csharp  \npublic override void OnApplyTemplate();  \n```  \n  \n#### OnKeyDown(KeyEventArgs)  \n  \n```csharp  \nprotected override void OnKeyDown(KeyEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [KeyEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.KeyEventArgs)  \n  \n#### OnKeyUp(KeyEventArgs)  \n  \n```csharp  \nprotected override void OnKeyUp(KeyEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [KeyEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.KeyEventArgs)  \n  \n#### OnLocationChanged()  \n  \nRaises the [ItemContainer.LocationChangedEvent](#itemcontainer-class#locationchangedevent) and sets [ItemContainer.IsPreviewingLocation](#itemcontainer-class#ispreviewinglocation) to false.  \n  \n```csharp  \nprotected void OnLocationChanged();  \n```  \n  \n#### OnLostMouseCapture(MouseEventArgs)  \n  \n```csharp  \nprotected override void OnLostMouseCapture(MouseEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [MouseEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.MouseEventArgs)  \n  \n#### OnMouseDown(MouseButtonEventArgs)  \n  \n```csharp  \nprotected override void OnMouseDown(MouseButtonEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [MouseButtonEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.MouseButtonEventArgs)  \n  \n#### OnMouseMove(MouseEventArgs)  \n  \n```csharp  \nprotected override void OnMouseMove(MouseEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [MouseEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.MouseEventArgs)  \n  \n#### OnMouseUp(MouseButtonEventArgs)  \n  \n```csharp  \nprotected override void OnMouseUp(MouseButtonEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [MouseButtonEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.MouseButtonEventArgs)  \n  \n#### OnMouseWheel(MouseWheelEventArgs)  \n  \n```csharp  \nprotected override void OnMouseWheel(MouseWheelEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [MouseWheelEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.MouseWheelEventArgs)  \n  \n#### OnRenderSizeChanged(SizeChangedInfo)  \n  \n```csharp  \nprotected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo);  \n```  \n  \n**Parameters**  \n  \n`sizeInfo` [SizeChangedInfo](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.SizeChangedInfo)  \n  \n#### OnSelectedChanged(Boolean)  \n  \nRaises the [ItemContainer.SelectedEvent](#itemcontainer-class#selectedevent) or [ItemContainer.UnselectedEvent](#itemcontainer-class#unselectedevent) based on newValue.\n            Called when the [ItemContainer.IsSelected](#itemcontainer-class#isselected) value is changed.  \n  \n```csharp  \nprotected void OnSelectedChanged(bool newValue);  \n```  \n  \n**Parameters**  \n  \n`newValue` [Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean): True if selected, false otherwise.  \n  \n#### PopAllStates()  \n  \nPops all states from the container.  \n  \n```csharp  \npublic void PopAllStates();  \n```  \n  \n#### PopState()  \n  \nPops the current [ItemContainer.State](#itemcontainer-class#state) from the stack.  \n  \n```csharp  \npublic void PopState();  \n```  \n  \n#### PushState(ContainerState)  \n  \nPushes the given state to the stack.  \n  \n```csharp  \npublic void PushState(ContainerState state);  \n```  \n  \n**Parameters**  \n  \n`state` [ContainerState](#containerstate-class): The new state of the container.  \n  \n### Events  \n  \n#### DragCompleted  \n  \nOccurs when this [ItemContainer](#itemcontainer-class) completed the drag operation.  \n  \n```csharp  \npublic event DragCompletedEventHandler DragCompleted;  \n```  \n  \n**Event Type**  \n  \n[DragCompletedEventHandler](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Controls.Primitives.DragCompletedEventHandler)  \n  \n#### DragDelta  \n  \nOccurs when this [ItemContainer](#itemcontainer-class) is being dragged.  \n  \n```csharp  \npublic event DragDeltaEventHandler DragDelta;  \n```  \n  \n**Event Type**  \n  \n[DragDeltaEventHandler](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Controls.Primitives.DragDeltaEventHandler)  \n  \n#### DragStarted  \n  \nOccurs when this [ItemContainer](#itemcontainer-class) is the instigator of a drag operation.  \n  \n```csharp  \npublic event DragStartedEventHandler DragStarted;  \n```  \n  \n**Event Type**  \n  \n[DragStartedEventHandler](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Controls.Primitives.DragStartedEventHandler)  \n  \n#### LocationChanged  \n  \nOccurs when the [ItemContainer.Location](#itemcontainer-class#location) of this [ItemContainer](#itemcontainer-class) is changed.  \n  \n```csharp  \npublic event RoutedEventHandler LocationChanged;  \n```  \n  \n**Event Type**  \n  \n[RoutedEventHandler](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.RoutedEventHandler)  \n  \n#### PreviewLocationChanged  \n  \nOccurs when the [ItemContainer](#itemcontainer-class) is previewing a new location.  \n  \n```csharp  \npublic event PreviewLocationChanged PreviewLocationChanged;  \n```  \n  \n**Event Type**  \n  \n[PreviewLocationChanged](#previewlocationchanged-delegate)  \n  \n#### Selected  \n  \nOccurs when this [ItemContainer](#itemcontainer-class) is selected.  \n  \n```csharp  \npublic event RoutedEventHandler Selected;  \n```  \n  \n**Event Type**  \n  \n[RoutedEventHandler](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.RoutedEventHandler)  \n  \n#### Unselected  \n  \nOccurs when this [ItemContainer](#itemcontainer-class) is unselected.  \n  \n```csharp  \npublic event RoutedEventHandler Unselected;  \n```  \n  \n**Event Type**  \n  \n[RoutedEventHandler](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.RoutedEventHandler)  \n  \n## ItemContainerGestures Class  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**Inheritance:** [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object) → [ItemContainerGestures](#itemcontainergestures-class)  \n  \n**References:** [EditorGestures](#editorgestures-class), [InputGestureRef](#inputgestureref-class), [SelectionGestures](#selectiongestures-class)  \n  \n```csharp  \npublic class ItemContainerGestures  \n```  \n  \n### Constructors  \n  \n#### ItemContainerGestures()  \n  \n```csharp  \npublic ItemContainerGestures();  \n```  \n  \n### Properties  \n  \n#### CancelAction  \n  \n```csharp  \npublic InputGestureRef CancelAction { get; set; }  \n```  \n  \n**Property Value**  \n  \n[InputGestureRef](#inputgestureref-class)  \n  \n#### Drag  \n  \n```csharp  \npublic InputGestureRef Drag { get; set; }  \n```  \n  \n**Property Value**  \n  \n[InputGestureRef](#inputgestureref-class)  \n  \n#### Selection  \n  \n```csharp  \npublic SelectionGestures Selection { get; set; }  \n```  \n  \n**Property Value**  \n  \n[SelectionGestures](#selectiongestures-class)  \n  \n### Methods  \n  \n#### Apply(ItemContainerGestures)  \n  \n```csharp  \npublic void Apply(ItemContainerGestures gestures);  \n```  \n  \n**Parameters**  \n  \n`gestures` [ItemContainerGestures](#itemcontainergestures-class)  \n  \n## KnotNode Class  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**Inheritance:** [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object) → [DispatcherObject](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Threading.DispatcherObject) → [DependencyObject](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.DependencyObject) → [Visual](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Media.Visual) → [UIElement](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.UIElement) → [FrameworkElement](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.FrameworkElement) → [Control](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Controls.Control) → [ContentControl](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Controls.ContentControl) → [KnotNode](#knotnode-class)  \n  \n**References:** [Connector](#connector-class)  \n  \nRepresents a control that owns a [Connector](#connector-class).  \n  \n```csharp  \npublic class KnotNode : ContentControl  \n```  \n  \n### Constructors  \n  \n#### KnotNode()  \n  \n```csharp  \npublic KnotNode();  \n```  \n  \n## LineConnection Class  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**Inheritance:** [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object) → [DispatcherObject](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Threading.DispatcherObject) → [DependencyObject](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.DependencyObject) → [Visual](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Media.Visual) → [UIElement](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.UIElement) → [FrameworkElement](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.FrameworkElement) → [Shape](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Shapes.Shape) → [BaseConnection](#baseconnection-class) → [LineConnection](#lineconnection-class)  \n  \n**Derived:** [CircuitConnection](#circuitconnection-class), [StepConnection](#stepconnection-class)  \n  \n**References:** [ConnectionDirection](#connectiondirection-enum)  \n  \nRepresents a line that has an arrow indicating its [BaseConnection.Direction](#baseconnection-class#direction).  \n  \n```csharp  \npublic class LineConnection : BaseConnection  \n```  \n  \n### Constructors  \n  \n#### LineConnection()  \n  \n```csharp  \npublic LineConnection();  \n```  \n  \n### Properties  \n  \n#### CornerRadius  \n  \nThe radius of the corners between the line segments.  \n  \n```csharp  \npublic double CornerRadius { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Double](https://docs.microsoft.com/en-us/dotnet/api/System.Double)  \n  \n### Methods  \n  \n#### AddSmoothCorner(StreamGeometryContext, Point, Point, Point, Double)  \n  \n```csharp  \nprotected static void AddSmoothCorner(StreamGeometryContext context, Point start, Point corner, Point end, double radius);  \n```  \n  \n**Parameters**  \n  \n`context` [StreamGeometryContext](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Media.StreamGeometryContext)  \n  \n`start` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n`corner` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n`end` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n`radius` [Double](https://docs.microsoft.com/en-us/dotnet/api/System.Double)  \n  \n#### DrawDefaultArrowhead(StreamGeometryContext, Point, Point, ConnectionDirection, Orientation)  \n  \n```csharp  \nprotected override void DrawDefaultArrowhead(StreamGeometryContext context, Point source, Point target, ConnectionDirection arrowDirection = 0, Orientation orientation = 0);  \n```  \n  \n**Parameters**  \n  \n`context` [StreamGeometryContext](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Media.StreamGeometryContext)  \n  \n`source` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n`target` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n`arrowDirection` [ConnectionDirection](#connectiondirection-enum)  \n  \n`orientation` [Orientation](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Controls.Orientation)  \n  \n#### DrawDirectionalArrowsGeometry(StreamGeometryContext, Point, Point)  \n  \n```csharp  \nprotected override void DrawDirectionalArrowsGeometry(StreamGeometryContext context, Point source, Point target);  \n```  \n  \n**Parameters**  \n  \n`context` [StreamGeometryContext](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Media.StreamGeometryContext)  \n  \n`source` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n`target` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n#### DrawLineGeometry(StreamGeometryContext, Point, Point)  \n  \n```csharp  \nprotected override ValueTuple<ValueTuple<Point, Point>, ValueTuple<Point, Point>> DrawLineGeometry(StreamGeometryContext context, Point source, Point target);  \n```  \n  \n**Parameters**  \n  \n`context` [StreamGeometryContext](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Media.StreamGeometryContext)  \n  \n`source` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n`target` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n**Returns**  \n  \n[ValueTuple<ValueTuple<Point, Point>, ValueTuple<Point, Point>>](https://docs.microsoft.com/en-us/dotnet/api/System.ValueTuple)  \n  \n#### InterpolateLine(Point, Point, Point, Point, Double)  \n  \n```csharp  \nprotected static ValueTuple<ValueTuple<Point, Point>, Point> InterpolateLine(Point p0, Point p1, Point p2, Point p3, double t);  \n```  \n  \n**Parameters**  \n  \n`p0` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n`p1` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n`p2` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n`p3` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n`t` [Double](https://docs.microsoft.com/en-us/dotnet/api/System.Double)  \n  \n**Returns**  \n  \n[ValueTuple<ValueTuple<Point, Point>, Point>](https://docs.microsoft.com/en-us/dotnet/api/System.ValueTuple)  \n  \n#### InterpolateLine(Point, Point, Point, Double)  \n  \n```csharp  \nprotected static ValueTuple<ValueTuple<Point, Point>, Point> InterpolateLine(Point p0, Point p1, Point p2, double t);  \n```  \n  \n**Parameters**  \n  \n`p0` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n`p1` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n`p2` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n`t` [Double](https://docs.microsoft.com/en-us/dotnet/api/System.Double)  \n  \n**Returns**  \n  \n[ValueTuple<ValueTuple<Point, Point>, Point>](https://docs.microsoft.com/en-us/dotnet/api/System.ValueTuple)  \n  \n#### InterpolateLineSegment(Point, Point, Double)  \n  \n```csharp  \nprotected static Point InterpolateLineSegment(Point p0, Point p1, double t);  \n```  \n  \n**Parameters**  \n  \n`p0` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n`p1` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n`t` [Double](https://docs.microsoft.com/en-us/dotnet/api/System.Double)  \n  \n**Returns**  \n  \n[Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n## Match Enum  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**References:** [MultiGesture](#multigesture-class)  \n  \n```csharp  \npublic enum Match  \n```  \n  \n### Fields  \n  \n#### All  \n  \n```csharp  \nAll = 1;  \n```  \n  \n#### Any  \n  \n```csharp  \nAny = 0;  \n```  \n  \n## Minimap Class  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**Inheritance:** [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object) → [DispatcherObject](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Threading.DispatcherObject) → [DependencyObject](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.DependencyObject) → [Visual](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Media.Visual) → [UIElement](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.UIElement) → [FrameworkElement](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.FrameworkElement) → [Control](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Controls.Control) → [ItemsControl](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Controls.ItemsControl) → [Minimap](#minimap-class)  \n  \n**References:** [MinimapItem](#minimapitem-class), [NodifyEditor](#nodifyeditor-class), [ZoomEventArgs](#zoomeventargs-class), [ZoomEventHandler](#zoomeventhandler-delegate)  \n  \nA minimap control that can position the viewport, and zoom in and out.  \n  \n```csharp  \npublic class Minimap : ItemsControl  \n```  \n  \n### Constructors  \n  \n#### Minimap()  \n  \n```csharp  \npublic Minimap();  \n```  \n  \n### Fields  \n  \n#### ElementItemsHost  \n  \n```csharp  \nprotected const string ElementItemsHost = \"PART_ItemsHost\";  \n```  \n  \n**Field Value**  \n  \n[String](https://docs.microsoft.com/en-us/dotnet/api/System.String)  \n  \n### Properties  \n  \n#### Extent  \n  \nThe area covered by the items and the viewport rectangle in graph space.  \n  \n```csharp  \npublic Rect Extent { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Rect](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Rect)  \n  \n#### IsDragging  \n  \n```csharp  \nprotected bool IsDragging { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean)  \n  \n#### IsReadOnly  \n  \nWhether the minimap can move and zoom the viewport.  \n  \n```csharp  \npublic bool IsReadOnly { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean)  \n  \n#### ItemsExtent  \n  \nThe area covered by the [MinimapItem](#minimapitem-class)s in graph space.  \n  \n```csharp  \npublic Rect ItemsExtent { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Rect](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Rect)  \n  \n#### MaxViewportOffset  \n  \nThe max position from the [NodifyEditor.ItemsExtent](#nodifyeditor-class#itemsextent) that the viewport can move to.  \n  \n```csharp  \npublic Size MaxViewportOffset { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Size](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Size)  \n  \n#### ResizeToViewport  \n  \nWhether the minimap should resize to also display the whole viewport.  \n  \n```csharp  \npublic bool ResizeToViewport { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean)  \n  \n#### ViewportLocation  \n  \n```csharp  \npublic Point ViewportLocation { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n#### ViewportSize  \n  \n```csharp  \npublic Size ViewportSize { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Size](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Size)  \n  \n#### ViewportStyle  \n  \nGets or sets the style to use for the viewport rectangle.  \n  \n```csharp  \npublic Style ViewportStyle { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Style](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Style)  \n  \n### Methods  \n  \n#### GetContainerForItemOverride()  \n  \n```csharp  \nprotected override DependencyObject GetContainerForItemOverride();  \n```  \n  \n**Returns**  \n  \n[DependencyObject](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.DependencyObject)  \n  \n#### IsItemItsOwnContainerOverride(Object)  \n  \n```csharp  \nprotected override bool IsItemItsOwnContainerOverride(object item);  \n```  \n  \n**Parameters**  \n  \n`item` [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object)  \n  \n**Returns**  \n  \n[Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean)  \n  \n#### OnApplyTemplate()  \n  \n```csharp  \npublic override void OnApplyTemplate();  \n```  \n  \n#### OnMouseDown(MouseButtonEventArgs)  \n  \n```csharp  \nprotected override void OnMouseDown(MouseButtonEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [MouseButtonEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.MouseButtonEventArgs)  \n  \n#### OnMouseMove(MouseEventArgs)  \n  \n```csharp  \nprotected override void OnMouseMove(MouseEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [MouseEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.MouseEventArgs)  \n  \n#### OnMouseUp(MouseButtonEventArgs)  \n  \n```csharp  \nprotected override void OnMouseUp(MouseButtonEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [MouseButtonEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.MouseButtonEventArgs)  \n  \n#### OnMouseWheel(MouseWheelEventArgs)  \n  \n```csharp  \nprotected override void OnMouseWheel(MouseWheelEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [MouseWheelEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.MouseWheelEventArgs)  \n  \n### Events  \n  \n#### Zoom  \n  \nTriggered when zooming in or out using the mouse wheel.  \n  \n```csharp  \npublic event ZoomEventHandler Zoom;  \n```  \n  \n**Event Type**  \n  \n[ZoomEventHandler](#zoomeventhandler-delegate)  \n  \n## MinimapGestures Class  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**Inheritance:** [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object) → [MinimapGestures](#minimapgestures-class)  \n  \n**References:** [EditorGestures](#editorgestures-class), [InputGestureRef](#inputgestureref-class)  \n  \n```csharp  \npublic class MinimapGestures  \n```  \n  \n### Constructors  \n  \n#### MinimapGestures()  \n  \n```csharp  \npublic MinimapGestures();  \n```  \n  \n### Properties  \n  \n#### DragViewport  \n  \n```csharp  \npublic InputGestureRef DragViewport { get; set; }  \n```  \n  \n**Property Value**  \n  \n[InputGestureRef](#inputgestureref-class)  \n  \n#### ZoomModifierKey  \n  \n```csharp  \npublic ModifierKeys ZoomModifierKey { get; set; }  \n```  \n  \n**Property Value**  \n  \n[ModifierKeys](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.ModifierKeys)  \n  \n### Methods  \n  \n#### Apply(MinimapGestures)  \n  \n```csharp  \npublic void Apply(MinimapGestures gestures);  \n```  \n  \n**Parameters**  \n  \n`gestures` [MinimapGestures](#minimapgestures-class)  \n  \n## MinimapItem Class  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**Inheritance:** [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object) → [DispatcherObject](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Threading.DispatcherObject) → [DependencyObject](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.DependencyObject) → [Visual](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Media.Visual) → [UIElement](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.UIElement) → [FrameworkElement](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.FrameworkElement) → [Control](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Controls.Control) → [ContentControl](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Controls.ContentControl) → [MinimapItem](#minimapitem-class)  \n  \n**References:** [Minimap](#minimap-class)  \n  \n```csharp  \npublic class MinimapItem : ContentControl  \n```  \n  \n### Constructors  \n  \n#### MinimapItem()  \n  \n```csharp  \npublic MinimapItem();  \n```  \n  \n### Properties  \n  \n#### Location  \n  \nGets or sets the location of this [MinimapItem](#minimapitem-class) inside the [Minimap](#minimap-class).  \n  \n```csharp  \npublic Point Location { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n## MultiGesture Class  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**Inheritance:** [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object) → [InputGesture](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.InputGesture) → [MultiGesture](#multigesture-class)  \n  \n**Derived:** [AnyGesture](#anygesture-class), [AllGestures](#allgestures-class)  \n  \n**References:** [Match](#match-enum)  \n  \nCombines multiple input gestures.  \n  \n```csharp  \npublic class MultiGesture : InputGesture  \n```  \n  \n### Constructors  \n  \n#### MultiGesture(Match, InputGesture[])  \n  \nConstructs an instance of a [MultiGesture](#multigesture-class).  \n  \n```csharp  \npublic MultiGesture(Match match, InputGesture[] gestures);  \n```  \n  \n**Parameters**  \n  \n`match` [Match](#match-enum): The matching strategy.  \n  \n`gestures` [InputGesture[]](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.InputGesture[]): The input gestures.  \n  \n### Fields  \n  \n#### None  \n  \n```csharp  \npublic static MultiGesture None;  \n```  \n  \n**Field Value**  \n  \n[MultiGesture](#multigesture-class)  \n  \n### Methods  \n  \n#### Matches(Object, InputEventArgs)  \n  \n```csharp  \npublic override bool Matches(object targetElement, InputEventArgs inputEventArgs);  \n```  \n  \n**Parameters**  \n  \n`targetElement` [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object)  \n  \n`inputEventArgs` [InputEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.InputEventArgs)  \n  \n**Returns**  \n  \n[Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean)  \n  \n## Node Class  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**Inheritance:** [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object) → [DispatcherObject](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Threading.DispatcherObject) → [DependencyObject](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.DependencyObject) → [Visual](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Media.Visual) → [UIElement](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.UIElement) → [FrameworkElement](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.FrameworkElement) → [Control](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Controls.Control) → [ContentControl](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Controls.ContentControl) → [HeaderedContentControl](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Controls.HeaderedContentControl) → [Node](#node-class)  \n  \n**References:** [Connector](#connector-class), [NodeInput](#nodeinput-class), [NodeOutput](#nodeoutput-class)  \n  \nRepresents a control that has a list of [Node.Input](#node-class#input)[Connector](#connector-class)s and a list of [Node.Output](#node-class#output)[Connector](#connector-class)s.  \n  \n```csharp  \npublic class Node : HeaderedContentControl  \n```  \n  \n### Constructors  \n  \n#### Node()  \n  \n```csharp  \npublic Node();  \n```  \n  \n### Fields  \n  \n#### ElementInputItemsControl  \n  \n```csharp  \nprotected const string ElementInputItemsControl = \"PART_Input\";  \n```  \n  \n**Field Value**  \n  \n[String](https://docs.microsoft.com/en-us/dotnet/api/System.String)  \n  \n#### ElementOutputItemsControl  \n  \n```csharp  \nprotected const string ElementOutputItemsControl = \"PART_Output\";  \n```  \n  \n**Field Value**  \n  \n[String](https://docs.microsoft.com/en-us/dotnet/api/System.String)  \n  \n### Properties  \n  \n#### ContentBrush  \n  \nGets or sets the brush used for the background of the [ContentControl.Content](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Controls.ContentControl.content) of this [Node](#node-class).  \n  \n```csharp  \npublic Brush ContentBrush { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Brush](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Media.Brush)  \n  \n#### ContentContainerStyle  \n  \nGets or sets the style for the content container.  \n  \n```csharp  \npublic Style ContentContainerStyle { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Style](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Style)  \n  \n#### Footer  \n  \nGets or sets the data for the footer of this control.  \n  \n```csharp  \npublic object Footer { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object)  \n  \n#### FooterBrush  \n  \nGets or sets the brush used for the background of the [Node.Footer](#node-class#footer) of this [Node](#node-class).  \n  \n```csharp  \npublic Brush FooterBrush { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Brush](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Media.Brush)  \n  \n#### FooterContainerStyle  \n  \nGets or sets the style for the footer container.  \n  \n```csharp  \npublic Style FooterContainerStyle { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Style](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Style)  \n  \n#### FooterTemplate  \n  \nGets or sets the template used to display the content of the control's footer.  \n  \n```csharp  \npublic DataTemplate FooterTemplate { get; set; }  \n```  \n  \n**Property Value**  \n  \n[DataTemplate](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.DataTemplate)  \n  \n#### HasFooter  \n  \nGets a value that indicates whether the [Node.Footer](#node-class#footer) is .  \n  \n```csharp  \npublic bool HasFooter { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean)  \n  \n#### HeaderBrush  \n  \nGets or sets the brush used for the background of the [HeaderedContentControl.Header](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Controls.HeaderedContentControl.header) of this [Node](#node-class).  \n  \n```csharp  \npublic Brush HeaderBrush { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Brush](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Media.Brush)  \n  \n#### HeaderContainerStyle  \n  \nGets or sets the style for the header container.  \n  \n```csharp  \npublic Style HeaderContainerStyle { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Style](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Style)  \n  \n#### Input  \n  \nGets or sets the data for the input [Connector](#connector-class)s of this control.  \n  \n```csharp  \npublic IEnumerable Input { get; set; }  \n```  \n  \n**Property Value**  \n  \n[IEnumerable](https://docs.microsoft.com/en-us/dotnet/api/System.Collections.IEnumerable)  \n  \n#### InputConnectorTemplate  \n  \nGets or sets the template used to display the content of the control's [Node.Input](#node-class#input) connectors.  \n  \n```csharp  \npublic DataTemplate InputConnectorTemplate { get; set; }  \n```  \n  \n**Property Value**  \n  \n[DataTemplate](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.DataTemplate)  \n  \n#### InputGroupStyle  \n  \n```csharp  \npublic ObservableCollection<GroupStyle> InputGroupStyle { get; set; }  \n```  \n  \n**Property Value**  \n  \n[ObservableCollection<GroupStyle>](https://docs.microsoft.com/en-us/dotnet/api/System.Collections.ObjectModel.ObservableCollection)  \n  \n#### InputItemsControl  \n  \n```csharp  \nprotected ItemsControl InputItemsControl { get; set; }  \n```  \n  \n**Property Value**  \n  \n[ItemsControl](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Controls.ItemsControl)  \n  \n#### Output  \n  \nGets or sets the data for the output [Connector](#connector-class)s of this control.  \n  \n```csharp  \npublic IEnumerable Output { get; set; }  \n```  \n  \n**Property Value**  \n  \n[IEnumerable](https://docs.microsoft.com/en-us/dotnet/api/System.Collections.IEnumerable)  \n  \n#### OutputConnectorTemplate  \n  \nGets or sets the template used to display the content of the control's [Node.Output](#node-class#output) connectors.  \n  \n```csharp  \npublic DataTemplate OutputConnectorTemplate { get; set; }  \n```  \n  \n**Property Value**  \n  \n[DataTemplate](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.DataTemplate)  \n  \n#### OutputGroupStyle  \n  \n```csharp  \npublic ObservableCollection<GroupStyle> OutputGroupStyle { get; set; }  \n```  \n  \n**Property Value**  \n  \n[ObservableCollection<GroupStyle>](https://docs.microsoft.com/en-us/dotnet/api/System.Collections.ObjectModel.ObservableCollection)  \n  \n#### OutputItemsControl  \n  \n```csharp  \nprotected ItemsControl OutputItemsControl { get; set; }  \n```  \n  \n**Property Value**  \n  \n[ItemsControl](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Controls.ItemsControl)  \n  \n### Methods  \n  \n#### OnApplyTemplate()  \n  \n```csharp  \npublic override void OnApplyTemplate();  \n```  \n  \n## NodeInput Class  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**Inheritance:** [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object) → [DispatcherObject](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Threading.DispatcherObject) → [DependencyObject](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.DependencyObject) → [Visual](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Media.Visual) → [UIElement](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.UIElement) → [FrameworkElement](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.FrameworkElement) → [Control](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Controls.Control) → [Connector](#connector-class) → [NodeInput](#nodeinput-class)  \n  \n**References:** [Node](#node-class)  \n  \nRepresents the default control for the [Node.InputConnectorTemplate](#node-class#inputconnectortemplate).  \n  \n```csharp  \npublic class NodeInput : Connector  \n```  \n  \n### Constructors  \n  \n#### NodeInput()  \n  \n```csharp  \npublic NodeInput();  \n```  \n  \n### Properties  \n  \n#### ConnectorTemplate  \n  \nGets or sets the template used to display the connecting point of this [Connector](#connector-class).  \n  \n```csharp  \npublic ControlTemplate ConnectorTemplate { get; set; }  \n```  \n  \n**Property Value**  \n  \n[ControlTemplate](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Controls.ControlTemplate)  \n  \n#### Header  \n  \nGets of sets the data used for the control's header.  \n  \n```csharp  \npublic object Header { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object)  \n  \n#### HeaderTemplate  \n  \nGets or sets the template used to display the content of the control's header.  \n  \n```csharp  \npublic DataTemplate HeaderTemplate { get; set; }  \n```  \n  \n**Property Value**  \n  \n[DataTemplate](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.DataTemplate)  \n  \n#### Orientation  \n  \n```csharp  \npublic Orientation Orientation { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Orientation](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Controls.Orientation)  \n  \n## NodeOutput Class  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**Inheritance:** [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object) → [DispatcherObject](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Threading.DispatcherObject) → [DependencyObject](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.DependencyObject) → [Visual](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Media.Visual) → [UIElement](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.UIElement) → [FrameworkElement](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.FrameworkElement) → [Control](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Controls.Control) → [Connector](#connector-class) → [NodeOutput](#nodeoutput-class)  \n  \n**References:** [Node](#node-class)  \n  \nRepresents the default control for the [Node.OutputConnectorTemplate](#node-class#outputconnectortemplate).  \n  \n```csharp  \npublic class NodeOutput : Connector  \n```  \n  \n### Constructors  \n  \n#### NodeOutput()  \n  \n```csharp  \npublic NodeOutput();  \n```  \n  \n### Properties  \n  \n#### ConnectorTemplate  \n  \nGets or sets the template used to display the connecting point of this [Connector](#connector-class).  \n  \n```csharp  \npublic ControlTemplate ConnectorTemplate { get; set; }  \n```  \n  \n**Property Value**  \n  \n[ControlTemplate](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Controls.ControlTemplate)  \n  \n#### Header  \n  \nGets of sets the data used for the control's header.  \n  \n```csharp  \npublic object Header { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object)  \n  \n#### HeaderTemplate  \n  \nGets or sets the template used to display the content of the control's header.  \n  \n```csharp  \npublic DataTemplate HeaderTemplate { get; set; }  \n```  \n  \n**Property Value**  \n  \n[DataTemplate](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.DataTemplate)  \n  \n#### Orientation  \n  \n```csharp  \npublic Orientation Orientation { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Orientation](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Controls.Orientation)  \n  \n## NodifyCanvas Class  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**Inheritance:** [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object) → [DispatcherObject](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Threading.DispatcherObject) → [DependencyObject](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.DependencyObject) → [Visual](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Media.Visual) → [UIElement](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.UIElement) → [FrameworkElement](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.FrameworkElement) → [Panel](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Controls.Panel) → [NodifyCanvas](#nodifycanvas-class)  \n  \n**References:** [INodifyCanvasItem](#inodifycanvasitem-interface)  \n  \nA canvas like panel that works with [INodifyCanvasItem](#inodifycanvasitem-interface)s.  \n  \n```csharp  \npublic class NodifyCanvas : Panel  \n```  \n  \n### Constructors  \n  \n#### NodifyCanvas()  \n  \n```csharp  \npublic NodifyCanvas();  \n```  \n  \n### Properties  \n  \n#### Extent  \n  \nThe area covered by the children of this panel.  \n  \n```csharp  \npublic Rect Extent { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Rect](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Rect)  \n  \n### Methods  \n  \n#### ArrangeOverride(Size)  \n  \n```csharp  \nprotected override Size ArrangeOverride(Size arrangeSize);  \n```  \n  \n**Parameters**  \n  \n`arrangeSize` [Size](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Size)  \n  \n**Returns**  \n  \n[Size](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Size)  \n  \n#### MeasureOverride(Size)  \n  \n```csharp  \nprotected override Size MeasureOverride(Size constraint);  \n```  \n  \n**Parameters**  \n  \n`constraint` [Size](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Size)  \n  \n**Returns**  \n  \n[Size](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Size)  \n  \n## NodifyEditor Class  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**Inheritance:** [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object) → [DispatcherObject](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Threading.DispatcherObject) → [DependencyObject](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.DependencyObject) → [Visual](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Media.Visual) → [UIElement](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.UIElement) → [FrameworkElement](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.FrameworkElement) → [Control](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Controls.Control) → [ItemsControl](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Controls.ItemsControl) → [Selector](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Controls.Primitives.Selector) → [MultiSelector](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Controls.Primitives.MultiSelector) → [NodifyEditor](#nodifyeditor-class)  \n  \n**Implements:** [IScrollInfo](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Controls.Primitives.IScrollInfo)  \n  \n**References:** [BaseConnection](#baseconnection-class), [Connection](#connection-class), [Connector](#connector-class), [ContainerState](#containerstate-class), [CuttingLine](#cuttingline-class), [DecoratorContainer](#decoratorcontainer-class), [EditorCommands](#editorcommands-class), [EditorCuttingState](#editorcuttingstate-class), [EditorDefaultState](#editordefaultstate-class), [EditorGestures](#editorgestures-class), [EditorPanningState](#editorpanningstate-class), [EditorPushingItemsState](#editorpushingitemsstate-class), [EditorSelectingState](#editorselectingstate-class), [EditorState](#editorstate-class), [GroupingNode](#groupingnode-class), [ItemContainer](#itemcontainer-class), [Minimap](#minimap-class), [PendingConnection](#pendingconnection-class), [SelectionHelper](#selectionhelper-class)  \n  \nGroups [ItemContainer](#itemcontainer-class)s and [Connection](#connection-class)s in an area that you can drag, zoom and select.  \n  \n```csharp  \npublic class NodifyEditor : MultiSelector, IScrollInfo  \n```  \n  \n### Constructors  \n  \n#### NodifyEditor()  \n  \nInitializes a new instance of the [NodifyEditor](#nodifyeditor-class) class.  \n  \n```csharp  \npublic NodifyEditor();  \n```  \n  \n### Fields  \n  \n#### CuttingConnectionTypes  \n  \nThe list of supported connection types for cutting. Type must be derived from [FrameworkElement](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.FrameworkElement).  \n  \n```csharp  \npublic static HashSet<Type> CuttingConnectionTypes;  \n```  \n  \n**Field Value**  \n  \n[HashSet<Type>](https://docs.microsoft.com/en-us/dotnet/api/System.Collections.Generic.HashSet)  \n  \n#### CuttingLineEndPropertyKey  \n  \n```csharp  \nprotected static DependencyPropertyKey CuttingLineEndPropertyKey;  \n```  \n  \n**Field Value**  \n  \n[DependencyPropertyKey](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.DependencyPropertyKey)  \n  \n#### CuttingLineStartPropertyKey  \n  \n```csharp  \nprotected static DependencyPropertyKey CuttingLineStartPropertyKey;  \n```  \n  \n**Field Value**  \n  \n[DependencyPropertyKey](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.DependencyPropertyKey)  \n  \n#### ElementConnectionsHost  \n  \n```csharp  \nprotected const string ElementConnectionsHost = \"PART_ConnectionsHost\";  \n```  \n  \n**Field Value**  \n  \n[String](https://docs.microsoft.com/en-us/dotnet/api/System.String)  \n  \n#### ElementItemsHost  \n  \n```csharp  \nprotected const string ElementItemsHost = \"PART_ItemsHost\";  \n```  \n  \n**Field Value**  \n  \n[String](https://docs.microsoft.com/en-us/dotnet/api/System.String)  \n  \n#### IsCuttingPropertyKey  \n  \n```csharp  \nprotected static DependencyPropertyKey IsCuttingPropertyKey;  \n```  \n  \n**Field Value**  \n  \n[DependencyPropertyKey](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.DependencyPropertyKey)  \n  \n#### IsPanningPropertyKey  \n  \n```csharp  \npublic static DependencyPropertyKey IsPanningPropertyKey;  \n```  \n  \n**Field Value**  \n  \n[DependencyPropertyKey](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.DependencyPropertyKey)  \n  \n#### IsPushingItemsPropertyKey  \n  \n```csharp  \nprotected static DependencyPropertyKey IsPushingItemsPropertyKey;  \n```  \n  \n**Field Value**  \n  \n[DependencyPropertyKey](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.DependencyPropertyKey)  \n  \n#### IsSelectingPropertyKey  \n  \n```csharp  \nprotected static DependencyPropertyKey IsSelectingPropertyKey;  \n```  \n  \n**Field Value**  \n  \n[DependencyPropertyKey](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.DependencyPropertyKey)  \n  \n#### MouseLocationPropertyKey  \n  \n```csharp  \nprotected static DependencyPropertyKey MouseLocationPropertyKey;  \n```  \n  \n**Field Value**  \n  \n[DependencyPropertyKey](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.DependencyPropertyKey)  \n  \n#### PushedAreaOrientationPropertyKey  \n  \n```csharp  \nprotected static DependencyPropertyKey PushedAreaOrientationPropertyKey;  \n```  \n  \n**Field Value**  \n  \n[DependencyPropertyKey](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.DependencyPropertyKey)  \n  \n#### PushedAreaPropertyKey  \n  \n```csharp  \nprotected static DependencyPropertyKey PushedAreaPropertyKey;  \n```  \n  \n**Field Value**  \n  \n[DependencyPropertyKey](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.DependencyPropertyKey)  \n  \n#### ScaleTransform  \n  \nGets the transform used to zoom on the viewport.  \n  \n```csharp  \nprotected readonly ScaleTransform ScaleTransform;  \n```  \n  \n**Field Value**  \n  \n[ScaleTransform](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Media.ScaleTransform)  \n  \n#### SelectedAreaPropertyKey  \n  \n```csharp  \nprotected static DependencyPropertyKey SelectedAreaPropertyKey;  \n```  \n  \n**Field Value**  \n  \n[DependencyPropertyKey](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.DependencyPropertyKey)  \n  \n#### TranslateTransform  \n  \nGets the transform used to offset the viewport.  \n  \n```csharp  \nprotected readonly TranslateTransform TranslateTransform;  \n```  \n  \n**Field Value**  \n  \n[TranslateTransform](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Media.TranslateTransform)  \n  \n### Properties  \n  \n#### AllowPushItemsCancellation  \n  \nGets or sets whether push items cancellation is allowed.  \n  \n```csharp  \npublic static bool AllowPushItemsCancellation { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean)  \n  \n#### AutoPanEdgeDistance  \n  \nGets or sets the maximum distance in pixels from the edge of the editor that will trigger auto-panning.  \n  \n```csharp  \npublic double AutoPanEdgeDistance { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Double](https://docs.microsoft.com/en-us/dotnet/api/System.Double)  \n  \n#### AutoPanningTickRate  \n  \nGets or sets how often the new [NodifyEditor.ViewportLocation](#nodifyeditor-class#viewportlocation) is calculated in milliseconds when [NodifyEditor.DisableAutoPanning](#nodifyeditor-class#disableautopanning) is false.  \n  \n```csharp  \npublic static double AutoPanningTickRate { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Double](https://docs.microsoft.com/en-us/dotnet/api/System.Double)  \n  \n#### AutoPanSpeed  \n  \nGets or sets the speed used when auto-panning scaled by [NodifyEditor.AutoPanningTickRate](#nodifyeditor-class#autopanningtickrate)  \n  \n```csharp  \npublic double AutoPanSpeed { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Double](https://docs.microsoft.com/en-us/dotnet/api/System.Double)  \n  \n#### BringIntoViewMaxDuration  \n  \nGets or sets the maximum animation duration in seconds for bringing a location into view.  \n  \n```csharp  \npublic double BringIntoViewMaxDuration { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Double](https://docs.microsoft.com/en-us/dotnet/api/System.Double)  \n  \n#### BringIntoViewSpeed  \n  \nGets or sets the animation speed in pixels per second for bringing a location into view.  \n  \n```csharp  \npublic double BringIntoViewSpeed { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Double](https://docs.microsoft.com/en-us/dotnet/api/System.Double)  \n  \n#### CanSelectMultipleConnections  \n  \nGets or sets whether multiple connections can be selected.  \n  \n```csharp  \npublic bool CanSelectMultipleConnections { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean)  \n  \n#### CanSelectMultipleItems  \n  \nGets or sets whether multiple [ItemContainer](#itemcontainer-class)s can be selected.  \n  \n```csharp  \npublic bool CanSelectMultipleItems { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean)  \n  \n#### ConnectionCompletedCommand  \n  \nInvoked when the [PendingConnection](#pendingconnection-class) is completed. \n            Use [PendingConnection.CompletedCommand](#pendingconnection-class#completedcommand) if you want to control the visibility of the connection from the viewmodel. \n            Parameter is System.Tuple`2 where System.Tuple`2.Item1 is the [PendingConnection.Source](#pendingconnection-class#source) and System.Tuple`2.Item2 is [PendingConnection.Target](#pendingconnection-class#target).  \n  \n```csharp  \npublic ICommand ConnectionCompletedCommand { get; set; }  \n```  \n  \n**Property Value**  \n  \n[ICommand](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.ICommand)  \n  \n#### Connections  \n  \nGets or sets the data source that [BaseConnection](#baseconnection-class)s will be generated for.  \n  \n```csharp  \npublic IEnumerable Connections { get; set; }  \n```  \n  \n**Property Value**  \n  \n[IEnumerable](https://docs.microsoft.com/en-us/dotnet/api/System.Collections.IEnumerable)  \n  \n#### ConnectionStartedCommand  \n  \nInvoked when the [PendingConnection](#pendingconnection-class) is completed. \n            Use [PendingConnection.StartedCommand](#pendingconnection-class#startedcommand) if you want to control the visibility of the connection from the viewmodel. \n            Parameter is [PendingConnection.Source](#pendingconnection-class#source).  \n  \n```csharp  \npublic ICommand ConnectionStartedCommand { get; set; }  \n```  \n  \n**Property Value**  \n  \n[ICommand](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.ICommand)  \n  \n#### ConnectionTemplate  \n  \nGets or sets the [DataTemplate](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.DataTemplate) to use when generating a new [BaseConnection](#baseconnection-class).  \n  \n```csharp  \npublic DataTemplate ConnectionTemplate { get; set; }  \n```  \n  \n**Property Value**  \n  \n[DataTemplate](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.DataTemplate)  \n  \n#### CuttingCompletedCommand  \n  \nInvoked when a cutting operation is completed.  \n  \n```csharp  \npublic ICommand CuttingCompletedCommand { get; set; }  \n```  \n  \n**Property Value**  \n  \n[ICommand](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.ICommand)  \n  \n#### CuttingLineEnd  \n  \nGets the end point of the [CuttingLine](#cuttingline-class) while [NodifyEditor.IsCutting](#nodifyeditor-class#iscutting) is true.  \n  \n```csharp  \npublic Point CuttingLineEnd { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n#### CuttingLineStart  \n  \nGets the start point of the [CuttingLine](#cuttingline-class) while [NodifyEditor.IsCutting](#nodifyeditor-class#iscutting) is true.  \n  \n```csharp  \npublic Point CuttingLineStart { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n#### CuttingLineStyle  \n  \nGets or sets the style to use for the cutting line.  \n  \n```csharp  \npublic Style CuttingLineStyle { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Style](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Style)  \n  \n#### CuttingStartedCommand  \n  \nInvoked when a cutting operation is started.  \n  \n```csharp  \npublic ICommand CuttingStartedCommand { get; set; }  \n```  \n  \n**Property Value**  \n  \n[ICommand](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.ICommand)  \n  \n#### DecoratorContainerStyle  \n  \nGets or sets the style to use for the [DecoratorContainer](#decoratorcontainer-class).  \n  \n```csharp  \npublic Style DecoratorContainerStyle { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Style](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Style)  \n  \n#### Decorators  \n  \nGets or sets the items that will be rendered in the decorators layer via [DecoratorContainer](#decoratorcontainer-class)s.  \n  \n```csharp  \npublic IEnumerable Decorators { get; set; }  \n```  \n  \n**Property Value**  \n  \n[IEnumerable](https://docs.microsoft.com/en-us/dotnet/api/System.Collections.IEnumerable)  \n  \n#### DecoratorsExtent  \n  \nThe area covered by the [DecoratorContainer](#decoratorcontainer-class)s.  \n  \n```csharp  \npublic Rect DecoratorsExtent { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Rect](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Rect)  \n  \n#### DecoratorTemplate  \n  \nGets or sets the [DataTemplate](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.DataTemplate) to use when generating a new [DecoratorContainer](#decoratorcontainer-class).  \n  \n```csharp  \npublic DataTemplate DecoratorTemplate { get; set; }  \n```  \n  \n**Property Value**  \n  \n[DataTemplate](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.DataTemplate)  \n  \n#### DisableAutoPanning  \n  \nGets or sets whether to disable the auto panning when selecting or dragging near the edge of the editor configured by [NodifyEditor.AutoPanEdgeDistance](#nodifyeditor-class#autopanedgedistance).  \n  \n```csharp  \npublic bool DisableAutoPanning { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean)  \n  \n#### DisablePanning  \n  \nGets or sets whether panning should be disabled.  \n  \n```csharp  \npublic bool DisablePanning { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean)  \n  \n#### DisableZooming  \n  \nGets or sets whether zooming should be disabled.  \n  \n```csharp  \npublic bool DisableZooming { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean)  \n  \n#### DisconnectConnectorCommand  \n  \nInvoked when the [Connector.Disconnect](#connector-class#disconnect) event is raised. \n            Can also be handled at the [Connector](#connector-class) level using the [Connector.DisconnectCommand](#connector-class#disconnectcommand) command. \n            Parameter is the [Connector](#connector-class)'s [FrameworkElement.DataContext](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.FrameworkElement.datacontext).  \n  \n```csharp  \npublic ICommand DisconnectConnectorCommand { get; set; }  \n```  \n  \n**Property Value**  \n  \n[ICommand](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.ICommand)  \n  \n#### DisplayConnectionsOnTop  \n  \nGets or sets whether to display connections on top of [ItemContainer](#itemcontainer-class)s or not.  \n  \n```csharp  \npublic bool DisplayConnectionsOnTop { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean)  \n  \n#### EnableCuttingLinePreview  \n  \nGets or sets whether the cutting line should apply the preview style to the interesected elements.  \n  \n```csharp  \npublic static bool EnableCuttingLinePreview { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean)  \n  \n#### EnableDraggingContainersOptimizations  \n  \nGets or sets if the current position of containers that are being dragged should not be committed until the end of the dragging operation.  \n  \n```csharp  \npublic static bool EnableDraggingContainersOptimizations { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean)  \n  \n#### EnableRealtimeSelection  \n  \nEnables selecting and deselecting items while the [NodifyEditor.SelectedArea](#nodifyeditor-class#selectedarea) changes.\n            Disable for maximum performance when hundreds of items are generated.  \n  \n```csharp  \npublic bool EnableRealtimeSelection { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean)  \n  \n#### EnableRenderingContainersOptimizations  \n  \nGets or sets if [NodifyEditor](#nodifyeditor-class)s should enable optimizations based on [NodifyEditor.OptimizeRenderingMinimumContainers](#nodifyeditor-class#optimizerenderingminimumcontainers) and [NodifyEditor.OptimizeRenderingZoomOutPercent](#nodifyeditor-class#optimizerenderingzoomoutpercent).  \n  \n```csharp  \npublic static bool EnableRenderingContainersOptimizations { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean)  \n  \n#### EnableSnappingCorrection  \n  \nCorrect [ItemContainer](#itemcontainer-class)'s position after moving if starting position is not snapped to grid.  \n  \n```csharp  \npublic static bool EnableSnappingCorrection { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean)  \n  \n#### FitToScreenExtentMargin  \n  \nGets or sets the margin to add in all directions to the [NodifyEditor.ItemsExtent](#nodifyeditor-class#itemsextent) or area parameter when using Nodify.NodifyEditor.FitToScreen(System.Nullable{System.Windows.Rect}).  \n  \n```csharp  \npublic static double FitToScreenExtentMargin { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Double](https://docs.microsoft.com/en-us/dotnet/api/System.Double)  \n  \n#### GridCellSize  \n  \nGets or sets the value of an invisible grid used to adjust locations (snapping) of [ItemContainer](#itemcontainer-class)s.  \n  \n```csharp  \npublic uint GridCellSize { get; set; }  \n```  \n  \n**Property Value**  \n  \n[UInt32](https://docs.microsoft.com/en-us/dotnet/api/System.UInt32)  \n  \n#### HandleRightClickAfterPanningThreshold  \n  \nGets or sets the maximum number of pixels allowed to move the mouse before cancelling the mouse event.\n            Useful for System.Windows.Controls.ContextMenus to appear if mouse only moved a bit or not at all.  \n  \n```csharp  \npublic static double HandleRightClickAfterPanningThreshold { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Double](https://docs.microsoft.com/en-us/dotnet/api/System.Double)  \n  \n#### IsBulkUpdatingItems  \n  \nTells if the [NodifyEditor](#nodifyeditor-class) is doing operations on multiple items at once.  \n  \n```csharp  \npublic bool IsBulkUpdatingItems { get; protected set; }  \n```  \n  \n**Property Value**  \n  \n[Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean)  \n  \n#### IsCutting  \n  \nGets a value that indicates whether a cutting operation is in progress.  \n  \n```csharp  \npublic bool IsCutting { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean)  \n  \n#### IsPanning  \n  \nGets a value that indicates whether a panning operation is in progress.  \n  \n```csharp  \npublic bool IsPanning { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean)  \n  \n#### IsPushingItems  \n  \nGets a value that indicates whether a pushing operation is in progress.  \n  \n```csharp  \npublic bool IsPushingItems { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean)  \n  \n#### IsSelecting  \n  \nGets a value that indicates whether a selection operation is in progress.  \n  \n```csharp  \npublic bool IsSelecting { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean)  \n  \n#### ItemsDragCompletedCommand  \n  \nInvoked when a drag operation is completed for the [NodifyEditor.SelectedItems](#nodifyeditor-class#selecteditems).  \n  \n```csharp  \npublic ICommand ItemsDragCompletedCommand { get; set; }  \n```  \n  \n**Property Value**  \n  \n[ICommand](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.ICommand)  \n  \n#### ItemsDragStartedCommand  \n  \nInvoked when a drag operation starts for the [NodifyEditor.SelectedItems](#nodifyeditor-class#selecteditems).  \n  \n```csharp  \npublic ICommand ItemsDragStartedCommand { get; set; }  \n```  \n  \n**Property Value**  \n  \n[ICommand](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.ICommand)  \n  \n#### ItemsExtent  \n  \nThe area covered by the [ItemContainer](#itemcontainer-class)s.  \n  \n```csharp  \npublic Rect ItemsExtent { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Rect](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Rect)  \n  \n#### ItemsSelectCompletedCommand  \n  \nInvoked when a selection operation is completed.  \n  \n```csharp  \npublic ICommand ItemsSelectCompletedCommand { get; set; }  \n```  \n  \n**Property Value**  \n  \n[ICommand](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.ICommand)  \n  \n#### ItemsSelectStartedCommand  \n  \nInvoked when a selection operation is started.  \n  \n```csharp  \npublic ICommand ItemsSelectStartedCommand { get; set; }  \n```  \n  \n**Property Value**  \n  \n[ICommand](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.ICommand)  \n  \n#### MaxViewportZoom  \n  \nGets or sets the maximum zoom factor of the viewport  \n  \n```csharp  \npublic double MaxViewportZoom { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Double](https://docs.microsoft.com/en-us/dotnet/api/System.Double)  \n  \n#### MinViewportZoom  \n  \nGets or sets the minimum zoom factor of the viewport  \n  \n```csharp  \npublic double MinViewportZoom { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Double](https://docs.microsoft.com/en-us/dotnet/api/System.Double)  \n  \n#### MouseLocation  \n  \nGets the current mouse location in graph space coordinates (relative to the [NodifyEditor.ItemsHost](#nodifyeditor-class#itemshost)).  \n  \n```csharp  \npublic Point MouseLocation { get; protected set; }  \n```  \n  \n**Property Value**  \n  \n[Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n#### OptimizeRenderingMinimumContainers  \n  \nGets or sets the minimum number of [ItemContainer](#itemcontainer-class)s needed to trigger optimizations when reaching the [NodifyEditor.OptimizeRenderingZoomOutPercent](#nodifyeditor-class#optimizerenderingzoomoutpercent).  \n  \n```csharp  \npublic static uint OptimizeRenderingMinimumContainers { get; set; }  \n```  \n  \n**Property Value**  \n  \n[UInt32](https://docs.microsoft.com/en-us/dotnet/api/System.UInt32)  \n  \n#### OptimizeRenderingZoomOutPercent  \n  \nGets or sets the minimum zoom out percent needed to start optimizing the rendering for [ItemContainer](#itemcontainer-class)s.\n            Value is between 0 and 1.  \n  \n```csharp  \npublic static double OptimizeRenderingZoomOutPercent { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Double](https://docs.microsoft.com/en-us/dotnet/api/System.Double)  \n  \n#### PendingConnection  \n  \nGets of sets the [FrameworkElement.DataContext](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.FrameworkElement.datacontext) of the [PendingConnection](#pendingconnection-class).  \n  \n```csharp  \npublic object PendingConnection { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object)  \n  \n#### PendingConnectionTemplate  \n  \nGets or sets the [DataTemplate](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.DataTemplate) to use for the [NodifyEditor.PendingConnection](#nodifyeditor-class#pendingconnection).  \n  \n```csharp  \npublic DataTemplate PendingConnectionTemplate { get; set; }  \n```  \n  \n**Property Value**  \n  \n[DataTemplate](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.DataTemplate)  \n  \n#### PushedArea  \n  \nGets the currently pushed area while [NodifyEditor.IsPushingItems](#nodifyeditor-class#ispushingitems) is true.  \n  \n```csharp  \npublic Rect PushedArea { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Rect](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Rect)  \n  \n#### PushedAreaOrientation  \n  \nGets the orientation of the [NodifyEditor.PushedArea](#nodifyeditor-class#pushedarea).  \n  \n```csharp  \npublic Orientation PushedAreaOrientation { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Orientation](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Controls.Orientation)  \n  \n#### PushedAreaStyle  \n  \nGets or sets the style to use for the pushed area.  \n  \n```csharp  \npublic Style PushedAreaStyle { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Style](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Style)  \n  \n#### RemoveConnectionCommand  \n  \nInvoked when the [BaseConnection.Disconnect](#baseconnection-class#disconnect) event is raised. \n            Can also be handled at the [BaseConnection](#baseconnection-class) level using the [BaseConnection.DisconnectCommand](#baseconnection-class#disconnectcommand) command. \n            Parameter is the [BaseConnection](#baseconnection-class)'s [FrameworkElement.DataContext](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.FrameworkElement.datacontext).  \n  \n```csharp  \npublic ICommand RemoveConnectionCommand { get; set; }  \n```  \n  \n**Property Value**  \n  \n[ICommand](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.ICommand)  \n  \n#### ScrollIncrement  \n  \nThe number of units the mouse wheel is rotated to scroll one line.  \n  \n```csharp  \npublic static double ScrollIncrement { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Double](https://docs.microsoft.com/en-us/dotnet/api/System.Double)  \n  \n#### SelectedArea  \n  \nGets the currently selected area while [NodifyEditor.IsSelecting](#nodifyeditor-class#isselecting) is true.  \n  \n```csharp  \npublic Rect SelectedArea { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Rect](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Rect)  \n  \n#### SelectedConnection  \n  \nGets or sets the selected connection.  \n  \n```csharp  \npublic object SelectedConnection { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object)  \n  \n#### SelectedConnections  \n  \nGets or sets the selected connections in the [NodifyEditor](#nodifyeditor-class).  \n  \n```csharp  \npublic IList SelectedConnections { get; set; }  \n```  \n  \n**Property Value**  \n  \n[IList](https://docs.microsoft.com/en-us/dotnet/api/System.Collections.IList)  \n  \n#### SelectedItems  \n  \nGets or sets the selected items in the [NodifyEditor](#nodifyeditor-class).  \n  \n```csharp  \npublic IList SelectedItems { get; set; }  \n```  \n  \n**Property Value**  \n  \n[IList](https://docs.microsoft.com/en-us/dotnet/api/System.Collections.IList)  \n  \n#### SelectionRectangleStyle  \n  \nGets or sets the style to use for the selection rectangle.  \n  \n```csharp  \npublic Style SelectionRectangleStyle { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Style](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Style)  \n  \n#### State  \n  \nThe current state of the editor.  \n  \n```csharp  \npublic EditorState State { get; set; }  \n```  \n  \n**Property Value**  \n  \n[EditorState](#editorstate-class)  \n  \n#### ViewportLocation  \n  \nGets or sets the viewport's top-left coordinates in graph space coordinates.  \n  \n```csharp  \npublic Point ViewportLocation { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n#### ViewportSize  \n  \nGets the size of the viewport in graph space (scaled by the [NodifyEditor.ViewportZoom](#nodifyeditor-class#viewportzoom)).  \n  \n```csharp  \npublic Size ViewportSize { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Size](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Size)  \n  \n#### ViewportTransform  \n  \nGets the transform that is applied to all child controls.  \n  \n```csharp  \npublic Transform ViewportTransform { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Transform](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Media.Transform)  \n  \n#### ViewportZoom  \n  \nGets or sets the zoom factor of the viewport.  \n  \n```csharp  \npublic double ViewportZoom { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Double](https://docs.microsoft.com/en-us/dotnet/api/System.Double)  \n  \n### Methods  \n  \n#### BringIntoView(Point, Boolean, Action)  \n  \nMoves the viewport center at the specified location.  \n  \n```csharp  \npublic void BringIntoView(Point point, bool animated = true, Action onFinish = null);  \n```  \n  \n**Parameters**  \n  \n`point` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point): The location in graph space coordinates.  \n  \n`animated` [Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean): True to animate the movement.  \n  \n`onFinish` [Action](https://docs.microsoft.com/en-us/dotnet/api/System.Action): The callback invoked when movement is finished.  \n  \n#### BringIntoView(Rect)  \n  \nMoves the viewport center at the center of the specified area.  \n  \n```csharp  \npublic void BringIntoView(Rect area);  \n```  \n  \n**Parameters**  \n  \n`area` [Rect](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Rect): The location in graph space coordinates.  \n  \n#### FitToScreen(Rect?)  \n  \nScales the viewport to fit the specified area or all the [ItemContainer](#itemcontainer-class)s if that's possible.  \n  \n```csharp  \npublic void FitToScreen(Rect? area = null);  \n```  \n  \n**Parameters**  \n  \n`area` [Rect?](https://docs.microsoft.com/en-us/dotnet/api/System.Nullable)  \n  \n#### GetContainerForItemOverride()  \n  \n```csharp  \nprotected override DependencyObject GetContainerForItemOverride();  \n```  \n  \n**Returns**  \n  \n[DependencyObject](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.DependencyObject)  \n  \n#### GetInitialState()  \n  \nCreates the initial state of the editor.  \n  \n```csharp  \nprotected virtual EditorState GetInitialState();  \n```  \n  \n**Returns**  \n  \n[EditorState](#editorstate-class): The initial state.  \n  \n#### GetLocationInsideEditor(Point, UIElement)  \n  \nTranslates the specified location to graph space coordinates (relative to the [NodifyEditor.ItemsHost](#nodifyeditor-class#itemshost)).  \n  \n```csharp  \npublic Point GetLocationInsideEditor(Point location, UIElement relativeTo);  \n```  \n  \n**Parameters**  \n  \n`location` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point): The location coordinates relative to relativeTo  \n  \n`relativeTo` [UIElement](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.UIElement): The element where the location was calculated from.  \n  \n**Returns**  \n  \n[Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point): A location inside the graph.  \n  \n#### GetLocationInsideEditor(DragEventArgs)  \n  \nTranslates the event location to graph space coordinates (relative to the [NodifyEditor.ItemsHost](#nodifyeditor-class#itemshost)).  \n  \n```csharp  \npublic Point GetLocationInsideEditor(DragEventArgs args);  \n```  \n  \n**Parameters**  \n  \n`args` [DragEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.DragEventArgs): The drag event.  \n  \n**Returns**  \n  \n[Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point): A location inside the graph  \n  \n#### GetLocationInsideEditor(MouseEventArgs)  \n  \nTranslates the event location to graph space coordinates (relative to the [NodifyEditor.ItemsHost](#nodifyeditor-class#itemshost)).  \n  \n```csharp  \npublic Point GetLocationInsideEditor(MouseEventArgs args);  \n```  \n  \n**Parameters**  \n  \n`args` [MouseEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.MouseEventArgs): The mouse event.  \n  \n**Returns**  \n  \n[Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point): A location inside the graph  \n  \n#### InvertSelection(Rect, Boolean)  \n  \nInverts the [ItemContainer](#itemcontainer-class)s selection in the specified area.  \n  \n```csharp  \npublic void InvertSelection(Rect area, bool fit = false);  \n```  \n  \n**Parameters**  \n  \n`area` [Rect](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Rect): The area to look for [ItemContainer](#itemcontainer-class)s.  \n  \n`fit` [Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean): True to check if the area contains the [ItemContainer](#itemcontainer-class). False to check if area intersects the [ItemContainer](#itemcontainer-class).  \n  \n#### IsItemItsOwnContainerOverride(Object)  \n  \n```csharp  \nprotected override bool IsItemItsOwnContainerOverride(object item);  \n```  \n  \n**Parameters**  \n  \n`item` [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object)  \n  \n**Returns**  \n  \n[Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean)  \n  \n#### OnApplyTemplate()  \n  \n```csharp  \npublic override void OnApplyTemplate();  \n```  \n  \n#### OnDisableAutoPanningChanged(Boolean)  \n  \nCalled when the [NodifyEditor.DisableAutoPanning](#nodifyeditor-class#disableautopanning) changes.  \n  \n```csharp  \nprotected virtual void OnDisableAutoPanningChanged(bool shouldDisable);  \n```  \n  \n**Parameters**  \n  \n`shouldDisable` [Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean): Whether to enable or disable auto panning.  \n  \n#### OnKeyDown(KeyEventArgs)  \n  \n```csharp  \nprotected override void OnKeyDown(KeyEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [KeyEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.KeyEventArgs)  \n  \n#### OnKeyUp(KeyEventArgs)  \n  \n```csharp  \nprotected override void OnKeyUp(KeyEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [KeyEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.KeyEventArgs)  \n  \n#### OnLostMouseCapture(MouseEventArgs)  \n  \n```csharp  \nprotected override void OnLostMouseCapture(MouseEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [MouseEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.MouseEventArgs)  \n  \n#### OnMouseDown(MouseButtonEventArgs)  \n  \n```csharp  \nprotected override void OnMouseDown(MouseButtonEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [MouseButtonEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.MouseButtonEventArgs)  \n  \n#### OnMouseMove(MouseEventArgs)  \n  \n```csharp  \nprotected override void OnMouseMove(MouseEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [MouseEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.MouseEventArgs)  \n  \n#### OnMouseUp(MouseButtonEventArgs)  \n  \n```csharp  \nprotected override void OnMouseUp(MouseButtonEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [MouseButtonEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.MouseButtonEventArgs)  \n  \n#### OnMouseWheel(MouseWheelEventArgs)  \n  \n```csharp  \nprotected override void OnMouseWheel(MouseWheelEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [MouseWheelEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.MouseWheelEventArgs)  \n  \n#### OnRemoveConnection(Object)  \n  \n```csharp  \nprotected void OnRemoveConnection(object dataContext);  \n```  \n  \n**Parameters**  \n  \n`dataContext` [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object)  \n  \n#### OnRenderSizeChanged(SizeChangedInfo)  \n  \n```csharp  \nprotected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo);  \n```  \n  \n**Parameters**  \n  \n`sizeInfo` [SizeChangedInfo](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.SizeChangedInfo)  \n  \n#### OnSelectionChanged(SelectionChangedEventArgs)  \n  \n```csharp  \nprotected override void OnSelectionChanged(SelectionChangedEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [SelectionChangedEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Controls.SelectionChangedEventArgs)  \n  \n#### OnViewportUpdated()  \n  \nUpdates the [NodifyEditor.ViewportSize](#nodifyeditor-class#viewportsize) and raises the [NodifyEditor.ViewportUpdatedEvent](#nodifyeditor-class#viewportupdatedevent).\n            Called when the [UIElement.RenderSize](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.UIElement.rendersize) or [NodifyEditor.ViewportZoom](#nodifyeditor-class#viewportzoom) is changed.  \n  \n```csharp  \nprotected void OnViewportUpdated();  \n```  \n  \n#### PopAllStates()  \n  \nPops all states from the editor.  \n  \n```csharp  \npublic void PopAllStates();  \n```  \n  \n#### PopState()  \n  \nPops the current [NodifyEditor.State](#nodifyeditor-class#state) from the stack.  \n  \n```csharp  \npublic void PopState();  \n```  \n  \n#### PushState(EditorState)  \n  \nPushes the given state to the stack.  \n  \n```csharp  \npublic void PushState(EditorState state);  \n```  \n  \n**Parameters**  \n  \n`state` [EditorState](#editorstate-class): The new state of the editor.  \n  \n#### SelectAllConnections()  \n  \nSelect all [NodifyEditor.Connections](#nodifyeditor-class#connections).  \n  \n```csharp  \npublic void SelectAllConnections();  \n```  \n  \n#### SelectArea(Rect, Boolean, Boolean)  \n  \nSelects the [ItemContainer](#itemcontainer-class)s in the specified area.  \n  \n```csharp  \npublic void SelectArea(Rect area, bool append = false, bool fit = false);  \n```  \n  \n**Parameters**  \n  \n`area` [Rect](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Rect): The area to look for [ItemContainer](#itemcontainer-class)s.  \n  \n`append` [Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean): If true, it will add to the existing selection.  \n  \n`fit` [Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean): True to check if the area contains the [ItemContainer](#itemcontainer-class). False to check if area intersects the [ItemContainer](#itemcontainer-class).  \n  \n#### SnapToGrid(Double)  \n  \nSnaps the given value down to the nearest multiple of the grid cell size.  \n  \n```csharp  \npublic double SnapToGrid(double value);  \n```  \n  \n**Parameters**  \n  \n`value` [Double](https://docs.microsoft.com/en-us/dotnet/api/System.Double): The value to be snapped to the grid.  \n  \n**Returns**  \n  \n[Double](https://docs.microsoft.com/en-us/dotnet/api/System.Double): The largest multiple of the grid cell size less than or equal to the value.  \n  \n#### UnselectAllConnection()  \n  \nUnselect all [NodifyEditor.Connections](#nodifyeditor-class#connections).  \n  \n```csharp  \npublic void UnselectAllConnection();  \n```  \n  \n#### UnselectArea(Rect, Boolean)  \n  \nUnselect the [ItemContainer](#itemcontainer-class)s in the specified area.  \n  \n```csharp  \npublic void UnselectArea(Rect area, bool fit = false);  \n```  \n  \n**Parameters**  \n  \n`area` [Rect](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Rect): The area to look for [ItemContainer](#itemcontainer-class)s.  \n  \n`fit` [Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean): True to check if the area contains the [ItemContainer](#itemcontainer-class). False to check if area intersects the [ItemContainer](#itemcontainer-class).  \n  \n#### ZoomAtPosition(Double, Point)  \n  \nZoom at the specified location in graph space coordinates.  \n  \n```csharp  \npublic void ZoomAtPosition(double zoom, Point location);  \n```  \n  \n**Parameters**  \n  \n`zoom` [Double](https://docs.microsoft.com/en-us/dotnet/api/System.Double): The zoom factor.  \n  \n`location` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point): The location to focus when zooming.  \n  \n#### ZoomIn()  \n  \nZoom in at the viewports center  \n  \n```csharp  \npublic void ZoomIn();  \n```  \n  \n#### ZoomOut()  \n  \nZoom out at the viewports center  \n  \n```csharp  \npublic void ZoomOut();  \n```  \n  \n### Events  \n  \n#### ViewportUpdated  \n  \nOccurs whenever the viewport updates.  \n  \n```csharp  \npublic event RoutedEventHandler ViewportUpdated;  \n```  \n  \n**Event Type**  \n  \n[RoutedEventHandler](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.RoutedEventHandler)  \n  \n## NodifyEditorGestures Class  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**Inheritance:** [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object) → [NodifyEditorGestures](#nodifyeditorgestures-class)  \n  \n**References:** [EditorGestures](#editorgestures-class), [InputGestureRef](#inputgestureref-class), [SelectionGestures](#selectiongestures-class)  \n  \n```csharp  \npublic class NodifyEditorGestures  \n```  \n  \n### Constructors  \n  \n#### NodifyEditorGestures()  \n  \n```csharp  \npublic NodifyEditorGestures();  \n```  \n  \n### Properties  \n  \n#### CancelAction  \n  \n```csharp  \npublic InputGestureRef CancelAction { get; set; }  \n```  \n  \n**Property Value**  \n  \n[InputGestureRef](#inputgestureref-class)  \n  \n#### Cutting  \n  \n```csharp  \npublic InputGestureRef Cutting { get; set; }  \n```  \n  \n**Property Value**  \n  \n[InputGestureRef](#inputgestureref-class)  \n  \n#### FitToScreen  \n  \n```csharp  \npublic InputGestureRef FitToScreen { get; set; }  \n```  \n  \n**Property Value**  \n  \n[InputGestureRef](#inputgestureref-class)  \n  \n#### Pan  \n  \n```csharp  \npublic InputGestureRef Pan { get; set; }  \n```  \n  \n**Property Value**  \n  \n[InputGestureRef](#inputgestureref-class)  \n  \n#### PanHorizontalModifierKey  \n  \n```csharp  \npublic ModifierKeys PanHorizontalModifierKey { get; set; }  \n```  \n  \n**Property Value**  \n  \n[ModifierKeys](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.ModifierKeys)  \n  \n#### PanVerticalModifierKey  \n  \n```csharp  \npublic ModifierKeys PanVerticalModifierKey { get; set; }  \n```  \n  \n**Property Value**  \n  \n[ModifierKeys](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.ModifierKeys)  \n  \n#### PanWithMouseWheel  \n  \n```csharp  \npublic bool PanWithMouseWheel { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean)  \n  \n#### PushItems  \n  \n```csharp  \npublic InputGestureRef PushItems { get; set; }  \n```  \n  \n**Property Value**  \n  \n[InputGestureRef](#inputgestureref-class)  \n  \n#### ResetViewportLocation  \n  \n```csharp  \npublic InputGestureRef ResetViewportLocation { get; set; }  \n```  \n  \n**Property Value**  \n  \n[InputGestureRef](#inputgestureref-class)  \n  \n#### Selection  \n  \n```csharp  \npublic SelectionGestures Selection { get; set; }  \n```  \n  \n**Property Value**  \n  \n[SelectionGestures](#selectiongestures-class)  \n  \n#### ZoomIn  \n  \n```csharp  \npublic InputGestureRef ZoomIn { get; set; }  \n```  \n  \n**Property Value**  \n  \n[InputGestureRef](#inputgestureref-class)  \n  \n#### ZoomModifierKey  \n  \n```csharp  \npublic ModifierKeys ZoomModifierKey { get; set; }  \n```  \n  \n**Property Value**  \n  \n[ModifierKeys](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.ModifierKeys)  \n  \n#### ZoomOut  \n  \n```csharp  \npublic InputGestureRef ZoomOut { get; set; }  \n```  \n  \n**Property Value**  \n  \n[InputGestureRef](#inputgestureref-class)  \n  \n### Methods  \n  \n#### Apply(NodifyEditorGestures)  \n  \n```csharp  \npublic void Apply(NodifyEditorGestures gestures);  \n```  \n  \n**Parameters**  \n  \n`gestures` [NodifyEditorGestures](#nodifyeditorgestures-class)  \n  \n## PendingConnection Class  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**Inheritance:** [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object) → [DispatcherObject](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Threading.DispatcherObject) → [DependencyObject](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.DependencyObject) → [Visual](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Media.Visual) → [UIElement](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.UIElement) → [FrameworkElement](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.FrameworkElement) → [Control](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Controls.Control) → [ContentControl](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Controls.ContentControl) → [PendingConnection](#pendingconnection-class)  \n  \n**References:** [ConnectionDirection](#connectiondirection-enum), [Connector](#connector-class), [ItemContainer](#itemcontainer-class), [NodifyEditor](#nodifyeditor-class), [PendingConnectionEventArgs](#pendingconnectioneventargs-class), [PendingConnectionEventHandler](#pendingconnectioneventhandler-delegate), [StateNode](#statenode-class)  \n  \nRepresents a pending connection usually started by a [Connector](#connector-class) which invokes the [PendingConnection.CompletedCommand](#pendingconnection-class#completedcommand) when completed.  \n  \n```csharp  \npublic class PendingConnection : ContentControl  \n```  \n  \n### Constructors  \n  \n#### PendingConnection()  \n  \n```csharp  \npublic PendingConnection();  \n```  \n  \n### Properties  \n  \n#### AllowOnlyConnectors  \n  \nIf true will preview and connect only to [Connector](#connector-class)s, otherwise will also enable [ItemContainer](#itemcontainer-class)s.  \n  \n```csharp  \npublic bool AllowOnlyConnectors { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean)  \n  \n#### CompletedCommand  \n  \nGets or sets the command to invoke when the pending connection is completed.\n            Will not be invoked if [NodifyEditor.ConnectionCompletedCommand](#nodifyeditor-class#connectioncompletedcommand) is used.\n            [PendingConnection.Target](#pendingconnection-class#target) will be set to the desired [Connector](#connector-class)'s [FrameworkElement.DataContext](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.FrameworkElement.datacontext) and will also be the command's parameter.  \n  \n```csharp  \npublic ICommand CompletedCommand { get; set; }  \n```  \n  \n**Property Value**  \n  \n[ICommand](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.ICommand)  \n  \n#### Direction  \n  \nGets or sets the direction of this connection.  \n  \n```csharp  \npublic ConnectionDirection Direction { get; set; }  \n```  \n  \n**Property Value**  \n  \n[ConnectionDirection](#connectiondirection-enum)  \n  \n#### Editor  \n  \nGets the [NodifyEditor](#nodifyeditor-class) that owns this [PendingConnection](#pendingconnection-class).  \n  \n```csharp  \nprotected NodifyEditor Editor { get; set; }  \n```  \n  \n**Property Value**  \n  \n[NodifyEditor](#nodifyeditor-class)  \n  \n#### EnablePreview  \n  \n[PendingConnection.PreviewTarget](#pendingconnection-class#previewtarget) will be updated with a potential [Connector](#connector-class)'s [FrameworkElement.DataContext](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.FrameworkElement.datacontext) if this is true.  \n  \n```csharp  \npublic bool EnablePreview { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean)  \n  \n#### EnableSnapping  \n  \nEnables snapping the [PendingConnection.TargetAnchor](#pendingconnection-class#targetanchor) to a possible [PendingConnection.Target](#pendingconnection-class#target) connector.  \n  \n```csharp  \npublic bool EnableSnapping { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean)  \n  \n#### IsVisible  \n  \nGets or sets the visibility of the connection.  \n  \n```csharp  \npublic bool IsVisible { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean)  \n  \n#### PreviewTarget  \n  \nGets or sets the [Connector](#connector-class) or the [ItemContainer](#itemcontainer-class) (if [PendingConnection.AllowOnlyConnectors](#pendingconnection-class#allowonlyconnectors) is false) that we're previewing.  \n  \n```csharp  \npublic object PreviewTarget { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object)  \n  \n#### Source  \n  \nGets or sets the [Connector](#connector-class)'s [FrameworkElement.DataContext](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.FrameworkElement.datacontext) that started this pending connection.  \n  \n```csharp  \npublic object Source { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object)  \n  \n#### SourceAnchor  \n  \nGets or sets the starting point for the connection.  \n  \n```csharp  \npublic Point SourceAnchor { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n#### StartedCommand  \n  \nGets or sets the command to invoke when the pending connection is started.\n            Will not be invoked if [NodifyEditor.ConnectionStartedCommand](#nodifyeditor-class#connectionstartedcommand) is used.\n            [PendingConnection.Source](#pendingconnection-class#source) will be set to the [Connector](#connector-class)'s [FrameworkElement.DataContext](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.FrameworkElement.datacontext) that started this connection and will also be the command's parameter.  \n  \n```csharp  \npublic ICommand StartedCommand { get; set; }  \n```  \n  \n**Property Value**  \n  \n[ICommand](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.ICommand)  \n  \n#### Stroke  \n  \nGets or sets the stroke color of the connection.  \n  \n```csharp  \npublic Brush Stroke { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Brush](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Media.Brush)  \n  \n#### StrokeDashArray  \n  \nGets or sets the pattern of dashes and gaps that is used to outline the connection.  \n  \n```csharp  \npublic DoubleCollection StrokeDashArray { get; set; }  \n```  \n  \n**Property Value**  \n  \n[DoubleCollection](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Media.DoubleCollection)  \n  \n#### StrokeThickness  \n  \nGets or set the connection thickness.  \n  \n```csharp  \npublic double StrokeThickness { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Double](https://docs.microsoft.com/en-us/dotnet/api/System.Double)  \n  \n#### Target  \n  \nGets or sets the [Connector](#connector-class)'s [FrameworkElement.DataContext](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.FrameworkElement.datacontext) (or potentially an [ItemContainer](#itemcontainer-class)'s [FrameworkElement.DataContext](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.FrameworkElement.datacontext) if [PendingConnection.AllowOnlyConnectors](#pendingconnection-class#allowonlyconnectors) is false) that the [PendingConnection.Source](#pendingconnection-class#source) can connect to.\n            Only set when the connection is completed (see [PendingConnection.CompletedCommand](#pendingconnection-class#completedcommand)).  \n  \n```csharp  \npublic object Target { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object)  \n  \n#### TargetAnchor  \n  \nGets or sets the end point for the connection.  \n  \n```csharp  \npublic Point TargetAnchor { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n### Methods  \n  \n#### GetIsOverElement(UIElement)  \n  \n```csharp  \npublic static bool GetIsOverElement(UIElement elem);  \n```  \n  \n**Parameters**  \n  \n`elem` [UIElement](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.UIElement)  \n  \n**Returns**  \n  \n[Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean)  \n  \n#### OnApplyTemplate()  \n  \n```csharp  \npublic override void OnApplyTemplate();  \n```  \n  \n#### OnPendingConnectionCompleted(Object, PendingConnectionEventArgs)  \n  \n```csharp  \nprotected virtual void OnPendingConnectionCompleted(object sender, PendingConnectionEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`sender` [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object)  \n  \n`e` [PendingConnectionEventArgs](#pendingconnectioneventargs-class)  \n  \n#### OnPendingConnectionDrag(Object, PendingConnectionEventArgs)  \n  \n```csharp  \nprotected virtual void OnPendingConnectionDrag(object sender, PendingConnectionEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`sender` [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object)  \n  \n`e` [PendingConnectionEventArgs](#pendingconnectioneventargs-class)  \n  \n#### OnPendingConnectionStarted(Object, PendingConnectionEventArgs)  \n  \n```csharp  \nprotected virtual void OnPendingConnectionStarted(object sender, PendingConnectionEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`sender` [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object)  \n  \n`e` [PendingConnectionEventArgs](#pendingconnectioneventargs-class)  \n  \n#### SetIsOverElement(UIElement, Boolean)  \n  \n```csharp  \npublic static void SetIsOverElement(UIElement elem, bool value);  \n```  \n  \n**Parameters**  \n  \n`elem` [UIElement](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.UIElement)  \n  \n`value` [Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean)  \n  \n## PendingConnectionEventArgs Class  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**Inheritance:** [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object) → [EventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.EventArgs) → [RoutedEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.RoutedEventArgs) → [PendingConnectionEventArgs](#pendingconnectioneventargs-class)  \n  \n**References:** [Connector](#connector-class), [PendingConnection](#pendingconnection-class), [PendingConnectionEventHandler](#pendingconnectioneventhandler-delegate)  \n  \nProvides data for [PendingConnection](#pendingconnection-class) related routed events.  \n  \n```csharp  \npublic class PendingConnectionEventArgs : RoutedEventArgs  \n```  \n  \n### Constructors  \n  \n#### PendingConnectionEventArgs(Object)  \n  \nInitializes a new instance of the [PendingConnectionEventArgs](#pendingconnectioneventargs-class) class using the specified [PendingConnectionEventArgs.SourceConnector](#pendingconnectioneventargs-class#sourceconnector).  \n  \n```csharp  \npublic PendingConnectionEventArgs(object sourceConnector);  \n```  \n  \n**Parameters**  \n  \n`sourceConnector` [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object): The [FrameworkElement.DataContext](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.FrameworkElement.datacontext) of a related [Connector](#connector-class).  \n  \n### Properties  \n  \n#### Anchor  \n  \nGets or sets the [Connector.Anchor](#connector-class#anchor) of the [Connector](#connector-class) that raised this event.  \n  \n```csharp  \npublic Point Anchor { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n#### Canceled  \n  \nGets or sets a value that indicates whether this [PendingConnection](#pendingconnection-class) was cancelled.  \n  \n```csharp  \npublic bool Canceled { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Boolean](https://docs.microsoft.com/en-us/dotnet/api/System.Boolean)  \n  \n#### OffsetX  \n  \nGets or sets the distance from the [PendingConnectionEventArgs.SourceConnector](#pendingconnectioneventargs-class#sourceconnector) in the X axis.  \n  \n```csharp  \npublic double OffsetX { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Double](https://docs.microsoft.com/en-us/dotnet/api/System.Double)  \n  \n#### OffsetY  \n  \nGets or sets the distance from the [PendingConnectionEventArgs.SourceConnector](#pendingconnectioneventargs-class#sourceconnector) in the Y axis.  \n  \n```csharp  \npublic double OffsetY { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Double](https://docs.microsoft.com/en-us/dotnet/api/System.Double)  \n  \n#### SourceConnector  \n  \nGets the [FrameworkElement.DataContext](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.FrameworkElement.datacontext) of the [Connector](#connector-class) that started this [PendingConnection](#pendingconnection-class).  \n  \n```csharp  \npublic object SourceConnector { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object)  \n  \n#### TargetConnector  \n  \nGets or sets the [FrameworkElement.DataContext](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.FrameworkElement.datacontext) of the target [Connector](#connector-class) when the [PendingConnection](#pendingconnection-class) is completed.  \n  \n```csharp  \npublic object TargetConnector { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object)  \n  \n### Methods  \n  \n#### InvokeEventHandler(Delegate, Object)  \n  \n```csharp  \nprotected override void InvokeEventHandler(Delegate genericHandler, object genericTarget);  \n```  \n  \n**Parameters**  \n  \n`genericHandler` [Delegate](https://docs.microsoft.com/en-us/dotnet/api/System.Delegate)  \n  \n`genericTarget` [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object)  \n  \n## PendingConnectionEventHandler Delegate  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**Inheritance:** [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object) → [Delegate](https://docs.microsoft.com/en-us/dotnet/api/System.Delegate) → [MulticastDelegate](https://docs.microsoft.com/en-us/dotnet/api/System.MulticastDelegate) → [PendingConnectionEventHandler](#pendingconnectioneventhandler-delegate)  \n  \n**References:** [Connector](#connector-class), [PendingConnection](#pendingconnection-class), [PendingConnectionEventArgs](#pendingconnectioneventargs-class)  \n  \nRepresents the method that will handle [PendingConnection](#pendingconnection-class) related routed events.  \n  \n```csharp  \npublic delegate void PendingConnectionEventHandler(object sender, PendingConnectionEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`sender` [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object): The object where the event handler is attached.  \n  \n`e` [PendingConnectionEventArgs](#pendingconnectioneventargs-class): The event data.  \n  \n## PreviewLocationChanged Delegate  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**Inheritance:** [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object) → [Delegate](https://docs.microsoft.com/en-us/dotnet/api/System.Delegate) → [MulticastDelegate](https://docs.microsoft.com/en-us/dotnet/api/System.MulticastDelegate) → [PreviewLocationChanged](#previewlocationchanged-delegate)  \n  \n**References:** [ItemContainer](#itemcontainer-class)  \n  \nDelegate used to notify when an [ItemContainer](#itemcontainer-class) is previewing a new location.  \n  \n```csharp  \npublic delegate void PreviewLocationChanged(Point newLocation);  \n```  \n  \n**Parameters**  \n  \n`newLocation` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point): The new location.  \n  \n## ResizeEventArgs Class  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**Inheritance:** [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object) → [EventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.EventArgs) → [RoutedEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.RoutedEventArgs) → [ResizeEventArgs](#resizeeventargs-class)  \n  \n**References:** [ResizeEventHandler](#resizeeventhandler-delegate)  \n  \nProvides data for resize related routed events.  \n  \n```csharp  \npublic class ResizeEventArgs : RoutedEventArgs  \n```  \n  \n### Constructors  \n  \n#### ResizeEventArgs(Size, Size)  \n  \nInitializes a new instance of the [ResizeEventArgs](#resizeeventargs-class) class with the previous and the new [Size](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Size).  \n  \n```csharp  \npublic ResizeEventArgs(Size previousSize, Size newSize);  \n```  \n  \n**Parameters**  \n  \n`previousSize` [Size](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Size): The previous size associated with this event.  \n  \n`newSize` [Size](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Size): The new size associated with this event.  \n  \n### Properties  \n  \n#### NewSize  \n  \nGets the new size of the object.  \n  \n```csharp  \npublic Size NewSize { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Size](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Size)  \n  \n#### PreviousSize  \n  \nGets the previous size of the object.  \n  \n```csharp  \npublic Size PreviousSize { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Size](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Size)  \n  \n### Methods  \n  \n#### InvokeEventHandler(Delegate, Object)  \n  \n```csharp  \nprotected override void InvokeEventHandler(Delegate genericHandler, object genericTarget);  \n```  \n  \n**Parameters**  \n  \n`genericHandler` [Delegate](https://docs.microsoft.com/en-us/dotnet/api/System.Delegate)  \n  \n`genericTarget` [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object)  \n  \n## ResizeEventHandler Delegate  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**Inheritance:** [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object) → [Delegate](https://docs.microsoft.com/en-us/dotnet/api/System.Delegate) → [MulticastDelegate](https://docs.microsoft.com/en-us/dotnet/api/System.MulticastDelegate) → [ResizeEventHandler](#resizeeventhandler-delegate)  \n  \n**References:** [GroupingNode](#groupingnode-class), [ResizeEventArgs](#resizeeventargs-class)  \n  \nRepresents the method that will handle resize related routed events.  \n  \n```csharp  \npublic delegate void ResizeEventHandler(object sender, ResizeEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`sender` [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object): The sender of this event.  \n  \n`e` [ResizeEventArgs](#resizeeventargs-class): The event data.  \n  \n## SelectionGestures Class  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**Inheritance:** [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object) → [SelectionGestures](#selectiongestures-class)  \n  \n**References:** [ConnectionGestures](#connectiongestures-class), [InputGestureRef](#inputgestureref-class), [ItemContainerGestures](#itemcontainergestures-class), [NodifyEditorGestures](#nodifyeditorgestures-class)  \n  \n```csharp  \npublic class SelectionGestures  \n```  \n  \n### Constructors  \n  \n#### SelectionGestures(MouseAction)  \n  \n```csharp  \npublic SelectionGestures(MouseAction mouseAction);  \n```  \n  \n**Parameters**  \n  \n`mouseAction` [MouseAction](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.MouseAction)  \n  \n#### SelectionGestures()  \n  \n```csharp  \npublic SelectionGestures();  \n```  \n  \n### Fields  \n  \n#### None  \n  \n```csharp  \npublic static SelectionGestures None;  \n```  \n  \n**Field Value**  \n  \n[SelectionGestures](#selectiongestures-class)  \n  \n### Properties  \n  \n#### Append  \n  \n```csharp  \npublic InputGestureRef Append { get; set; }  \n```  \n  \n**Property Value**  \n  \n[InputGestureRef](#inputgestureref-class)  \n  \n#### Cancel  \n  \n```csharp  \npublic InputGestureRef Cancel { get; set; }  \n```  \n  \n**Property Value**  \n  \n[InputGestureRef](#inputgestureref-class)  \n  \n#### Invert  \n  \n```csharp  \npublic InputGestureRef Invert { get; set; }  \n```  \n  \n**Property Value**  \n  \n[InputGestureRef](#inputgestureref-class)  \n  \n#### Remove  \n  \n```csharp  \npublic InputGestureRef Remove { get; set; }  \n```  \n  \n**Property Value**  \n  \n[InputGestureRef](#inputgestureref-class)  \n  \n#### Replace  \n  \n```csharp  \npublic InputGestureRef Replace { get; set; }  \n```  \n  \n**Property Value**  \n  \n[InputGestureRef](#inputgestureref-class)  \n  \n#### Select  \n  \n```csharp  \npublic InputGestureRef Select { get; set; }  \n```  \n  \n**Property Value**  \n  \n[InputGestureRef](#inputgestureref-class)  \n  \n### Methods  \n  \n#### Apply(SelectionGestures)  \n  \n```csharp  \npublic void Apply(SelectionGestures gestures);  \n```  \n  \n**Parameters**  \n  \n`gestures` [SelectionGestures](#selectiongestures-class)  \n  \n## SelectionHelper Class  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**Inheritance:** [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object) → [SelectionHelper](#selectionhelper-class)  \n  \n**References:** [EditorSelectingState](#editorselectingstate-class), [ItemContainer](#itemcontainer-class), [NodifyEditor](#nodifyeditor-class), [SelectionType](#selectiontype-enum)  \n  \nHelps with selecting [ItemContainer](#itemcontainer-class)s and updating the [NodifyEditor.SelectedArea](#nodifyeditor-class#selectedarea) and [NodifyEditor.IsSelecting](#nodifyeditor-class#isselecting) properties.  \n  \n```csharp  \npublic sealed class SelectionHelper  \n```  \n  \n### Constructors  \n  \n#### SelectionHelper(NodifyEditor)  \n  \nConstructs a new instance of a [SelectionHelper](#selectionhelper-class).  \n  \n```csharp  \npublic SelectionHelper(NodifyEditor host);  \n```  \n  \n**Parameters**  \n  \n`host` [NodifyEditor](#nodifyeditor-class): The editor to select items from.  \n  \n### Methods  \n  \n#### Abort()  \n  \nAborts the current selection.  \n  \n```csharp  \npublic void Abort();  \n```  \n  \n#### End()  \n  \nCommits the current selection to the editor.  \n  \n```csharp  \npublic void End();  \n```  \n  \n#### Start(Point, SelectionType)  \n  \nAttempts to start a new selection.  \n  \n```csharp  \npublic void Start(Point location, SelectionType selectionType);  \n```  \n  \n**Parameters**  \n  \n`location` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point): The location inside the graph.  \n  \n`selectionType` [SelectionType](#selectiontype-enum): The type of selection.  \n  \n#### Update(Point)  \n  \nUpdate the end location for the selection.  \n  \n```csharp  \npublic void Update(Point endLocation);  \n```  \n  \n**Parameters**  \n  \n`endLocation` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point): An absolute location.  \n  \n## SelectionType Enum  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**References:** [EditorSelectingState](#editorselectingstate-class), [SelectionHelper](#selectionhelper-class)  \n  \n```csharp  \npublic enum SelectionType  \n```  \n  \n### Fields  \n  \n#### Append  \n  \n```csharp  \nAppend = 2;  \n```  \n  \n#### Invert  \n  \n```csharp  \nInvert = 3;  \n```  \n  \n#### Remove  \n  \n```csharp  \nRemove = 1;  \n```  \n  \n#### Replace  \n  \n```csharp  \nReplace = 0;  \n```  \n  \n## StateNode Class  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**Inheritance:** [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object) → [DispatcherObject](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Threading.DispatcherObject) → [DependencyObject](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.DependencyObject) → [Visual](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Media.Visual) → [UIElement](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.UIElement) → [FrameworkElement](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.FrameworkElement) → [Control](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Controls.Control) → [Connector](#connector-class) → [StateNode](#statenode-class)  \n  \n**References:** [PendingConnection](#pendingconnection-class)  \n  \nRepresents a control that acts as a [Connector](#connector-class).  \n  \n```csharp  \npublic class StateNode : Connector  \n```  \n  \n### Constructors  \n  \n#### StateNode()  \n  \n```csharp  \npublic StateNode();  \n```  \n  \n### Fields  \n  \n#### ElementContent  \n  \n```csharp  \nprotected const string ElementContent = \"PART_Content\";  \n```  \n  \n**Field Value**  \n  \n[String](https://docs.microsoft.com/en-us/dotnet/api/System.String)  \n  \n### Properties  \n  \n#### Content  \n  \nGets or sets the data for the control's content.  \n  \n```csharp  \npublic object Content { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object)  \n  \n#### ContentControl  \n  \nGets the [StateNode.ContentControl](#statenode-class#contentcontrol) control of this [StateNode](#statenode-class).  \n  \n```csharp  \nprotected UIElement ContentControl { get; set; }  \n```  \n  \n**Property Value**  \n  \n[UIElement](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.UIElement)  \n  \n#### ContentTemplate  \n  \nGets or sets the template used to display the content of the control's header.  \n  \n```csharp  \npublic DataTemplate ContentTemplate { get; set; }  \n```  \n  \n**Property Value**  \n  \n[DataTemplate](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.DataTemplate)  \n  \n#### CornerRadius  \n  \nGets or sets a value that represents the degree to which the corners of the [StateNode](#statenode-class) are rounded.  \n  \n```csharp  \npublic CornerRadius CornerRadius { get; set; }  \n```  \n  \n**Property Value**  \n  \n[CornerRadius](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.CornerRadius)  \n  \n#### HighlightBrush  \n  \nGets or sets the brush used when the [PendingConnection.IsOverElementProperty](#pendingconnection-class#isoverelementproperty) attached property is true for this [StateNode](#statenode-class).  \n  \n```csharp  \npublic Brush HighlightBrush { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Brush](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Media.Brush)  \n  \n### Methods  \n  \n#### OnApplyTemplate()  \n  \n```csharp  \npublic override void OnApplyTemplate();  \n```  \n  \n#### OnMouseDown(MouseButtonEventArgs)  \n  \n```csharp  \nprotected override void OnMouseDown(MouseButtonEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [MouseButtonEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.MouseButtonEventArgs)  \n  \n#### OnMouseUp(MouseButtonEventArgs)  \n  \n```csharp  \nprotected override void OnMouseUp(MouseButtonEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`e` [MouseButtonEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Input.MouseButtonEventArgs)  \n  \n## StepConnection Class  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**Inheritance:** [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object) → [DispatcherObject](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Threading.DispatcherObject) → [DependencyObject](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.DependencyObject) → [Visual](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Media.Visual) → [UIElement](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.UIElement) → [FrameworkElement](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.FrameworkElement) → [Shape](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Shapes.Shape) → [BaseConnection](#baseconnection-class) → [LineConnection](#lineconnection-class) → [StepConnection](#stepconnection-class)  \n  \n**References:** [ConnectorPosition](#connectorposition-enum)  \n  \n```csharp  \npublic class StepConnection : LineConnection  \n```  \n  \n### Constructors  \n  \n#### StepConnection()  \n  \n```csharp  \npublic StepConnection();  \n```  \n  \n### Properties  \n  \n#### SourcePosition  \n  \nGets or sets the position of the source connector.  \n  \n```csharp  \npublic ConnectorPosition SourcePosition { get; set; }  \n```  \n  \n**Property Value**  \n  \n[ConnectorPosition](#connectorposition-enum)  \n  \n#### TargetPosition  \n  \nGets or sets the position of the target connector.  \n  \n```csharp  \npublic ConnectorPosition TargetPosition { get; set; }  \n```  \n  \n**Property Value**  \n  \n[ConnectorPosition](#connectorposition-enum)  \n  \n### Methods  \n  \n#### DrawDirectionalArrowsGeometry(StreamGeometryContext, Point, Point)  \n  \n```csharp  \nprotected override void DrawDirectionalArrowsGeometry(StreamGeometryContext context, Point source, Point target);  \n```  \n  \n**Parameters**  \n  \n`context` [StreamGeometryContext](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Media.StreamGeometryContext)  \n  \n`source` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n`target` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n#### DrawLineGeometry(StreamGeometryContext, Point, Point)  \n  \n```csharp  \nprotected override ValueTuple<ValueTuple<Point, Point>, ValueTuple<Point, Point>> DrawLineGeometry(StreamGeometryContext context, Point source, Point target);  \n```  \n  \n**Parameters**  \n  \n`context` [StreamGeometryContext](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Media.StreamGeometryContext)  \n  \n`source` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n`target` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n**Returns**  \n  \n[ValueTuple<ValueTuple<Point, Point>, ValueTuple<Point, Point>>](https://docs.microsoft.com/en-us/dotnet/api/System.ValueTuple)  \n  \n#### GetTextPosition(FormattedText, Point, Point)  \n  \n```csharp  \nprotected override Point GetTextPosition(FormattedText text, Point source, Point target);  \n```  \n  \n**Parameters**  \n  \n`text` [FormattedText](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Media.FormattedText)  \n  \n`source` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n`target` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n**Returns**  \n  \n[Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n## ZoomEventArgs Class  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**Inheritance:** [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object) → [EventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.EventArgs) → [RoutedEventArgs](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.RoutedEventArgs) → [ZoomEventArgs](#zoomeventargs-class)  \n  \n**References:** [Minimap](#minimap-class), [ZoomEventHandler](#zoomeventhandler-delegate)  \n  \nProvides data for [Minimap.Zoom](#minimap-class#zoom) routed event.  \n  \n```csharp  \npublic class ZoomEventArgs : RoutedEventArgs  \n```  \n  \n### Constructors  \n  \n#### ZoomEventArgs(Double, Point)  \n  \nInitializes a new instance of the [ZoomEventArgs](#zoomeventargs-class) class using the specified [ZoomEventArgs.Zoom](#zoomeventargs-class#zoom) and [ZoomEventArgs.Location](#zoomeventargs-class#location).  \n  \n```csharp  \npublic ZoomEventArgs(double zoom, Point location);  \n```  \n  \n**Parameters**  \n  \n`zoom` [Double](https://docs.microsoft.com/en-us/dotnet/api/System.Double)  \n  \n`location` [Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n### Properties  \n  \n#### Location  \n  \nGets the location where the editor should zoom in.  \n  \n```csharp  \npublic Point Location { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Point](https://docs.microsoft.com/en-us/dotnet/api/System.Windows.Point)  \n  \n#### Zoom  \n  \nGets the zoom amount.  \n  \n```csharp  \npublic double Zoom { get; set; }  \n```  \n  \n**Property Value**  \n  \n[Double](https://docs.microsoft.com/en-us/dotnet/api/System.Double)  \n  \n### Methods  \n  \n#### InvokeEventHandler(Delegate, Object)  \n  \n```csharp  \nprotected override void InvokeEventHandler(Delegate genericHandler, object genericTarget);  \n```  \n  \n**Parameters**  \n  \n`genericHandler` [Delegate](https://docs.microsoft.com/en-us/dotnet/api/System.Delegate)  \n  \n`genericTarget` [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object)  \n  \n## ZoomEventHandler Delegate  \n  \n**Namespace:** Nodify  \n  \n**Assembly:** Nodify  \n  \n**Inheritance:** [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object) → [Delegate](https://docs.microsoft.com/en-us/dotnet/api/System.Delegate) → [MulticastDelegate](https://docs.microsoft.com/en-us/dotnet/api/System.MulticastDelegate) → [ZoomEventHandler](#zoomeventhandler-delegate)  \n  \n**References:** [Minimap](#minimap-class), [ZoomEventArgs](#zoomeventargs-class)  \n  \nRepresents the method that will handle [Minimap.Zoom](#minimap-class#zoom) routed event.  \n  \n```csharp  \npublic delegate void ZoomEventHandler(object sender, ZoomEventArgs e);  \n```  \n  \n**Parameters**  \n  \n`sender` [Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object): The object where the event handler is attached.  \n  \n`e` [ZoomEventArgs](#zoomeventargs-class): The event data.  \n  \n"
  },
  {
    "path": "docs/localization/zh-CN/_Sidebar.md",
    "content": "| [English](https://github.com/WYihei/nodify/wiki/Home) | [简体中文](https://github.com/WYihei/nodify/wiki/主页)\n| --- | --- |\n***\n## [主页](主页)\n\n[开始](开始)\n- [层次结构和术语](开始#层次结构和术语)\n- [内容层](开始#内容层)\n- [使用现有主题](开始#使用现有主题)\n- [一个小案例](开始#一个小案例)\n- [绘制轴网](开始#绘制轴网)\n\n[编辑器概述](编辑器概述)\n- [移动视口](编辑器概述#平移)\n- [缩放视口](编辑器概述#缩放)\n- [选择项目](编辑器概述#选择)\n- [对齐轴网](编辑器概述#对齐)\n- [命令](编辑器概述#命令)\n\n[项目容器概述](项目容器概述)\n- [选择](项目容器概述#选择)\n- [移动](项目容器概述#移动和拖拽)\n\n[节点概述](节点概述)\n- [节点](节点概述#1-node-控件)\n- [组节点](节点概述#2-groupingnode-控件)\n- [结节点](节点概述#3-knotnode-控件)\n- [状态节点](节点概述#4-statenode-控件)\n\n[连接概述](连接概述)\n- [基本连接](连接概述#基本连接)\n- [预备连接](连接概述#预备连接)\n\n[连接器概述](连接器概述)\n\n[API 参考](API-Reference)\n\n[(问答) 常见问题](问答)"
  },
  {
    "path": "docs/localization/zh-CN/主页.md",
    "content": "\n![editor-interaction](https://user-images.githubusercontent.com/12727904/192004838-ec6dd997-5e64-4c01-940c-1cd1b8d27837.gif)\n\n\n# 欢迎来到Nodify!\n\nNodify是一个WPF基于节点的[编辑器控件](编辑器概述)，其中包含一系列[节点](节点概述)、[连接](连接概述)和[连接器](连接器概述)组件，旨在简化构建基于节点的工具的过程。\n\n这是受虚幻引擎的[蓝图视觉脚本](https://docs.unrealengine.com/en-US/ProgrammingAndScripting/Blueprints/index.html)系统启发，但仅专注于用户界面和用户交互部分。与蓝图不同，Nodify是一个通用库，提供了一个节点图编辑器组件，可以嵌入到任何 WPF 应用程序中。\n\n该图形编辑器是一个无限区域，您可以在其中放置和移动节点，选择和拖动节点组，连接和断开节点或连接器，放大和缩小，以及在将节点或导线拖动到边缘附近时自动移动屏幕等。\n\nNodify功能丰富，经过优化，可以同时与数百个节点进行交互，并且...它是为与MVVM一起工作而重新架构的。\n\n### 要求\n![IDE](https://img.shields.io/static/v1?label=%20&message=VS%202019%20or%20greater&color=informational&style=for-the-badge&logo=visual-studio)\n\n![C#](https://img.shields.io/static/v1?label=%20&message=8.0&color=239120&style=for-the-badge&logo=c-sharp)\n\n![.NET](https://img.shields.io/static/v1?label=%20&message=Framework%204.7.2%20to%20NET%206&color=5C2D91&style=for-the-badge&logo=.net)\n\n### 从Nuget安装Nodify\n[![Download Package](https://img.shields.io/nuget/v/Nodify?label=Download&logo=nuget&style=for-the-badge)](https://www.nuget.org/packages/Nodify/)\n\n```xml\nInstall-Package Nodify\n```\n\n### 应用案例\n- [Playground](https://github.com/miroiu/nodify/tree/master/Examples/Nodify.Playground)\n- [State machine](https://github.com/miroiu/nodify/tree/master/Examples/Nodify.StateMachine)\n- [Calculator](https://github.com/miroiu/nodify/tree/master/Examples/Nodify.Calculator)\n\n\n[![IDE](https://img.shields.io/static/v1?label=%20&message=GET%20STARTED&color=9cf&style=for-the-badge)](开始)\n\n"
  },
  {
    "path": "docs/localization/zh-CN/开始.md",
    "content": "了解组件的命名方式及其在编辑器可视化树中的角色对于理解代码和文档非常重要。\n\n## 层次结构和术语\n\n根组件是一个[编辑器（editor）](编辑器概述)，它包含[节点（nodes）](节点概述)和[连接（connections）](连接概述)以及一些额外的UI元素，如[选择框（selection rectangle）](编辑器概述#选择)和一个[预备连接（pending connection）](连接概述#预备连接)，以使编辑器具有交互性。\n\n节点是[连接器(connectors)](连接器概述)的容器，有时候节点本身也可以作为连接器(比如 [状态节点](节点概述#4-状态节点(state-node)控件)).\n\n连接器可以创建预备连接，预备连接在完成后可以成为实际的连接。\n\n_一图胜千言_\n\n![nodes-hierarchy](https://user-images.githubusercontent.com/12727904/192028123-e2847f29-6517-4731-8672-f5d8356dead0.png)\n\n## 内容层\n\n你可能会好奇，一个节点如何既能作为连接器本身又能像普通节点一样运行。编辑器包含三个主要层次，这些层次有助于解决这个问题：\n\n1. 项目层（NodifyEditor.ItemsSource）——在这里，每个控件都被包装在一个容器中，使其可以选择、拖动等，并且可以渲染任何控件（例如连接器、文本块）\n2. 连接层（NodifyEditor.Connections）——这是所有连接共存的地方，并默认在项目层下面渲染。\n3. 装饰层（NodifyEditor.Decorators）——在这里，每个控件在窗口中都有一个位置。\n\n将这些层次分开，使得每个层次可以异步加载成为可能。\n\n## 使用现有主题\n\n将以下其中一个主题合并到 App.xaml 中的资源字典中：\n\n- 深色主题（如果未指定，则为默认主题）：\n```xml\n<ResourceDictionary Source=\"pack://application:,,,/Nodify;component/Themes/Dark.xaml\" />\n```\n\n- 浅色主题：\n```xml\n<ResourceDictionary Source=\"pack://application:,,,/Nodify;component/Themes/Light.xaml\" />\n```\n\n- Nodify主题：\n```xml\n<ResourceDictionary Source=\"pack://application:,,,/Nodify;component/Themes/Nodify.xaml\" />\n```\n\n## 一个小案例\n\n导入 `nodify` 命名空间：`xmlns:nodify=\"https://miroiu.github.io/nodify\"` 或 `xmlns:nodify=\"clr-namespace:Nodify;assembly=Nodify\"` 到你的文件中，并创建一个编辑器实例 `<nodify:NodifyEditor />`。如果你启动应用程序，你会看到一个可以创建选择矩形的空白区域。\n> 提示：将选择矩形拖动到编辑器区域的边缘附近，屏幕将自动向该方向移动。\n\n### 添加节点（nodes）\n\n现在我们将显示一些节点。让我们创建视图模型并将它们绑定到视图。\n\n```csharp\npublic class NodeViewModel\n{\n    public string Title { get; set; }\n}\n\npublic class EditorViewModel\n{\n    public ObservableCollection<NodeViewModel> Nodes { get; } = new ObservableCollection<NodeViewModel>();\n\n    public EditorViewModel()\n    {\n        Nodes.Add(new NodeViewModel { Title = \"Welcome\" });\n    }\n}\n```\n\n视图模型可以是任何形状，但节点的视图由 `ItemTemplate` 生成。（将 DataTemplate 放在 NodifyEditor.Resources 中也能实现相同的效果）\n\n```xml\n<nodify:NodifyEditor ItemsSource=\"{Binding Nodes}\">\n    <nodify:NodifyEditor.DataContext>\n        <local:EditorViewModel />\n    </nodify:NodifyEditor.DataContext>\n\n    <nodify:NodifyEditor.ItemTemplate>\n        <DataTemplate DataType=\"{x:Type local:NodeViewModel}\">\n            <nodify:Node Header=\"{Binding Title}\" />\n        </DataTemplate>\n    </nodify:NodifyEditor.ItemTemplate>\n    \n</nodify:NodifyEditor>\n```\n\n请注意，我们绑定 Node 的 Header 属性来显示 Title。要了解更多节点类型和自定义，请查看[节点概述](节点概述)。\n\n### 连接节点（nodes）\n\n好的，现在让我们添加更多节点并将它们连接起来。首先，我们需要一个连接器的表示以及节点上一些集合来存储我们的连接器。\n\n```csharp \npublic class ConnectorViewModel\n{\n    public string Title { get; set; }\n}\n\npublic class NodeViewModel\n{\n    public string Title { get; set; }\n\n    public ObservableCollection<ConnectorViewModel> Input { get; set; } = new ObservableCollection<ConnectorViewModel>();\n    public ObservableCollection<ConnectorViewModel> Output { get; set; } = new ObservableCollection<ConnectorViewModel>();\n}\n\npublic class EditorViewModel\n{\n    public ObservableCollection<NodeViewModel> Nodes { get; } = new ObservableCollection<NodeViewModel>();\n\n    public EditorViewModel()\n    {\n        Nodes.Add(new NodeViewModel\n        {\n            Title = \"Welcome\",\n            Input = new ObservableCollection<ConnectorViewModel>\n            {\n                new ConnectorViewModel\n                {\n                    Title = \"In\"\n                }\n            },\n            Output = new ObservableCollection<ConnectorViewModel>\n            {\n                new ConnectorViewModel\n                {\n                    Title = \"Out\"\n                }\n            }\n        });\n    }\n}\n```\n然后将它们绑定到视图。（我们使用了内置的 `NodeInput` 和 `NodeOutput` 作为视图，但也有[其他连接器](连接器概述)。或者根据需要创建自己的连接器。）\n\n```xml\n<nodify:Node Header=\"{Binding Title}\"\n             Input=\"{Binding Input}\"\n             Output=\"{Binding Output}\">\n  <nodify:Node.InputConnectorTemplate>\n      <DataTemplate DataType=\"{x:Type local:ConnectorViewModel}\">\n          <nodify:NodeInput Header=\"{Binding Title}\" />\n      </DataTemplate>\n  </nodify:Node.InputConnectorTemplate>\n  \n  <nodify:Node.OutputConnectorTemplate>\n      <DataTemplate DataType=\"{x:Type local:ConnectorViewModel}\">\n          <nodify:NodeOutput Header=\"{Binding Title}\" />\n      </DataTemplate>\n  </nodify:Node.OutputConnectorTemplate>\n</nodify:Node>\n```\n`Node` 控件支持 `Input` 和 `Output` 连接器，您可以通过重写 `InputConnectorTemplate` 和 `OutputConnectorTemplate` 的默认模板来自定义这些连接器。\n\n从 `Input` 或 `Output` 连接器点击并拖动一根线将创建一个[预备连接](连接概述#预备连接)，我们可以将其转换为实际连接。\n\n**Nodify 最复杂的部分是如何将连接绑定到它们的连接器。** 让我们为连接创建 ViewModel，并在 `EditorViewModel` 中添加连接列表。\n\n```csharp\npublic class ConnectionViewModel\n{\n    public ConnectorViewModel Source { get; set; }\n    public ConnectorViewModel Target { get; set; }\n}\n\npublic class EditorViewModel\n{\n    public ObservableCollection<NodeViewModel> Nodes { get; } = new ObservableCollection<NodeViewModel>();\n    public ObservableCollection<ConnectionViewModel> Connections { get; } = new ObservableCollection<ConnectionViewModel>();\n\n    public EditorViewModel()\n    {\n        var welcome = new NodeViewModel\n        {\n            Title = \"Welcome\",\n            Input = new ObservableCollection<ConnectorViewModel>\n            {\n                new ConnectorViewModel\n                {\n                    Title = \"In\"\n                }\n            },\n            Output = new ObservableCollection<ConnectorViewModel>\n            {\n                new ConnectorViewModel\n                {\n                    Title = \"Out\"\n                }\n            }\n        };\n\n        var nodify = new NodeViewModel\n        {\n            Title = \"To Nodify\",\n            Input = new ObservableCollection<ConnectorViewModel>\n            {\n                new ConnectorViewModel\n                {\n                    Title = \"In\"\n                }\n            }\n        };\n\n        Nodes.Add(welcome);\n        Nodes.Add(nodify);\n\n        Connections.Add(new ConnectionViewModel\n        {\n            Source = welcome.Output[0],\n            Target = nodify.Input[0]\n        });\n    }\n}\n```\n然后更新 `ConnectorViewModel` 以具有连接可以附加的 `Anchor` 点。（这需要是响应式的，因此我们将在视图模型中实现 INotifyPropertyChanged 接口）。\n\n> 注意：Point 类型必须来自 System.Windows。\n\n```csharp\npublic class ConnectorViewModel : INotifyPropertyChanged\n{\n    private Point _anchor;\n    public Point Anchor\n    {\n        set\n        {\n            _anchor = value;\n            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Anchor)));\n        }\n        get => _anchor;\n    }\n\n    public string Title { get; set; }\n\n    public event PropertyChangedEventHandler PropertyChanged;\n}\n```\n\n将 `Anchor` 绑定到连接器的视图，并设置 `Mode=OneWayToSource`。还需将 `IsConnected` 设置为 `True` 以接收 `Anchor` 更新。\n\n```xml\n<nodify:Node.InputConnectorTemplate>\n    <DataTemplate DataType=\"{x:Type local:ConnectorViewModel}\">\n        <nodify:NodeInput Header=\"{Binding Title}\"\n                          IsConnected=\"True\"\n                          Anchor=\"{Binding Anchor, Mode=OneWayToSource}\" />\n    </DataTemplate>\n</nodify:Node.InputConnectorTemplate>\n\n<nodify:Node.OutputConnectorTemplate>\n    <DataTemplate DataType=\"{x:Type local:ConnectorViewModel}\">\n        <nodify:NodeOutput Header=\"{Binding Title}\"\n                           IsConnected=\"True\"\n                           Anchor=\"{Binding Anchor, Mode=OneWayToSource}\"  />\n    </DataTemplate>\n</nodify:Node.OutputConnectorTemplate>\n```\n并将连接绑定到视图，让它们在 `ConnectionTemplate` 中使用我们 `ConnectorViewModel` 的 `Anchor`。有关更多自定义，请参阅[连接概述](连接概述)。\n\n```xml\n<nodify:NodifyEditor ItemsSource=\"{Binding Nodes}\"\n                     Connections=\"{Binding Connections}\">\n    ...\n    <nodify:NodifyEditor.ConnectionTemplate>\n        <DataTemplate DataType=\"{x:Type local:ConnectionViewModel}\">\n            <nodify:LineConnection Source=\"{Binding Source.Anchor}\"\n                                   Target=\"{Binding Target.Anchor}\" />\n        </DataTemplate>\n    </nodify:NodifyEditor.ConnectionTemplate>\n    ...\n```\n\n如果你现在启动应用程序，你会看到有一个连接，并且如果你拖动节点，连接会跟随它们移动。\n\n现在让我们在 `ConnectorViewModel` 中添加 `IsConnected` 属性，以便在实际连接时设置它。并更新 `ConnectionViewModel` 以便在构造时自动连接它们。\n\n```csharp\npublic class ConnectorViewModel : INotifyPropertyChanged\n{\n    private Point _anchor;\n    public Point Anchor\n    {\n        set\n        {\n            _anchor = value;\n            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Anchor)));\n        }\n        get => _anchor;\n    }\n\n    private bool _isConnected;\n    public bool IsConnected\n    {\n        set\n        {\n            _isConnected = value;\n            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IsConnected)));\n        }\n        get => _isConnected;\n    }\n\n    public string Title { get; set; }\n\n    public event PropertyChangedEventHandler PropertyChanged;\n}\n\npublic class ConnectionViewModel\n{\n    public ConnectionViewModel(ConnectorViewModel source, ConnectorViewModel target)\n    {\n        Source = source;\n        Target = target;\n\n        Source.IsConnected = true;\n        Target.IsConnected = true;\n    }\n\n    public ConnectorViewModel Source { get; }\n    public ConnectorViewModel Target { get; }\n}\n```\n\n并且不要忘记在连接器模板中绑定它。\n\n```xml\nIsConnected=\"{Binding IsConnected}\"\n```\n\n### 将预备连接转换为实际连接。\n\n[预备连接](连接概述#预备连接)从一个 `Source` 开始，当放置到一个 `Target` 上时将完成。源*始终*是一个连接器，目标可以是一个[连接器](连接器概述)、一个[项目容器](项目容器概述)或 `null`。我们现在只关心其他连接器。当连接开始时，执行 `StartedCommand`，该命令接收 `Source` 作为参数。当连接完成时，执行 `CompletedCommand`，该命令接收 `Target` 作为参数。\n\n让我们实现预备连接的视图模型，并将其添加到 `EditorViewModel` 中。\n\n```csharp\npublic class PendingConnectionViewModel\n{\n    private readonly EditorViewModel _editor;\n    private ConnectorViewModel _source;\n\n    public PendingConnectionViewModel(EditorViewModel editor)\n    {\n        _editor = editor;\n        StartCommand = new DelegateCommand<ConnectorViewModel>(source => _source = source);\n        FinishCommand = new DelegateCommand<ConnectorViewModel>(target =>\n        {\n            if (target != null)\n                _editor.Connect(_source, target);\n        });\n    }\n\n    public ICommand StartCommand { get; }\n    public ICommand FinishCommand { get; }\n}\n\npublic class EditorViewModel\n{\n    public PendingConnectionViewModel PendingConnection { get; }\n\n    ...\n\n    public EditorViewModel()\n    {\n        PendingConnection = new PendingConnectionViewModel(this);\n        ...\n    }\n\n    ...\n\n    public void Connect(ConnectorViewModel source, ConnectorViewModel target)\n    {\n        Connections.Add(new ConnectionViewModel(source, target));\n    }\n}\n```\n\n并将其绑定到视图上\n\n```xml\n<nodify:NodifyEditor PendingConnection=\"{Binding PendingConnection}\">\n...\n    <nodify:NodifyEditor.PendingConnectionTemplate>\n        <DataTemplate DataType=\"{x:Type local:PendingConnectionViewModel}\">\n            <nodify:PendingConnection StartedCommand=\"{Binding StartCommand}\"\n                                      CompletedCommand=\"{Binding FinishCommand}\"\n                                      AllowOnlyConnectors=\"True\" />\n        </DataTemplate>\n    </nodify:NodifyEditor.PendingConnectionTemplate>\n...\n</nodify:NodifyEditor>\n```\n这就是创建连接的全部内容。现在你应该可以在连接器之间创建连接了。\n\n### 移除连接\n\n要删除连接，只需监听来自连接器本身或编辑器的断开连接事件，并删除具有连接器作为源或目标的连接。为了简单起见，我们将为 `NodifyEditor` 实现 `DisconnectConnectorCommand`。首先让我们将其添加到 `EditorViewModel`。\n\n```csharp\npublic class EditorViewModel\n{\n    public ICommand DisconnectConnectorCommand { get; }\n\n    ...\n\n    public EditorViewModel()\n    {\n        DisconnectConnectorCommand = new DelegateCommand<ConnectorViewModel>(connector =>\n        {\n            var connection = Connections.First(x => x.Source == connector || x.Target == connector);\n            connection.Source.IsConnected = false;  // This is not correct if there are multiple connections to the same connector\n            connection.Target.IsConnected = false;\n            Connections.Remove(connection);\n        });\n\n        ...\n    }\n}\n```\n\n现在我们将此命令绑定到编辑器试图上。\n\n```xml\n<nodify:NodifyEditor ItemsSource=\"{Binding Nodes}\"\n                     Connections=\"{Binding Connections}\"\n                     PendingConnection=\"{Binding PendingConnection}\"\n                     DisconnectConnectorCommand=\"{Binding DisconnectConnectorCommand}\">\n  ...\n  \n</nodify:NodifyEditor>\n```\n\n### 控制节点位置\n\n如你所见，节点总是在屏幕的左上角。这是因为它们在图中的位置是 (0, 0)。让我们来改变这一点！\n\n在 `NodeViewModel` 中添加一个 `Location` 属性，类型为 `System.Windows.Point`，并触发 `PropertyChanged` 事件。\n\n```csharp\npublic class NodeViewModel : INotifyPropertyChanged\n{\n    private Point _location;\n    public Point Location\n    {\n        set\n        {\n            _location = value;\n            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Location)));\n        }\n        get => _location;\n    }\n\n    public event PropertyChangedEventHandler PropertyChanged;\n\n    ...\n}\n```\n\n并将其绑定到视图\n\n```xml\n<nodify:NodifyEditor ItemsSource=\"{Binding Nodes}\"\n                     Connections=\"{Binding Connections}\"\n                     PendingConnection=\"{Binding PendingConnection}\">\n\n    <nodify:NodifyEditor.ItemContainerStyle>\n        <Style TargetType=\"{x:Type nodify:ItemContainer}\">\n            <Setter Property=\"Location\" \n                    Value=\"{Binding Location}\" />\n        </Style>\n    </nodify:NodifyEditor.ItemContainerStyle>\n\n    ...\n\n</nodify:NodifyEditor>\n```\n\n> 注意：我使用了 ItemContainerStyle 来绑定节点的位置。请查看[项目容器概述](项目容器概述)获取更多信息。\n\n现在你可以在构造节点时设置它们的位置。\n\n## 绘制轴网\n\n绘制简单的网格只需创建一个网格画笔，同时将编辑器的变换持续应用于它，并将该画笔用作编辑器的 `Background`。\n\n因为我们绘制的网格是由线条组成的，而不是填充的，所以编辑器的 `Background` 将具有一些透明度，这意味着我们会看到下面控件的背景颜色。为了解决这个问题，将编辑器包装在一个 `Grid` 中，并设置其 `Background`，或者设置 `Window` 的 `Background`。\n\n使用 `ViewportTransform` 依赖属性使网格随视图移动。\n\n> 注意：示例使用了在 `App.xaml` 中选择的主题提供的静态资源。\n\n```xml\n<Window x:Class=\"MyProject.MainWindow\"\n        xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n        xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n        xmlns:d=\"http://schemas.microsoft.com/expression/blend/2008\"\n        xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\"\n        xmlns:nodify=\"https://miroiu.github.io/nodify\"\n        mc:Ignorable=\"d\">\n\n    <Window.Resources>\n        <GeometryDrawing x:Key=\"SmallGridGeometry\"\n                        Geometry=\"M0,0 L0,1 0.03,1 0.03,0.03 1,0.03 1,0 Z\"\n                        Brush=\"{StaticResource NodifyEditor.SelectionRectangleBackgroundBrush}\" />\n\n        <GeometryDrawing x:Key=\"LargeGridGeometry\"\n                        Geometry=\"M0,0 L0,1 0.015,1 0.015,0.015 1,0.015 1,0 Z\"\n                        Brush=\"{StaticResource NodifyEditor.SelectionRectangleBackgroundBrush}\" />\n\n        <DrawingBrush x:Key=\"SmallGridLinesDrawingBrush\"\n                    TileMode=\"Tile\"\n                    ViewportUnits=\"Absolute\"\n                    Viewport=\"0 0 20 20\"\n                    Transform=\"{Binding ViewportTransform, ElementName=Editor}\"\n                    Drawing=\"{StaticResource SmallGridGeometry}\" />\n\n        <DrawingBrush x:Key=\"LargeGridLinesDrawingBrush\"\n                    TileMode=\"Tile\"\n                    ViewportUnits=\"Absolute\"\n                    Opacity=\"0.5\"\n                    Viewport=\"0 0 100 100\"\n                    Transform=\"{Binding ViewportTransform, ElementName=Editor}\"\n                    Drawing=\"{StaticResource LargeGridGeometry}\" />\n    </Window.Resources>\n\n    <Grid Background=\"{StaticResource NodifyEditor.BackgroundBrush}\">\n        <nodify:NodifyEditor x:Name=\"Editor\" Background=\"{StaticResource SmallGridLinesDrawingBrush}\" />\n\n        <Grid Background=\"{StaticResource LargeGridLinesDrawingBrush}\"\n              Panel.ZIndex=\"-2\" />\n    </Grid>\n</Window>\n```\n\n> 提示：右键单击并拖动屏幕以移动视图，使用鼠标滚轮放大和缩小。"
  },
  {
    "path": "docs/localization/zh-CN/编辑器概述.md",
    "content": "## 目录\n* [移动视口](#平移)\n* [缩放](#缩放)\n* [选择项目](#选择)\n  * [选择功能相关API](#选择功能相关API)\n* [对齐到轴网](#对齐)\n* [命令](#命令)\n* [编辑器API](#编辑器API)\n\n## 平移\n\n平移是通过按住鼠标右键并移动鼠标来完成的，可以通过将```DisablePanning```依赖属性设置为```true```来禁用平移功能。\n> 注意：可以通过设置```ViewportLocation```依赖属性以编程方式更改它。\n\n在平移过程中，```IsPanning```依赖属性将被设置为```true```，并且```ViewportSize```, ```ViewportLocation``` 和 ```ViewportTransform``` 依赖属性将会更新。\n\n默认情况下，自动平移也是启用的，可以通过将```DisableAutoPanning```依赖属性设置为```true```来禁用。其行为是在选择或拖动选区或待连接物体靠近视口边缘时平移视口。\n\n可以使用```AutoPanSpeed```依赖属性更改自动平移速度，使用```AutoPanEdgeDistance```依赖属性更改触发平移的边缘距离。\n\n平移功能还可以与选择和缩放结合使用，而自动平移不仅可以与选择和缩放结合使用，还可以与拖动选区或预备连接一起使用。\n\n默认值：\n\n* ```DisablePanning```: false\n* ```DisableAutoPanning```: false\n* ```AutoPanSpeed```: 10 pixels per tick\n* ```AutoPanEdgeDistance```: 15 pixels\n* ```AutoPanningTickRate```: 1 millisecond\n\n## 缩放 \n\n缩放是通过使用鼠标滚轮或按```CTRL +```来放大或按```CTRL -```来缩小完成的，并且可以通过将```DisableZooming```依赖属性设置为```true```来禁用缩放功能。\n> 注意：可以通过将```ViewportZoom```依赖属性设置为```MinViewportZoom```和```MaxViewportZoom```之间的值来以编程方式更改它。\n\n在缩放过程中，```ViewportLocation```, ```ViewportSize``` 和 ```ViewportTransform``` 依赖属性将会更新。\n\n缩放功能还可以与平移、拖动选区或预备连接结合使用。\n\n默认值：\n* ```ViewportZoom```: 1\n* ```MinViewportZoom```: 0.1\n* ```MaxViewportZoom```: 2\n\n## 选择\n\n选择项目是通过按住鼠标左键并移动鼠标来完成的。当选择操作正在进行时，```IsSelecting```依赖属性将被设置为```true```，并且```SelectedArea```依赖属性将随着每次移动而更新。\n> 注意：也可以通过将集合绑定到```SelectedItems```依赖属性来以编程方式设置选定的项目。\n\n如果启用了实时选择（```EnableRealtimeSelection```: true），则在调整选择矩形大小时，项目将被选中和取消选中。否则，在选择操作完成后，才会选中```SelectedArea```中包含的项目。\n\n当选择一个```ItemContainer```时，其```IsSelected```依赖属性将被设置为```true```。\n\n根据开始选择时按住的```ModifierKeys```不同，使用不同的行为：\n- ```Replace``` - 无修饰键（默认行为，清除已选项目并开始新选择）\n- ```Append``` - shift键（将选择添加到当前已选项目）\n- ```Remove``` - alt键（从当前已选项目中移除选择）\n- ```Invert``` - control键（移除选定项目并添加未选中项目）\n\n选择项目也可以与平移和缩放结合使用。\n\n默认值:\n* ```EnableRealtimeSelection```: true\n\n### 选择功能相关API:\n\n以下方法可以在 NodifyEditor 实例上调用。\n\n* SelectArea\n* InvertSelection\n* UnselectArea\n\n## 对齐\n\n当移动选中项目时，```GridCellSize```依赖属性会决定将选定项目对齐到何处。\n对齐是相对于选定项目的位置而不是虚拟网格的位置。\n\n如果选定项目在初始创建时未对齐到网格，或者在运行时更改了```GridCellSize```，那么当```EnableSnappingCorrection```依赖属性为```true```时，则在移动选中项目后将会校正最终位置。\n\n默认值:\n* ```GridCellSize```: 1\n* ```EnableSnappingCorrection```: true\n\n## 命令\n\n 在```EditorCommands```类中可以看到以下```RoutedUICommand```：\n* ```ZoomIn``` - ```CTRL +```（相对于视口中心放大）\n* ```ZoomOut``` - ```CTRL -```（相对于视口中心缩小）\n* ```SelectAll``` - ```CTRL A```（选择所有项目）\n* ```BringIntoView``` - 将视口移动到指定位置，默认为[0,0]。（```CommandParameter```为类型为```Point```或```string```的位置）\n* ```Align``` - 使用指定的对齐方法对齐选定的项目，默认为顶部。（```CommandParameter```为类型为```Alignment```或```string```的对齐方法。可能的对齐方法：Top、Left、Bottom、Right、Middle、Center）\n* ```FitToScreen``` - 缩放并移动```Viewport```以显示尽可能多的项目\n\n## 编辑器API\n\n您可以在`NodifyEditor`实例上以编程方式调用这些命令的相应方法。\n\n* FitToScreen\n* BringIntoView\n* ZoomAtPosition\n* ZoomIn\n* ZoomOut"
  },
  {
    "path": "docs/localization/zh-CN/节点概述.md",
    "content": "节点是节点编辑器的基本组件。它们被包装在[`ItemContainer`](项目容器概述)中，并且可以是任何自定义控件。（例如，TextBlock）\n\n以下是库中包含的节点：\n\n### 1. ```Node``` 控件\n这种类型的节点支持```Input```和```Output```连接器，可以随意移动。\n\n```xml\n<nodify:NodifyEditor xmlns:sys=\"clr-namespace:System;assembly=mscorlib\">\n  <nodify:NodifyEditor.ItemsSource>\n    <CompositeCollection>\n        <nodify:Node Header=\"My Node\">\n            <nodify:Node.Input>\n                <CompositeCollection>\n                    <sys:String>In 0</sys:String>\n                    <sys:String>In 1</sys:String>\n                </CompositeCollection>\n            </nodify:Node.Input>\n            <nodify:Node.Output>\n                <CompositeCollection>\n                    <sys:String>Out 0</sys:String>\n                    <sys:String>Out 1</sys:String>\n                </CompositeCollection>\n            </nodify:Node.Output>\n            <nodify:Node.InputConnectorTemplate>\n                <DataTemplate>\n                    <nodify:NodeInput Header=\"{Binding}\" />\n                </DataTemplate>\n            </nodify:Node.InputConnectorTemplate>\n            <nodify:Node.OutputConnectorTemplate>\n                <DataTemplate>\n                    <nodify:NodeOutput Header=\"{Binding}\" />\n                </DataTemplate>\n            </nodify:Node.OutputConnectorTemplate>\n        </nodify:Node>\n    </CompositeCollection>\n  </nodify:NodifyEditor.ItemsSource>\n</nodify:NodifyEditor>\n```\n\n节点的`Header`可以使用`HeaderTemplate`进行自定义。同样，节点的`Footer`可以使用`FooterTemplate`进行自定义。\n\n`Input`集合中的每个项目可以使用`InputConnectorTemplate`进行自定义。同样，`Output`可以使用`OutputConnectorTemplate`进行自定义。\n\n![Node](https://i.imgur.com/VwAYlX3.gif)\n\n### 2. ```GroupingNode``` 控件\n\n这种类型的节点可以调整大小，如果通过```Header```拖动，它将移动其内部的节点。\n\n如果按住```SwitchMovementModeModifierKey```（默认情况下为**Shift**键），它将移动而不会移动其子节点。\n\n```xml\n<nodify:NodifyEditor>\n    <nodify:NodifyEditor.ItemsSource>\n        <CompositeCollection>\n            <nodify:GroupingNode Header=\"Grouping node\"\n                            Width=\"300\"\n                            Height=\"250\" />            \n            <nodify:Node Header=\"My node\" />\n            <nodify:Node Header=\"My other node\" />\n        </CompositeCollection>\n    </nodify:NodifyEditor.ItemsSource>\n</nodify:NodifyEditor>\n```\n\n节点的`Header`可以使用`HeaderTemplate`进行自定义。同样，节点的`Content`可以使用`ContentTemplate`进行自定义。\n\n节点的大小可以通过更改`ActualSize`依赖属性的值来编程设置。\n\n#### 默认值\n\n* CanResize: true\n* MovementMode: Grouped\n\n#### 命令\n\n* ResizeCompleted\n* ResizeStarted\n\n![Grouping Node](https://i.imgur.com/HYxt2cs.gif)\n\n### 3. ```KnotNode``` 控件\n\n这种类型的控件可以用于重新排布（reroute）```Connection```，因为它只支持一个```Connector```。\n\n节点的`Content`可以使用`ContentTemplate`进行自定义。\n\n```xml\n<nodify:NodifyEditor>\n    <nodify:NodifyEditor.ItemsSource>\n        <CompositeCollection>\n            <nodify:KnotNode />\n        </CompositeCollection>\n    </nodify:NodifyEditor.ItemsSource>\n</nodify:NodifyEditor>\n```\n\n![Knot Node](https://i.imgur.com/fMrEqY1.gif)\n\n### 4. ```StateNode``` 控件\n\n这种类型的节点本身就是一个```Connector```，这意味着它将在交互时引发```PendingConnection```事件。由于它继承自`Connector`，你需要将`Anchor`属性和`IsConnected`绑定到相应的状态。（如果`IsConnected`设置为false，连接将不会更新）\n\n```xml\n<nodify:NodifyEditor>\n    <nodify:NodifyEditor.ItemsSource>\n        <CompositeCollection>\n            <nodify:StateNode Content=\"My node\" />\n        </CompositeCollection>\n    </nodify:NodifyEditor.ItemsSource>\n</nodify:NodifyEditor>\n```\n\n节点的`Content`可以使用`ContentTemplate`进行自定义。\n\n![State Node](https://i.imgur.com/FrI2epL.gif)"
  },
  {
    "path": "docs/localization/zh-CN/连接器概述.md",
    "content": "连接器通过引发`PendingConnectionStartedEvent`事件来创建预备连接。连接器具有一个**必须**绑定的`Anchor`依赖属性，以便在节点位置更改时实时更新它们之间的连接。`IsConnected`依赖属性**必须**设置为true，以接收`Anchor`更新。\n\n按住`ALT`并点击连接器会触发`DisconnectCommand`。\n\n## NodeInput 和 NodeOutput\n\nNodeInput和NodeOutput是带有`Header`的`Connector`的实现，可以通过自定义`HeaderTemplate`来显示文本或输入框。它们还公开了一个`ConnectorTemplate`来自定义连接器本身。通常，它们与`Node.InputConnectorTemplate`和`Node.OutputConnectorTemplate`一起使用。\n\n![image](https://user-images.githubusercontent.com/12727904/192117525-a7e1b309-70d6-4ed7-bcd7-8210dbd680ce.png)\n"
  },
  {
    "path": "docs/localization/zh-CN/连接概述.md",
    "content": "连接是由两个点之间创建的。`Source`和`Target`依赖属性是`Point`类型，通常绑定到连接器的`Anchor`点。\n\n## 基本连接\n\n库中所有连接的基类是`BaseConnection`，它派生自`Shape`。在创建自定义连接时，可以不受任何限值地从`BaseConnection`派生。\n\n它公开了两个命令及其对应的事件：\n- `DisconnectCommand` 及 `DisconnectEvent` - 当按住`ALT`点击连接时触发\n- `SplitCommand` 及 `SplitEvent` - 当双击连接时触发\n\n连接的`Direction`有两种值：\n- `Forward`\n\n![image](https://user-images.githubusercontent.com/12727904/192101918-af9b0da6-ecc8-48f7-bf4d-8f9fdd005153.png)\n![image](https://user-images.githubusercontent.com/12727904/192101959-2cb9a837-1642-4e96-b2ef-eea5502a587f.png)\n\n- `Backward`\n\n![image](https://user-images.githubusercontent.com/12727904/192101941-a00e23db-07ae-49ac-a907-72e35ef67877.png)\n![image](https://user-images.githubusercontent.com/12727904/192101977-1afd69f1-dab0-478e-9c3d-7d601486c289.png)\n\n`SourceOffset`和`TargetOffset`与`OffsetMode`一起使用，会控制与锚点的距离：\n\n![image](https://user-images.githubusercontent.com/12727904/192102096-b20887d5-b7ba-450f-9cf3-7fa4086d9637.png)\n\n连接还有一个`Spacing`属性，会使连接在一段距离后转折到沿着从`Source`和`Target`点的方向：\n\n- 有间距：\n\n![image](https://user-images.githubusercontent.com/12727904/192102286-9a79da8e-5e87-4f60-9e82-979bfabcd6f3.png)\n\n- 无间距：\n\n![image](https://user-images.githubusercontent.com/12727904/192102302-4125b44a-dfad-4d9e-9131-efb7c17cefbe.png)\n\n将`ArrowSize`设置为\"0, 0\"会移除箭头。\n\n## 线连接\n\n一条从`Source`到`Target`的直线。\n\n![image](https://user-images.githubusercontent.com/12727904/192115137-d8d2145b-a769-4ee9-b4e0-8a362c94e9e7.png)\n\n## 电路连接\n\n有一个`Angle`依赖属性来控制转折的位置。角度以度为单位。\n\n![image](https://user-images.githubusercontent.com/12727904/192115226-b0e515b4-5a21-46aa-956a-401f07b7d308.png)\n\n## 曲线连接\n\n`Source`和`Target`之间的贝塞尔曲线。\n\n![image](https://user-images.githubusercontent.com/12727904/192115259-2fe56a68-b3e4-4f5d-aa5c-5ab83e84a84d.png)\n\n## 预备连接\n\n可以从连接器创建预备连接，并可以放置在`ItemContainer`或`Connector`上（如果`AllowOnlyConnectors`为false）。\n\n预备连接的`Content`可以使用`ContentTemplate`进行自定义。如果`EnablePreview`为true，`PreviewTarget`将更新为鼠标光标下的连接器或项目容器，或者为`null`（如果没有这样的元素）。\n\n![image](https://user-images.githubusercontent.com/12727904/192115698-fbe29101-884f-4cec-9c25-e318701d30b1.png)\n\n预备连接的可见性可以使用`IsVisible`依赖属性进行控制。\n\n连接器的连接捕捉可以使用`EnableSnapping`依赖属性启用。\n\n`Source`和`Target`属性是连接器的数据上下文，预备连接完成时`Target`将更新。\n\n还有一个`StartedCommand`，参数是`Source`，以及一个`CompletedCommand`，参数是`Target`。\n\n> 提示：取消预备连接的方法是释放右键。"
  },
  {
    "path": "docs/localization/zh-CN/问答.md",
    "content": "\n#### 问：为什么我移动节点时连接没有跟随连接器？\n\n答1：您没有绑定连接器的`Anchor`属性，或者连接的`Source`和`Target`属性。\n\n答2：仅当连接器的`IsConnected`属性设置为true时，`Anchor`才会更新。\n\n***\n\n#### 问：我可以更改鼠标/键盘绑定吗？\n\n答：可以！您可以根据喜好配置[编辑器手势](https://github.com/miroiu/nodify/blob/master/Nodify/EditorGestures.cs)。\n\n***\n\n#### 问：有Avalonia移植版本吗？\n\n答：有一个非常棒的Avalonia移植版本！您可以在[这里](https://github.com/BAndysc/nodify-avalonia)查看。非常感谢[BAndysc](https://github.com/BAndysc)使这一切成为可能。\n\n***\n\n#### 问：有没有非MVVM的方法添加节点和连接？\n\n答：没有。\n\n***\n\n#### 问：如何设置选中的节点始终在顶部？\n\n答：[链接](https://github.com/miroiu/nodify/issues/57)\n\n***\n\n#### 问：如何更改节点的大小？\n\n答：[链接](https://github.com/miroiu/nodify/issues/55)\n\n***\n\n#### 问：如何限制X和Y方向的平移？\n\n答：[链接](https://github.com/miroiu/nodify/issues/53)\n\n***\n\n#### 问：视口滞后。如何修复？\n\n答：[链接](https://github.com/miroiu/nodify/issues/60)\n\n***\n\n#### 问：如何将节点从工具箱拖入编辑器？\n\n答：[链接](https://github.com/miroiu/nodify/issues/81)"
  },
  {
    "path": "docs/localization/zh-CN/项目容器概述.md",
    "content": "```项目容器（ItemContainer）``` 是编辑器中**最重要**的部分。它是一个内容控件，用于包装由 ```编辑器（NodifyEditor）``` 的 ```ItemsSource``` 生成的每个控件，并在图形坐标中具有 `Location`属性。\n\n## 目录\n - [选择](#选择)\n   - [API](#选择-api)\n - [移动](#移动和拖拽)\n\n## 选择\n\n选择一个 ```ItemContainer``` 通过在容器的边界框上释放左键来完成。边界框是通过 ```DesiredSizeForSelection``` 依赖属性计算的（如果指定），否则使用 ```RenderSize``` 计算。\n\n> 注意：可以通过将 ```IsSelected``` 依赖属性设置为 ```true``` 来编程选择容器。\n\n如果左键没有释放并且鼠标移动，容器也会被选择，这将清除当前选择并在选定的容器上开始拖拽操作。\n\n还可以通过在容器上释放右键来选择，右键点击会清除当前选择并选择目标容器。\n\n根据选择容器时按下的 ```ModifierKeys``` 不同，会有不同的行为：\n- ```Replace``` - 没有按下修饰键（默认行为，清除选定项并选择容器）\n- ```Append``` - 按下 Shift 键（将容器添加到选定项）\n- ```Invert``` - 按下 Ctrl 键（切换容器的 ```IsSelected``` 依赖属性）\n\n只有在 ```IsSelectable``` 依赖属性为 ```true``` 时，```ItemContainer``` 才能被选择。\n\n选择或取消选择容器会分别触发 ```Selected``` 和 ```Unselected``` 事件，并将 ```IsSelected``` 依赖属性设置为新值。\n\n如果未重写默认样式，容器的边框将使用 ```SelectedBrush``` 依赖属性。\n\n默认值：\n- ```IsSelectable```: true\n- ```IsSelected```: false\n\n### 选择 API\n\n* IsSelectableInArea\n\n## 移动和拖拽\n\n可以通过按住左键并移动鼠标来开始拖拽 ```ItemContainer``` 的操作。\n只有在 ```IsDraggable``` 依赖属性设置为 ```true``` 时，```ItemContainer``` 才能被拖拽。\n\n> 注意：可以通过设置它们的 ```Location``` 依赖属性来编程移动 ```ItemContainer```。\n\n拖拽 ```ItemContainer``` 会触发一系列事件，这些事件由 ```NodifyEditor``` 处理并应用于所有选定的项：\n- ```DragStarted``` - 将 ```IsPreviewingLocation``` 依赖属性设置为 ```true```;\n- ```DragDelta``` - 在拖拽操作完成或取消之前持续触发，并将所有选定项移动；\n- ```DragCompleted``` - 完成或取消拖拽操作，并将 ```Location``` 依赖属性设置为最终位置，```IsPreviewingLocation``` 设置为 ```false```。\n\n> 注意：如果 ```AllowDraggingCancellation``` 设置为 ```true```，可以通过释放右键取消拖拽操作。\n\n默认值：\n- ```IsDraggable```: true\n- ```AllowDraggingCancellation```: true\n"
  }
]