From baf56bce25e7516dd8d6f00e13cbd4f9970b7839 Mon Sep 17 00:00:00 2001 From: Flojo101 Date: Mon, 31 Aug 2026 18:13:57 +0200 Subject: [PATCH 01/15] Bypass avalonia property store and measure passes by implementing custom control measurements --- .../Builders/GenericGraphElementBuilder.cs | 4 +- .../Builders/GraphNodeBuilder.cs | 24 ++++---- .../Controls/GenericGraphElementControl.cs | 21 +------ .../Controls/GraphEdgeControl.cs | 4 +- .../Controls/GraphEntityNodeControl.cs | 2 +- .../Controls/GraphJunctionControl.cs | 18 +++--- .../Controls/GraphLabelControl.cs | 4 +- .../Controls/GraphNodeControl.cs | 31 +++++++--- .../Controls/GraphPortControl.cs | 12 ++-- .../Controls/GraphRootNodeControl.cs | 7 +++ .../Controls/PanningControl.cs | 9 +-- .../Controls/PositionableSubControl.cs | 58 ++++++++++--------- 12 files changed, 102 insertions(+), 92 deletions(-) diff --git a/src/FEntwumS.Common/Builders/GenericGraphElementBuilder.cs b/src/FEntwumS.Common/Builders/GenericGraphElementBuilder.cs index 9b3c779..1cc39f1 100644 --- a/src/FEntwumS.Common/Builders/GenericGraphElementBuilder.cs +++ b/src/FEntwumS.Common/Builders/GenericGraphElementBuilder.cs @@ -70,8 +70,8 @@ public T Build() { X = this._x, Y = this._y, - Width = this._width, - Height = this._height, + ElementWidth = this._width, + ElementHeight = this._height, srcLocation = this._srclocation, ZIndex = _zIndex, ClipToBounds = false diff --git a/src/FEntwumS.Common/Builders/GraphNodeBuilder.cs b/src/FEntwumS.Common/Builders/GraphNodeBuilder.cs index 2f8f74a..c7a4d43 100644 --- a/src/FEntwumS.Common/Builders/GraphNodeBuilder.cs +++ b/src/FEntwumS.Common/Builders/GraphNodeBuilder.cs @@ -386,18 +386,18 @@ public GraphNodeBuilder WithExpandCollapseInteractionControl( (expandCollapseButton.Content as PathIcon)?.Data = AppIcons.MINUS; } - directAncestorNode.PropertyChanged += (o, eventArgs) => - { - if (eventArgs.Property == PositionableSubControl.ScaleProperty && - expandCollapseButton.Content is PathIcon buttonContent) - { - double newScale = (double)eventArgs.NewValue!; - double oldScale = (double)eventArgs.OldValue!; - double scaleDifference = newScale / oldScale; - - expandCollapseButton.GetVisualParent()?.RenderTransform = new ScaleTransform(newScale, newScale); - } - }; + // directAncestorNode.PropertyChanged += (o, eventArgs) => + // { + // if (eventArgs.Property == PositionableSubControl.ScaleProperty && + // expandCollapseButton.Content is PathIcon buttonContent) + // { + // double newScale = (double)eventArgs.NewValue!; + // double oldScale = (double)eventArgs.OldValue!; + // double scaleDifference = newScale / oldScale; + // + // expandCollapseButton.GetVisualParent()?.RenderTransform = new ScaleTransform(newScale, newScale); + // } + // }; } }; diff --git a/src/FEntwumS.Common/Controls/GenericGraphElementControl.cs b/src/FEntwumS.Common/Controls/GenericGraphElementControl.cs index 773d4a2..aef5e41 100644 --- a/src/FEntwumS.Common/Controls/GenericGraphElementControl.cs +++ b/src/FEntwumS.Common/Controls/GenericGraphElementControl.cs @@ -37,32 +37,13 @@ public string srcLocation #region Event Handling - protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs e) - { - if (e.Property == ParentProperty) - { - var newParent = e.NewValue; - - if (newParent is PanningControl { Child: GenericGraphElementControl childControl }) - childControl.PropertyChanged += (sender, args) => - { - if (args.Property == ScaleProperty && args.NewValue is double newScale) - { - childControl.NetlistTheme.RegenerateBrushesAndPens(newScale); - } - }; - } - - base.OnPropertyChanged(e); - } - #endregion #region Rendering protected override Size MeasureCore(Size availableSize) { - return Double.IsNaN(Width) || Double.IsNaN(Height) ? new Size(100, 100) : new Size(Width, Height); + return Double.IsNaN(ElementWidth) || Double.IsNaN(ElementHeight) ? new Size(100, 100) : new Size(ElementWidth, ElementHeight); } protected override void ArrangeCore(Rect finalRect) diff --git a/src/FEntwumS.Common/Controls/GraphEdgeControl.cs b/src/FEntwumS.Common/Controls/GraphEdgeControl.cs index eac023e..b5c4834 100644 --- a/src/FEntwumS.Common/Controls/GraphEdgeControl.cs +++ b/src/FEntwumS.Common/Controls/GraphEdgeControl.cs @@ -58,9 +58,9 @@ public override void Render(DrawingContext context) context.DrawGeometry(null, linePen, _contentGeometry); } - protected override void RegenerateDrawnElements() + protected override void RegenerateDrawnElements(double newScale) { - _contentGeometry.Transform = new ScaleTransform(Scale, Scale); + _contentGeometry.Transform = new ScaleTransform(newScale, newScale); } #endregion diff --git a/src/FEntwumS.Common/Controls/GraphEntityNodeControl.cs b/src/FEntwumS.Common/Controls/GraphEntityNodeControl.cs index 0d4aa21..7bb0cf7 100644 --- a/src/FEntwumS.Common/Controls/GraphEntityNodeControl.cs +++ b/src/FEntwumS.Common/Controls/GraphEntityNodeControl.cs @@ -58,7 +58,7 @@ protected void CheckChildVisibility() foreach (GraphEntityNodeControl childEntity in _childEntities) { Rect childArea = new Rect(childEntity.X, childEntity.Y, - childEntity.Width, childEntity.Height); + childEntity.ElementWidth, childEntity.ElementHeight); bool isVisible = CurrentViewPort.Contains(childArea) || childArea.Intersects(CurrentViewPort) diff --git a/src/FEntwumS.Common/Controls/GraphJunctionControl.cs b/src/FEntwumS.Common/Controls/GraphJunctionControl.cs index 6df795a..9bfffdc 100644 --- a/src/FEntwumS.Common/Controls/GraphJunctionControl.cs +++ b/src/FEntwumS.Common/Controls/GraphJunctionControl.cs @@ -36,11 +36,11 @@ public JunctionShape _junctionShape protected override void OnInitialized() { - double rh = Width / 2.0d, - rv = Height / 2.0d; + double rh = ElementWidth / 2.0d, + rv = ElementHeight / 2.0d; - double ow = Width * 1.4d, - oh = Height * 1.4d; + double ow = ElementWidth * 1.4d, + oh = ElementHeight * 1.4d; double orh = ow / 2.0d, orv = oh / 2.0d; @@ -56,8 +56,8 @@ protected override void OnInitialized() oby = orv; _contentGeometry = _junctionShape switch { - JunctionShape.Circle => new EllipseGeometry(new Rect(ilx, ity, Width, Height)), - JunctionShape.Square => new RectangleGeometry(new Rect(ilx, ity, Width, Height)), + JunctionShape.Circle => new EllipseGeometry(new Rect(ilx, ity, ElementWidth, ElementHeight)), + JunctionShape.Square => new RectangleGeometry(new Rect(ilx, ity, ElementWidth, ElementHeight)), JunctionShape.Diamond => new PolylineGeometry([ new Point(0.0d, oty), new Point(orx, 0.0d), @@ -83,15 +83,15 @@ protected override void OnInitialized() public override void Render(DrawingContext context) { - if (_contentGeometry.Bounds.Height >= 2.0d) + if (ElementHeight >= 2.0d) { context.DrawGeometry(NetlistTheme.EdgeBrush, null, _contentGeometry); } } - protected override void RegenerateDrawnElements() + protected override void RegenerateDrawnElements(double newScale) { - _contentGeometry.Transform = new ScaleTransform(Scale, Scale); + _contentGeometry.Transform = new ScaleTransform(newScale, newScale); } #endregion diff --git a/src/FEntwumS.Common/Controls/GraphLabelControl.cs b/src/FEntwumS.Common/Controls/GraphLabelControl.cs index 364143e..6237bcf 100644 --- a/src/FEntwumS.Common/Controls/GraphLabelControl.cs +++ b/src/FEntwumS.Common/Controls/GraphLabelControl.cs @@ -59,13 +59,13 @@ public override void Render(DrawingContext context) } } - protected override void RegenerateDrawnElements() + protected override void RegenerateDrawnElements(double newScale) { _formattedContent = new FormattedText(Content ?? "", CultureInfo.InvariantCulture, FlowDirection.LeftToRight, NetlistTheme.Typeface, - Fontsize * Scale, + Fontsize * newScale, NetlistTheme.TextBrush); } diff --git a/src/FEntwumS.Common/Controls/GraphNodeControl.cs b/src/FEntwumS.Common/Controls/GraphNodeControl.cs index 8c68c47..6f2d088 100644 --- a/src/FEntwumS.Common/Controls/GraphNodeControl.cs +++ b/src/FEntwumS.Common/Controls/GraphNodeControl.cs @@ -83,7 +83,7 @@ public string? LocationPath protected override void OnInitialized() { - var contentRect = new Rect(0.0d, 0.0d, Width, Height); + var contentRect = new Rect(0.0d, 0.0d, ElementWidth, ElementHeight); // double l = ((NetlistTheme.BorderThickness + NetlistTheme.DropShadowThickness) / 2) * Scale; // double r = l + Width; @@ -137,6 +137,21 @@ private void ItemsChanged(object? sender, NotifyCollectionChangedEventArgs e) AddChildrenToVisualTree(); } + protected override void UpdateScale(double newScale) + { + foreach (Control v in _interactionControls) + { + v.RenderTransform = new ScaleTransform(newScale, newScale); + } + + foreach (PositionableSubControl child in Items) + { + child.Scale = newScale; + } + + base.UpdateScale(newScale); + } + #endregion #region Rendering @@ -152,7 +167,7 @@ protected override Size MeasureCore(Size availableSize) protected override Size MeasureOverride(Size availableSize) { - availableSize = new Size(double.PositiveInfinity, double.PositiveInfinity); + availableSize = new Size(ElementWidth, ElementHeight); foreach (PositionableSubControl child in Items) { @@ -190,15 +205,15 @@ private void ArrangeInteractionControl(Control interactionControl, Size availabl double offset = (NetlistTheme.BorderThickness + 2.0d) * Scale; double x = interactionControl.HorizontalAlignment switch { - HorizontalAlignment.Center => availableSize.Width / 2.0d - (interactionControl.DesiredSize.Width / 2.0d) - offset, - HorizontalAlignment.Right => availableSize.Width - interactionControl.DesiredSize.Width, + HorizontalAlignment.Center => ElementWidth / 2.0d - (interactionControl.DesiredSize.Width / 2.0d) - offset, + HorizontalAlignment.Right => ElementWidth - interactionControl.DesiredSize.Width, _ => offset }; double y = interactionControl.VerticalAlignment switch { - VerticalAlignment.Center => availableSize.Height / 2.0d - (interactionControl.DesiredSize.Height / 2.0d) - offset, - VerticalAlignment.Bottom => availableSize.Height - interactionControl.DesiredSize.Height, + VerticalAlignment.Center => ElementHeight / 2.0d - (interactionControl.DesiredSize.Height / 2.0d) - offset, + VerticalAlignment.Bottom => ElementHeight - interactionControl.DesiredSize.Height, _ => offset }; @@ -243,9 +258,9 @@ public override void Render(DrawingContext context) } } - protected override void RegenerateDrawnElements() + protected override void RegenerateDrawnElements(double newScale) { - _contentGeometry.Transform = new ScaleTransform(Scale, Scale); + _contentGeometry.Transform = new ScaleTransform(newScale, newScale); } #endregion diff --git a/src/FEntwumS.Common/Controls/GraphPortControl.cs b/src/FEntwumS.Common/Controls/GraphPortControl.cs index 8db3653..f4733c0 100644 --- a/src/FEntwumS.Common/Controls/GraphPortControl.cs +++ b/src/FEntwumS.Common/Controls/GraphPortControl.cs @@ -33,12 +33,12 @@ public PortShape _portShape protected override void OnInitialized() { double lx = 0.0d, - rx = 0.0d + Width, + rx = 0.0d + ElementWidth, rox = rx - 5.0d, - mx = rx - (Width / 2.0d), + mx = rx - (ElementWidth / 2.0d), ty = 0.0d, - by = 0.0d + Height, - my = by - (Height / 2.0d); + by = 0.0d + ElementHeight, + my = by - (ElementHeight / 2.0d); _contentGeometry = _portShape switch { @@ -88,9 +88,9 @@ public override void Render(DrawingContext context) } } - protected override void RegenerateDrawnElements() + protected override void RegenerateDrawnElements(double newScale) { - _contentGeometry.Transform = new ScaleTransform(Scale, Scale); + _contentGeometry.Transform = new ScaleTransform(newScale, newScale); } #endregion diff --git a/src/FEntwumS.Common/Controls/GraphRootNodeControl.cs b/src/FEntwumS.Common/Controls/GraphRootNodeControl.cs index e234cc6..a9c5220 100644 --- a/src/FEntwumS.Common/Controls/GraphRootNodeControl.cs +++ b/src/FEntwumS.Common/Controls/GraphRootNodeControl.cs @@ -12,4 +12,11 @@ public override void Render(DrawingContext context) } #endregion + + protected override void UpdateScale(double newScale) + { + NetlistTheme.RegenerateBrushesAndPens(newScale); + + base.UpdateScale(newScale); + } } \ No newline at end of file diff --git a/src/FEntwumS.Common/Controls/PanningControl.cs b/src/FEntwumS.Common/Controls/PanningControl.cs index fa8bbe0..db39cbb 100644 --- a/src/FEntwumS.Common/Controls/PanningControl.cs +++ b/src/FEntwumS.Common/Controls/PanningControl.cs @@ -93,7 +93,8 @@ protected override Size MeasureOverride(Size availableSize) { if (Child is not null) { - Child.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity)); + //Child.InvalidateMeasure(); + Child.Measure(new Size(Child.ElementWidth, Child.ElementHeight)); } return new Size(); @@ -161,7 +162,7 @@ private void OnPointerWheelChanged(object? sender, PointerWheelEventArgs e) ScaleChangedCommand?.Execute(newScale); - InvalidateArrange(); + InvalidateMeasure(); Child?.IsHitTestVisible = true; } @@ -265,8 +266,8 @@ public void ZoomToFit() var invariantBounds = new Rect(Child.X * invariantScaleFactor, Child.Y * invariantScaleFactor, - Child.Width * invariantScaleFactor, - Child.Height * invariantScaleFactor); + Child.ElementWidth * invariantScaleFactor, + Child.ElementHeight * invariantScaleFactor); ZoomToRect(invariantBounds); } } diff --git a/src/FEntwumS.Common/Controls/PositionableSubControl.cs b/src/FEntwumS.Common/Controls/PositionableSubControl.cs index cbaba90..914094d 100644 --- a/src/FEntwumS.Common/Controls/PositionableSubControl.cs +++ b/src/FEntwumS.Common/Controls/PositionableSubControl.cs @@ -24,20 +24,33 @@ public double Y set => _y = value; } - public double Scale + private double _elementWidth = 1.0d; + + public double ElementWidth + { + get => _elementWidth; + set => _elementWidth = value; + } + + private double _elementHeight = 1.0d; + + public double ElementHeight { - get => GetValue(ScaleProperty); - set => SetValue(ScaleProperty, value); + get => _elementHeight; + set => _elementHeight = value; } - /// - /// The scale of the element. Since the scale is inherited, it only needs to be set on the root element - /// - public static readonly StyledProperty ScaleProperty = - AvaloniaProperty.Register(nameof(Scale), - defaultBindingMode: BindingMode.TwoWay, - defaultValue: 1.0d, - inherits: true); + private double _scale = 1.0d; + + public double Scale + { + get => _scale; + set + { + UpdateScale(value); + _scale = value; + } + } #endregion @@ -49,26 +62,19 @@ public double Scale #region Event handling - protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs e) + protected virtual void UpdateScale(double newScale) { - if (e.Property == ScaleProperty) - { - double scaleDifference = ((double)e.NewValue!) / ((double)e.OldValue!); + double scaleDifference = newScale / _scale; - this.Width *= scaleDifference; - this.Height *= scaleDifference; - this.X *= scaleDifference; - this.Y *= scaleDifference; + this.ElementWidth *= scaleDifference; + this.ElementHeight *= scaleDifference; + this.X *= scaleDifference; + this.Y *= scaleDifference; - RegenerateDrawnElements(); - - return; - } - - base.OnPropertyChanged(e); + RegenerateDrawnElements(newScale); } - protected abstract void RegenerateDrawnElements(); + protected abstract void RegenerateDrawnElements(double newScale); #endregion } \ No newline at end of file From 3c33040bac1f0ed3f0d11a2fb3fbffc180bee8f4 Mon Sep 17 00:00:00 2001 From: Flojo101 Date: Tue, 1 Sep 2026 17:40:50 +0200 Subject: [PATCH 02/15] Use custom width and height instead of avalonia property --- src/FEntwumS.Common/Builders/GraphNodeBuilder.cs | 2 +- src/FEntwumS.Common/Controls/GraphPortControl.cs | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/FEntwumS.Common/Builders/GraphNodeBuilder.cs b/src/FEntwumS.Common/Builders/GraphNodeBuilder.cs index c7a4d43..6f8e9f9 100644 --- a/src/FEntwumS.Common/Builders/GraphNodeBuilder.cs +++ b/src/FEntwumS.Common/Builders/GraphNodeBuilder.cs @@ -514,7 +514,7 @@ await Dispatcher.UIThread.InvokeAsync(() => } sendingPanningNetlist.ZoomBounds = new Rect(new Point(absoluteX, absoluteY), - new Size(clickedNode.Width, clickedNode.Height)); + new Size(clickedNode.ElementWidth, clickedNode.ElementHeight)); } else { diff --git a/src/FEntwumS.Common/Controls/GraphPortControl.cs b/src/FEntwumS.Common/Controls/GraphPortControl.cs index f4733c0..a0ff39e 100644 --- a/src/FEntwumS.Common/Controls/GraphPortControl.cs +++ b/src/FEntwumS.Common/Controls/GraphPortControl.cs @@ -59,15 +59,15 @@ protected override void OnInitialized() PortShape.SquareCircle => new GeometryGroup() { Children = [ - new RectangleGeometry(new Rect(lx, ty, Width / 2.0d, Height)), - new EllipseGeometry(new Rect(mx + 1.0d, ty + 1.0d, (Width / 2.0d) - 2.0d, Height - 2.0d)) + new RectangleGeometry(new Rect(lx, ty, ElementWidth / 2.0d, ElementHeight)), + new EllipseGeometry(new Rect(mx + 1.0d, ty + 1.0d, (ElementWidth / 2.0d) - 2.0d, ElementHeight - 2.0d)) ] }, PortShape.CircleSquare => new GeometryGroup() { Children = [ - new EllipseGeometry(new Rect(lx + 1.0d, ty + 1.0d, (Width / 2.0d) - 2.0d, Height - 2.0d)), - new RectangleGeometry(new Rect(mx, ty, Width / 2.0d, Height)) + new EllipseGeometry(new Rect(lx + 1.0d, ty + 1.0d, (ElementWidth / 2.0d) - 2.0d, ElementHeight - 2.0d)), + new RectangleGeometry(new Rect(mx, ty, ElementWidth / 2.0d, ElementHeight)) ] }, _ => throw new ArgumentOutOfRangeException() From b0f0719d132323230a5ac845a9154dc7083a7098 Mon Sep 17 00:00:00 2001 From: Flojo101 Date: Tue, 1 Sep 2026 18:11:51 +0200 Subject: [PATCH 03/15] Invalidate measurement whenever ZoomToRect() is called --- src/FEntwumS.Common/Controls/PanningControl.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/FEntwumS.Common/Controls/PanningControl.cs b/src/FEntwumS.Common/Controls/PanningControl.cs index db39cbb..84c9cb5 100644 --- a/src/FEntwumS.Common/Controls/PanningControl.cs +++ b/src/FEntwumS.Common/Controls/PanningControl.cs @@ -304,7 +304,7 @@ private void ZoomToRect(Rect shownBounds) OffsetY = shownBounds.Y * -scaleY; } - InvalidateArrange(); + InvalidateMeasure(); } #endregion From 747967d011c09af17c0ce226be4af16c08bfb7b9 Mon Sep 17 00:00:00 2001 From: Flojo101 Date: Thu, 3 Sep 2026 10:50:08 +0200 Subject: [PATCH 04/15] Don't zoom to fit whenever PanningControl is loaded --- .../Controls/PanningNetlistControl.cs | 2 +- src/FEntwumS.Common/ViewModels/NetlistViewModel.cs | 13 +++++++++++++ src/FEntwumS.Common/Views/NetlistView.axaml | 1 + .../Services/FrontendService.cs | 1 + 4 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/FEntwumS.Common/Controls/PanningNetlistControl.cs b/src/FEntwumS.Common/Controls/PanningNetlistControl.cs index 3fcc12f..1a9f0d7 100644 --- a/src/FEntwumS.Common/Controls/PanningNetlistControl.cs +++ b/src/FEntwumS.Common/Controls/PanningNetlistControl.cs @@ -45,7 +45,7 @@ protected override void OnLoaded(RoutedEventArgs e) if (Child is not null) { - ZoomToFit(); + // ZoomToFit(); } } diff --git a/src/FEntwumS.Common/ViewModels/NetlistViewModel.cs b/src/FEntwumS.Common/ViewModels/NetlistViewModel.cs index 70a5392..3d8a848 100644 --- a/src/FEntwumS.Common/ViewModels/NetlistViewModel.cs +++ b/src/FEntwumS.Common/ViewModels/NetlistViewModel.cs @@ -1,6 +1,7 @@  using System.Runtime.Serialization; using System.Windows.Input; +using Avalonia; using Avalonia.Data; using CommunityToolkit.Mvvm.Input; using FEntwumS.Common.Controls; @@ -62,6 +63,18 @@ public string ProjectRootFolder } [DataMember] private string _projectRootFolder { get; set; } + + private Rect? _zoomBounds; + + public Rect? ZoomBounds + { + get => _zoomBounds; + set + { + _zoomBounds = value; + OnPropertyChanged(nameof(ZoomBounds)); + } + } private ICommand _scaleChangedCommand; diff --git a/src/FEntwumS.Common/Views/NetlistView.axaml b/src/FEntwumS.Common/Views/NetlistView.axaml index 50332ab..f1ec0d0 100644 --- a/src/FEntwumS.Common/Views/NetlistView.axaml +++ b/src/FEntwumS.Common/Views/NetlistView.axaml @@ -36,6 +36,7 @@ Child="{Binding RootNode}" ScaleChangedCommand="{Binding ScaleChangedCommand}" ProjectRootFolder="{Binding ProjectRootFolder}" + ZoomBounds="{Binding ZoomBounds}" > diff --git a/src/FEntwumS.NetlistViewer/Services/FrontendService.cs b/src/FEntwumS.NetlistViewer/Services/FrontendService.cs index 7c3255a..db63e07 100644 --- a/src/FEntwumS.NetlistViewer/Services/FrontendService.cs +++ b/src/FEntwumS.NetlistViewer/Services/FrontendService.cs @@ -3,6 +3,7 @@ using System.Text; using System.Text.RegularExpressions; using Asmichi.ProcessManagement; +using Avalonia; using Avalonia.Collections; using Avalonia.Media; using FEntwumS.Common.Controls; From c80c4d228202c70e6aa22da59e6ab7a75cb96aa5 Mon Sep 17 00:00:00 2001 From: Flojo101 Date: Fri, 4 Sep 2026 19:24:49 +0200 Subject: [PATCH 05/15] Add debug setting to freeze rendering state of netlist GUI controls --- src/FEntwumS.Common/Controls/GraphEntityNodeControl.cs | 9 +++++++++ .../FEntwumSNetlistReaderFrontendModule.cs | 5 +++++ .../Helpers/FentwumSNetlistViewerSettingsHelper.cs | 4 ++++ 3 files changed, 18 insertions(+) diff --git a/src/FEntwumS.Common/Controls/GraphEntityNodeControl.cs b/src/FEntwumS.Common/Controls/GraphEntityNodeControl.cs index 7bb0cf7..6e1a976 100644 --- a/src/FEntwumS.Common/Controls/GraphEntityNodeControl.cs +++ b/src/FEntwumS.Common/Controls/GraphEntityNodeControl.cs @@ -1,5 +1,7 @@ using Avalonia; using Avalonia.Data; +using FEntwumS.Common.Services; +using OneWare.Essentials.Services; namespace FEntwumS.Common.Controls; @@ -55,6 +57,13 @@ protected void CheckChildVisibility() return; } + #if DEBUG + if (ServiceManager.GetService().GetSettingValue("NetlistViewer_FreezeGUIState")) + { + return; + } + #endif + foreach (GraphEntityNodeControl childEntity in _childEntities) { Rect childArea = new Rect(childEntity.X, childEntity.Y, diff --git a/src/FEntwumS.NetlistViewer/FEntwumSNetlistReaderFrontendModule.cs b/src/FEntwumS.NetlistViewer/FEntwumSNetlistReaderFrontendModule.cs index e679a4a..4942b01 100644 --- a/src/FEntwumS.NetlistViewer/FEntwumSNetlistReaderFrontendModule.cs +++ b/src/FEntwumS.NetlistViewer/FEntwumSNetlistReaderFrontendModule.cs @@ -948,6 +948,11 @@ private void RegisterSettings() // These settings are not displayed to the user; They store internal extension state ServiceManager.GetService().Register(FentwumSNetlistViewerSettingsHelper.FentwumsSettingVersionKey, -1); ServiceManager.GetService().Register(FentwumSNetlistViewerSettingsHelper.NetlistGenerationSettingsChangedKey, ""); + + #if DEBUG + ServiceManager.GetService().RegisterSetting("Netlist Viewer", "Experimental", + FentwumSNetlistViewerSettingsHelper.FreezeGUIStateKey, new CheckBoxSetting("Freeze GUI state", false)); + #endif ServiceManager.GetService().Log("Registered custom settings"); } diff --git a/src/FEntwumS.NetlistViewer/Helpers/FentwumSNetlistViewerSettingsHelper.cs b/src/FEntwumS.NetlistViewer/Helpers/FentwumSNetlistViewerSettingsHelper.cs index 9bb2883..d684b98 100644 --- a/src/FEntwumS.NetlistViewer/Helpers/FentwumSNetlistViewerSettingsHelper.cs +++ b/src/FEntwumS.NetlistViewer/Helpers/FentwumSNetlistViewerSettingsHelper.cs @@ -55,6 +55,10 @@ PlatformHelper.Platform is PlatformId.OsxArm64 || PlatformHelper.Platform is Pla public static readonly string LayoutEffortKey = "NetlistViewer_LayoutEffort"; public static readonly string ShowUnconnectedPortsKey = "NetlistViewer_ShowUnconnectedPorts"; public static readonly string OnlyShowUserGeneratedSignalNamesKey = "NetlistViewer_OnlyShowUserGeneratedSignalNames"; + + #if DEBUG + public static readonly string FreezeGUIStateKey = "NetlistViewer_FreezeGUIState"; + #endif public static readonly string AutomaticNetlistGenerationIntervalKey = "NetlistViewer_AutomaticNetlistGenerationInterval"; From 3044ca5ec66255392a14084b372f184bd87e68fe Mon Sep 17 00:00:00 2001 From: Flojo101 Date: Fri, 4 Sep 2026 19:27:19 +0200 Subject: [PATCH 06/15] Fix determination whether a control has entity instance children --- src/FEntwumS.Common/Controls/GraphEntityNodeControl.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/FEntwumS.Common/Controls/GraphEntityNodeControl.cs b/src/FEntwumS.Common/Controls/GraphEntityNodeControl.cs index 6e1a976..ceeedd9 100644 --- a/src/FEntwumS.Common/Controls/GraphEntityNodeControl.cs +++ b/src/FEntwumS.Common/Controls/GraphEntityNodeControl.cs @@ -1,5 +1,6 @@ using Avalonia; using Avalonia.Data; +using Avalonia.Interactivity; using FEntwumS.Common.Services; using OneWare.Essentials.Services; @@ -40,14 +41,14 @@ public Rect CurrentViewPort #region Event handling - protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) + protected override void OnLoaded(RoutedEventArgs e) { _childEntities.Clear(); _childEntities.AddRange(this.VisualChildren.OfType()); _hasEntityChildren = _childEntities.Count > 0; - base.OnAttachedToVisualTree(e); + base.OnLoaded(e); } protected void CheckChildVisibility() From 91e95e5358cacb71265e5ac2adf8d124b7602a27 Mon Sep 17 00:00:00 2001 From: Flojo101 Date: Mon, 7 Sep 2026 15:43:21 +0200 Subject: [PATCH 07/15] Add basic culling for all GraphLabelControls --- .../Controls/GraphEntityNodeControl.cs | 30 ++++++++++++++++--- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/src/FEntwumS.Common/Controls/GraphEntityNodeControl.cs b/src/FEntwumS.Common/Controls/GraphEntityNodeControl.cs index ceeedd9..b9e71e6 100644 --- a/src/FEntwumS.Common/Controls/GraphEntityNodeControl.cs +++ b/src/FEntwumS.Common/Controls/GraphEntityNodeControl.cs @@ -36,7 +36,9 @@ public Rect CurrentViewPort #region Variables private List _childEntities = new List(); + private List _childLabels = new List(); private bool _hasEntityChildren = true; + private bool _hasLabelChildren = true; #endregion #region Event handling @@ -48,22 +50,42 @@ protected override void OnLoaded(RoutedEventArgs e) _hasEntityChildren = _childEntities.Count > 0; + _childLabels.Clear(); + _childLabels.AddRange(this.VisualChildren.OfType()); + + _hasLabelChildren = _childLabels.Count > 0; + base.OnLoaded(e); } protected void CheckChildVisibility() { - if (!_hasEntityChildren) +#if DEBUG + if (ServiceManager.GetService().GetSettingValue("NetlistViewer_FreezeGUIState")) { return; } +#endif - #if DEBUG - if (ServiceManager.GetService().GetSettingValue("NetlistViewer_FreezeGUIState")) + if (_hasLabelChildren) + { + foreach (GraphLabelControl childLabel in _childLabels) + { + Rect childArea = new Rect(childLabel.X, childLabel.Y, + childLabel.ElementWidth, childLabel.ElementHeight); + + bool isVisible = CurrentViewPort.Contains(childArea) + || childArea.Intersects(CurrentViewPort) + || childArea.Contains(CurrentViewPort); + + childLabel.IsVisible = isVisible; + } + } + + if (!_hasEntityChildren) { return; } - #endif foreach (GraphEntityNodeControl childEntity in _childEntities) { From 78ae7d044dc43dcff87cf254d78a92bdec9bf3e2 Mon Sep 17 00:00:00 2001 From: Flojo101 Date: Mon, 7 Sep 2026 16:01:19 +0200 Subject: [PATCH 08/15] Fix label positioning in visual tree --- src/FEntwumS.Common/Interfaces/IJsonLoader.cs | 2 +- src/FEntwumS.NetlistViewer/Services/JsonLoader.cs | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/FEntwumS.Common/Interfaces/IJsonLoader.cs b/src/FEntwumS.Common/Interfaces/IJsonLoader.cs index 8b5889b..be764b4 100644 --- a/src/FEntwumS.Common/Interfaces/IJsonLoader.cs +++ b/src/FEntwumS.Common/Interfaces/IJsonLoader.cs @@ -16,7 +16,7 @@ public interface IJsonLoader public void CreateLabels(JsonArray labels, GraphNodeControl parent, double xRef, double yRef, ushort depth); - public void CreatePorts(JsonArray ports, GraphNodeControl parent, double xRef, + public void CreatePorts(JsonArray ports, GraphNodeControl parent, GraphNodeControl attachedTo, double xRef, double yRef, ushort depth); public void CreateEdges(JsonArray edges, GraphNodeControl parent, double xRef, diff --git a/src/FEntwumS.NetlistViewer/Services/JsonLoader.cs b/src/FEntwumS.NetlistViewer/Services/JsonLoader.cs index 0255731..4aefe75 100644 --- a/src/FEntwumS.NetlistViewer/Services/JsonLoader.cs +++ b/src/FEntwumS.NetlistViewer/Services/JsonLoader.cs @@ -319,14 +319,14 @@ public JsonLoader() if (labels != null) { - CreateLabels(labels, parent, 0, 0, newDepth); + CreateLabels(labels, prevParent, xRef + x, yRef + y, newDepth); } JsonArray? ports = node["ports"] as JsonArray; if (ports != null) { - CreatePorts(ports, prevParent, x, y, newDepth); + CreatePorts(ports, prevParent, parent, x, y, newDepth); } JsonArray? edges = node["edges"] as JsonArray; @@ -443,7 +443,7 @@ public void CreateLabels(JsonArray labels, GraphNodeControl parent, double xRef, } } - public void CreatePorts(JsonArray ports, GraphNodeControl parent, double xRef, + public void CreatePorts(JsonArray ports, GraphNodeControl parent, GraphNodeControl attachedTo, double xRef, double yRef, ushort depth) { double x = 0; @@ -491,7 +491,7 @@ public void CreatePorts(JsonArray ports, GraphNodeControl parent, double xRef, if (labels is not null) { - CreateLabels(labels, parent, xRef + x, yRef + y, (ushort)(depth + 1)); + CreateLabels(labels, attachedTo, x, y, (ushort)(depth + 1)); } if (layoutOptions is not null) From 73500ddfac7c22ecc726862a8309574c17046a2b Mon Sep 17 00:00:00 2001 From: Flojo101 Date: Mon, 7 Sep 2026 16:17:25 +0200 Subject: [PATCH 09/15] Start work on general label culling --- .../Controls/GraphEntityNodeControl.cs | 58 +++++------------- .../Controls/GraphNodeControl.cs | 59 +++++++++++++++++++ 2 files changed, 74 insertions(+), 43 deletions(-) diff --git a/src/FEntwumS.Common/Controls/GraphEntityNodeControl.cs b/src/FEntwumS.Common/Controls/GraphEntityNodeControl.cs index b9e71e6..fea120b 100644 --- a/src/FEntwumS.Common/Controls/GraphEntityNodeControl.cs +++ b/src/FEntwumS.Common/Controls/GraphEntityNodeControl.cs @@ -9,36 +9,13 @@ namespace FEntwumS.Common.Controls; public class GraphEntityNodeControl : GraphNodeControl { #region Properties - - private Rect _currentViewPort= new Rect(0, 0, 3840, 2160); - - public Rect CurrentViewPort - { - - get => _currentViewPort; - set - { - _currentViewPort = value; - CheckChildVisibility(); - - if (_hasEntityChildren) - { - foreach (GraphEntityNodeControl childEntity in _childEntities) - { - childEntity.CurrentViewPort = new Rect(_currentViewPort.X - childEntity.X, _currentViewPort.Y - childEntity.Y, _currentViewPort.Width, _currentViewPort.Height); - } - } - } - } #endregion #region Variables private List _childEntities = new List(); - private List _childLabels = new List(); private bool _hasEntityChildren = true; - private bool _hasLabelChildren = true; #endregion #region Event handling @@ -50,15 +27,23 @@ protected override void OnLoaded(RoutedEventArgs e) _hasEntityChildren = _childEntities.Count > 0; - _childLabels.Clear(); - _childLabels.AddRange(this.VisualChildren.OfType()); - - _hasLabelChildren = _childLabels.Count > 0; - base.OnLoaded(e); } - protected void CheckChildVisibility() + protected override void OnViewPortChanged() + { + if (_hasEntityChildren) + { + foreach (GraphEntityNodeControl childEntity in _childEntities) + { + childEntity.CurrentViewPort = new Rect(CurrentViewPort.X - childEntity.X, CurrentViewPort.Y - childEntity.Y, CurrentViewPort.Width, CurrentViewPort.Height);; + } + } + + base.OnViewPortChanged(); + } + + protected override void CheckChildVisibility() { #if DEBUG if (ServiceManager.GetService().GetSettingValue("NetlistViewer_FreezeGUIState")) @@ -67,20 +52,7 @@ protected void CheckChildVisibility() } #endif - if (_hasLabelChildren) - { - foreach (GraphLabelControl childLabel in _childLabels) - { - Rect childArea = new Rect(childLabel.X, childLabel.Y, - childLabel.ElementWidth, childLabel.ElementHeight); - - bool isVisible = CurrentViewPort.Contains(childArea) - || childArea.Intersects(CurrentViewPort) - || childArea.Contains(CurrentViewPort); - - childLabel.IsVisible = isVisible; - } - } + base.CheckChildVisibility(); if (!_hasEntityChildren) { diff --git a/src/FEntwumS.Common/Controls/GraphNodeControl.cs b/src/FEntwumS.Common/Controls/GraphNodeControl.cs index 6f2d088..ba27b31 100644 --- a/src/FEntwumS.Common/Controls/GraphNodeControl.cs +++ b/src/FEntwumS.Common/Controls/GraphNodeControl.cs @@ -3,9 +3,12 @@ using Avalonia.Collections; using Avalonia.Controls; using Avalonia.Data; +using Avalonia.Interactivity; using Avalonia.Layout; using Avalonia.Media; using Avalonia.Rendering; +using FEntwumS.Common.Services; +using OneWare.Essentials.Services; namespace FEntwumS.Common.Controls; @@ -67,6 +70,19 @@ public string? LocationPath public static readonly StyledProperty LocationPathProperty = AvaloniaProperty.Register(nameof(LocationPath), defaultBindingMode: BindingMode.TwoWay); + + private Rect _currentViewPort= new Rect(0, 0, 3840, 2160); + + public Rect CurrentViewPort + { + + get => _currentViewPort; + set + { + _currentViewPort = value; + OnViewPortChanged(); + } + } #endregion @@ -76,6 +92,9 @@ public string? LocationPath { Children = new GeometryCollection([new RectangleGeometry(new Rect(0, 0, 100, 100))]) }; + + private List _childLabels = new List(); + private bool _hasLabelChildren = true; #endregion @@ -114,6 +133,16 @@ protected override void OnInitialized() base.OnInitialized(); } + protected override void OnLoaded(RoutedEventArgs e) + { + _childLabels.Clear(); + _childLabels.AddRange(this.VisualChildren.OfType()); + + _hasLabelChildren = _childLabels.Count > 0; + + base.OnLoaded(e); + } + protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) { VisualChildren.Clear(); @@ -152,6 +181,11 @@ protected override void UpdateScale(double newScale) base.UpdateScale(newScale); } + protected virtual void OnViewPortChanged() + { + CheckChildVisibility(); + } + #endregion #region Rendering @@ -294,4 +328,29 @@ private void AddChildrenToVisualTree() VisualChildren.Add(interactionControl); } } + + protected virtual void CheckChildVisibility() + { +#if DEBUG + if (ServiceManager.GetService().GetSettingValue("NetlistViewer_FreezeGUIState")) + { + return; + } +#endif + + if (_hasLabelChildren) + { + foreach (GraphLabelControl childLabel in _childLabels) + { + Rect childArea = new Rect(childLabel.X, childLabel.Y, + childLabel.ElementWidth, childLabel.ElementHeight); + + bool isVisible = CurrentViewPort.Contains(childArea) + || childArea.Intersects(CurrentViewPort) + || childArea.Contains(CurrentViewPort); + + childLabel.IsVisible = isVisible; + } + } + } } \ No newline at end of file From 8a5a53519c6ea39a70fb971fde235a3daca8d759 Mon Sep 17 00:00:00 2001 From: Flojo101 Date: Mon, 7 Sep 2026 17:28:14 +0200 Subject: [PATCH 10/15] Add all child element culling to entity controls --- .../Controls/GenericGraphElementControl.cs | 3 + .../Controls/GraphEdgeControl.cs | 6 ++ .../Controls/GraphEntityNodeControl.cs | 56 ++++++++++++------ .../Controls/GraphJunctionControl.cs | 2 + .../Controls/GraphLabelControl.cs | 3 + .../Controls/GraphNodeControl.cs | 57 +------------------ .../Controls/GraphPortControl.cs | 2 + 7 files changed, 58 insertions(+), 71 deletions(-) diff --git a/src/FEntwumS.Common/Controls/GenericGraphElementControl.cs b/src/FEntwumS.Common/Controls/GenericGraphElementControl.cs index aef5e41..c3549cd 100644 --- a/src/FEntwumS.Common/Controls/GenericGraphElementControl.cs +++ b/src/FEntwumS.Common/Controls/GenericGraphElementControl.cs @@ -32,6 +32,9 @@ public string srcLocation AvaloniaProperty.Register(nameof(srcLocation), defaultBindingMode: BindingMode.TwoWay, defaultValue: ""); + + public double cullingOriginX = 0.0d; + public double cullingOriginY = 0.0d; #endregion diff --git a/src/FEntwumS.Common/Controls/GraphEdgeControl.cs b/src/FEntwumS.Common/Controls/GraphEdgeControl.cs index b5c4834..d990444 100644 --- a/src/FEntwumS.Common/Controls/GraphEdgeControl.cs +++ b/src/FEntwumS.Common/Controls/GraphEdgeControl.cs @@ -61,6 +61,12 @@ public override void Render(DrawingContext context) protected override void RegenerateDrawnElements(double newScale) { _contentGeometry.Transform = new ScaleTransform(newScale, newScale); + Rect newBounds = _contentGeometry.Bounds; + + ElementWidth = newBounds.Width; + ElementHeight = newBounds.Height; + cullingOriginX = newBounds.X + X; + cullingOriginY = newBounds.Y + Y; } #endregion diff --git a/src/FEntwumS.Common/Controls/GraphEntityNodeControl.cs b/src/FEntwumS.Common/Controls/GraphEntityNodeControl.cs index fea120b..7c65a9c 100644 --- a/src/FEntwumS.Common/Controls/GraphEntityNodeControl.cs +++ b/src/FEntwumS.Common/Controls/GraphEntityNodeControl.cs @@ -10,12 +10,28 @@ public class GraphEntityNodeControl : GraphNodeControl { #region Properties + private Rect _currentViewPort= new Rect(0, 0, 3840, 2160); + + public Rect CurrentViewPort + { + + get => _currentViewPort; + set + { + _currentViewPort = value; + OnViewPortChanged(); + } + } + #endregion #region Variables private List _childEntities = new List(); private bool _hasEntityChildren = true; + + private List _childElements = new List(); + private bool _hasChildElements = true; #endregion #region Event handling @@ -27,23 +43,33 @@ protected override void OnLoaded(RoutedEventArgs e) _hasEntityChildren = _childEntities.Count > 0; + _childElements.Clear(); + _childElements.AddRange(this.VisualChildren.OfType()); + + _hasChildElements = _childElements.Count > 0; + base.OnLoaded(e); } - protected override void OnViewPortChanged() + protected virtual void OnViewPortChanged() { + if (!this.IsVisible) + { + return; + } + + CheckChildVisibility(); + if (_hasEntityChildren) { foreach (GraphEntityNodeControl childEntity in _childEntities) { - childEntity.CurrentViewPort = new Rect(CurrentViewPort.X - childEntity.X, CurrentViewPort.Y - childEntity.Y, CurrentViewPort.Width, CurrentViewPort.Height);; + childEntity.CurrentViewPort = new Rect(CurrentViewPort.X - childEntity.X, CurrentViewPort.Y - childEntity.Y, CurrentViewPort.Width, CurrentViewPort.Height); } } - - base.OnViewPortChanged(); } - protected override void CheckChildVisibility() + protected virtual void CheckChildVisibility() { #if DEBUG if (ServiceManager.GetService().GetSettingValue("NetlistViewer_FreezeGUIState")) @@ -51,24 +77,22 @@ protected override void CheckChildVisibility() return; } #endif - - base.CheckChildVisibility(); - - if (!_hasEntityChildren) + + if (!_hasChildElements) { return; } - - foreach (GraphEntityNodeControl childEntity in _childEntities) + + foreach (GenericGraphElementControl childElement in _childElements) { - Rect childArea = new Rect(childEntity.X, childEntity.Y, - childEntity.ElementWidth, childEntity.ElementHeight); + Rect childArea = new Rect(childElement.cullingOriginX, childElement.cullingOriginY, + childElement.ElementWidth, childElement.ElementHeight); bool isVisible = CurrentViewPort.Contains(childArea) - || childArea.Intersects(CurrentViewPort) - || childArea.Contains(CurrentViewPort); + || childArea.Intersects(CurrentViewPort) + || childArea.Contains(CurrentViewPort); - childEntity.IsVisible = isVisible; + childElement.IsVisible = isVisible; } } diff --git a/src/FEntwumS.Common/Controls/GraphJunctionControl.cs b/src/FEntwumS.Common/Controls/GraphJunctionControl.cs index 9bfffdc..cc3fc36 100644 --- a/src/FEntwumS.Common/Controls/GraphJunctionControl.cs +++ b/src/FEntwumS.Common/Controls/GraphJunctionControl.cs @@ -92,6 +92,8 @@ public override void Render(DrawingContext context) protected override void RegenerateDrawnElements(double newScale) { _contentGeometry.Transform = new ScaleTransform(newScale, newScale); + cullingOriginX = X; + cullingOriginY = Y; } #endregion diff --git a/src/FEntwumS.Common/Controls/GraphLabelControl.cs b/src/FEntwumS.Common/Controls/GraphLabelControl.cs index 6237bcf..d188c5f 100644 --- a/src/FEntwumS.Common/Controls/GraphLabelControl.cs +++ b/src/FEntwumS.Common/Controls/GraphLabelControl.cs @@ -67,6 +67,9 @@ protected override void RegenerateDrawnElements(double newScale) NetlistTheme.Typeface, Fontsize * newScale, NetlistTheme.TextBrush); + + cullingOriginX = X; + cullingOriginY = Y; } #endregion diff --git a/src/FEntwumS.Common/Controls/GraphNodeControl.cs b/src/FEntwumS.Common/Controls/GraphNodeControl.cs index ba27b31..5aa4e6d 100644 --- a/src/FEntwumS.Common/Controls/GraphNodeControl.cs +++ b/src/FEntwumS.Common/Controls/GraphNodeControl.cs @@ -71,18 +71,6 @@ public string? LocationPath AvaloniaProperty.Register(nameof(LocationPath), defaultBindingMode: BindingMode.TwoWay); - private Rect _currentViewPort= new Rect(0, 0, 3840, 2160); - - public Rect CurrentViewPort - { - - get => _currentViewPort; - set - { - _currentViewPort = value; - OnViewPortChanged(); - } - } #endregion @@ -92,9 +80,6 @@ public Rect CurrentViewPort { Children = new GeometryCollection([new RectangleGeometry(new Rect(0, 0, 100, 100))]) }; - - private List _childLabels = new List(); - private bool _hasLabelChildren = true; #endregion @@ -133,16 +118,6 @@ protected override void OnInitialized() base.OnInitialized(); } - protected override void OnLoaded(RoutedEventArgs e) - { - _childLabels.Clear(); - _childLabels.AddRange(this.VisualChildren.OfType()); - - _hasLabelChildren = _childLabels.Count > 0; - - base.OnLoaded(e); - } - protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) { VisualChildren.Clear(); @@ -181,11 +156,6 @@ protected override void UpdateScale(double newScale) base.UpdateScale(newScale); } - protected virtual void OnViewPortChanged() - { - CheckChildVisibility(); - } - #endregion #region Rendering @@ -295,6 +265,8 @@ public override void Render(DrawingContext context) protected override void RegenerateDrawnElements(double newScale) { _contentGeometry.Transform = new ScaleTransform(newScale, newScale); + cullingOriginX = X; + cullingOriginY = Y; } #endregion @@ -328,29 +300,4 @@ private void AddChildrenToVisualTree() VisualChildren.Add(interactionControl); } } - - protected virtual void CheckChildVisibility() - { -#if DEBUG - if (ServiceManager.GetService().GetSettingValue("NetlistViewer_FreezeGUIState")) - { - return; - } -#endif - - if (_hasLabelChildren) - { - foreach (GraphLabelControl childLabel in _childLabels) - { - Rect childArea = new Rect(childLabel.X, childLabel.Y, - childLabel.ElementWidth, childLabel.ElementHeight); - - bool isVisible = CurrentViewPort.Contains(childArea) - || childArea.Intersects(CurrentViewPort) - || childArea.Contains(CurrentViewPort); - - childLabel.IsVisible = isVisible; - } - } - } } \ No newline at end of file diff --git a/src/FEntwumS.Common/Controls/GraphPortControl.cs b/src/FEntwumS.Common/Controls/GraphPortControl.cs index a0ff39e..79d150c 100644 --- a/src/FEntwumS.Common/Controls/GraphPortControl.cs +++ b/src/FEntwumS.Common/Controls/GraphPortControl.cs @@ -91,6 +91,8 @@ public override void Render(DrawingContext context) protected override void RegenerateDrawnElements(double newScale) { _contentGeometry.Transform = new ScaleTransform(newScale, newScale); + cullingOriginX = X; + cullingOriginY = Y; } #endregion From e2a2b3be278786c4f27a92dfd214675613143573 Mon Sep 17 00:00:00 2001 From: Flojo101 Date: Wed, 9 Sep 2026 15:59:26 +0200 Subject: [PATCH 11/15] Don't regenerate elements if they are not visible --- src/FEntwumS.Common/Controls/GraphEdgeControl.cs | 5 +++++ .../Controls/GraphEntityNodeControl.cs | 14 ++++++++++++-- .../Controls/GraphJunctionControl.cs | 5 +++++ src/FEntwumS.Common/Controls/GraphLabelControl.cs | 11 +++++++++++ src/FEntwumS.Common/Controls/GraphNodeControl.cs | 5 +++++ src/FEntwumS.Common/Controls/GraphPortControl.cs | 6 ++++++ 6 files changed, 44 insertions(+), 2 deletions(-) diff --git a/src/FEntwumS.Common/Controls/GraphEdgeControl.cs b/src/FEntwumS.Common/Controls/GraphEdgeControl.cs index d990444..630ca77 100644 --- a/src/FEntwumS.Common/Controls/GraphEdgeControl.cs +++ b/src/FEntwumS.Common/Controls/GraphEdgeControl.cs @@ -60,6 +60,11 @@ public override void Render(DrawingContext context) protected override void RegenerateDrawnElements(double newScale) { + if (!this.IsVisible) + { + return; + } + _contentGeometry.Transform = new ScaleTransform(newScale, newScale); Rect newBounds = _contentGeometry.Bounds; diff --git a/src/FEntwumS.Common/Controls/GraphEntityNodeControl.cs b/src/FEntwumS.Common/Controls/GraphEntityNodeControl.cs index 7c65a9c..dc60b36 100644 --- a/src/FEntwumS.Common/Controls/GraphEntityNodeControl.cs +++ b/src/FEntwumS.Common/Controls/GraphEntityNodeControl.cs @@ -91,8 +91,18 @@ protected virtual void CheckChildVisibility() bool isVisible = CurrentViewPort.Contains(childArea) || childArea.Intersects(CurrentViewPort) || childArea.Contains(CurrentViewPort); - - childElement.IsVisible = isVisible; + if (!childElement.IsVisible && isVisible) + { + childElement.IsVisible = true; + + childElement.Scale = this.Scale; + + childElement.InvalidateMeasure(); + } + else + { + childElement.IsVisible = isVisible; + } } } diff --git a/src/FEntwumS.Common/Controls/GraphJunctionControl.cs b/src/FEntwumS.Common/Controls/GraphJunctionControl.cs index cc3fc36..59abb6b 100644 --- a/src/FEntwumS.Common/Controls/GraphJunctionControl.cs +++ b/src/FEntwumS.Common/Controls/GraphJunctionControl.cs @@ -91,6 +91,11 @@ public override void Render(DrawingContext context) protected override void RegenerateDrawnElements(double newScale) { + if (!this.IsVisible) + { + return; + } + _contentGeometry.Transform = new ScaleTransform(newScale, newScale); cullingOriginX = X; cullingOriginY = Y; diff --git a/src/FEntwumS.Common/Controls/GraphLabelControl.cs b/src/FEntwumS.Common/Controls/GraphLabelControl.cs index d188c5f..ddf6ecb 100644 --- a/src/FEntwumS.Common/Controls/GraphLabelControl.cs +++ b/src/FEntwumS.Common/Controls/GraphLabelControl.cs @@ -2,6 +2,7 @@ using Avalonia; using Avalonia.Data; using Avalonia.Media; +using Avalonia.VisualTree; namespace FEntwumS.Common.Controls; @@ -53,6 +54,11 @@ public double Fontsize public override void Render(DrawingContext context) { + if (!this.GetVisualParent().IsVisible || !this.IsVisible) + { + return; + } + if (Scale >= 0.2d) { context.DrawText(_formattedContent, new Point(0, 0)); @@ -61,6 +67,11 @@ public override void Render(DrawingContext context) protected override void RegenerateDrawnElements(double newScale) { + if (!this.GetVisualParent().IsVisible || !this.IsVisible) + { + return; + } + _formattedContent = new FormattedText(Content ?? "", CultureInfo.InvariantCulture, FlowDirection.LeftToRight, diff --git a/src/FEntwumS.Common/Controls/GraphNodeControl.cs b/src/FEntwumS.Common/Controls/GraphNodeControl.cs index 5aa4e6d..e805732 100644 --- a/src/FEntwumS.Common/Controls/GraphNodeControl.cs +++ b/src/FEntwumS.Common/Controls/GraphNodeControl.cs @@ -264,6 +264,11 @@ public override void Render(DrawingContext context) protected override void RegenerateDrawnElements(double newScale) { + if (!this.IsVisible) + { + return; + } + _contentGeometry.Transform = new ScaleTransform(newScale, newScale); cullingOriginX = X; cullingOriginY = Y; diff --git a/src/FEntwumS.Common/Controls/GraphPortControl.cs b/src/FEntwumS.Common/Controls/GraphPortControl.cs index 79d150c..7841d58 100644 --- a/src/FEntwumS.Common/Controls/GraphPortControl.cs +++ b/src/FEntwumS.Common/Controls/GraphPortControl.cs @@ -1,6 +1,7 @@ using Avalonia; using Avalonia.Data; using Avalonia.Media; +using Avalonia.VisualTree; using FEntwumS.Common.Types; namespace FEntwumS.Common.Controls; @@ -90,6 +91,11 @@ public override void Render(DrawingContext context) protected override void RegenerateDrawnElements(double newScale) { + if (!this.IsVisible) + { + return; + } + _contentGeometry.Transform = new ScaleTransform(newScale, newScale); cullingOriginX = X; cullingOriginY = Y; From c78c16a4f19986f9f1a6138f0da22a4ac4b1712a Mon Sep 17 00:00:00 2001 From: Flojo101 Date: Thu, 10 Sep 2026 14:59:37 +0200 Subject: [PATCH 12/15] Fix most issues regarding stale boundings box information --- src/FEntwumS.Common/Controls/GraphEdgeControl.cs | 14 ++++++++++++-- .../Controls/GraphJunctionControl.cs | 10 ++++++++-- src/FEntwumS.Common/Controls/GraphLabelControl.cs | 11 ++++++++--- src/FEntwumS.Common/Controls/GraphNodeControl.cs | 5 +++-- src/FEntwumS.Common/Controls/GraphPortControl.cs | 10 ++++++++-- 5 files changed, 39 insertions(+), 11 deletions(-) diff --git a/src/FEntwumS.Common/Controls/GraphEdgeControl.cs b/src/FEntwumS.Common/Controls/GraphEdgeControl.cs index 630ca77..9fbc704 100644 --- a/src/FEntwumS.Common/Controls/GraphEdgeControl.cs +++ b/src/FEntwumS.Common/Controls/GraphEdgeControl.cs @@ -58,6 +58,18 @@ public override void Render(DrawingContext context) context.DrawGeometry(null, linePen, _contentGeometry); } + protected override void UpdateScale(double newScale) + { + double scaleDifference = newScale / Scale; + + base.UpdateScale(newScale); + + cullingOriginX *= scaleDifference; + cullingOriginY *= scaleDifference; + ElementWidth *= scaleDifference; + ElementHeight *= scaleDifference; + } + protected override void RegenerateDrawnElements(double newScale) { if (!this.IsVisible) @@ -70,8 +82,6 @@ protected override void RegenerateDrawnElements(double newScale) ElementWidth = newBounds.Width; ElementHeight = newBounds.Height; - cullingOriginX = newBounds.X + X; - cullingOriginY = newBounds.Y + Y; } #endregion diff --git a/src/FEntwumS.Common/Controls/GraphJunctionControl.cs b/src/FEntwumS.Common/Controls/GraphJunctionControl.cs index 59abb6b..4a16748 100644 --- a/src/FEntwumS.Common/Controls/GraphJunctionControl.cs +++ b/src/FEntwumS.Common/Controls/GraphJunctionControl.cs @@ -89,6 +89,14 @@ public override void Render(DrawingContext context) } } + protected override void UpdateScale(double newScale) + { + base.UpdateScale(newScale); + + cullingOriginX = X; + cullingOriginY = Y; + } + protected override void RegenerateDrawnElements(double newScale) { if (!this.IsVisible) @@ -97,8 +105,6 @@ protected override void RegenerateDrawnElements(double newScale) } _contentGeometry.Transform = new ScaleTransform(newScale, newScale); - cullingOriginX = X; - cullingOriginY = Y; } #endregion diff --git a/src/FEntwumS.Common/Controls/GraphLabelControl.cs b/src/FEntwumS.Common/Controls/GraphLabelControl.cs index ddf6ecb..3083530 100644 --- a/src/FEntwumS.Common/Controls/GraphLabelControl.cs +++ b/src/FEntwumS.Common/Controls/GraphLabelControl.cs @@ -65,6 +65,14 @@ public override void Render(DrawingContext context) } } + protected override void UpdateScale(double newScale) + { + base.UpdateScale(newScale); + + cullingOriginX = X; + cullingOriginY = Y; + } + protected override void RegenerateDrawnElements(double newScale) { if (!this.GetVisualParent().IsVisible || !this.IsVisible) @@ -78,9 +86,6 @@ protected override void RegenerateDrawnElements(double newScale) NetlistTheme.Typeface, Fontsize * newScale, NetlistTheme.TextBrush); - - cullingOriginX = X; - cullingOriginY = Y; } #endregion diff --git a/src/FEntwumS.Common/Controls/GraphNodeControl.cs b/src/FEntwumS.Common/Controls/GraphNodeControl.cs index e805732..a024f1b 100644 --- a/src/FEntwumS.Common/Controls/GraphNodeControl.cs +++ b/src/FEntwumS.Common/Controls/GraphNodeControl.cs @@ -154,6 +154,9 @@ protected override void UpdateScale(double newScale) } base.UpdateScale(newScale); + + cullingOriginX = X; + cullingOriginY = Y; } #endregion @@ -270,8 +273,6 @@ protected override void RegenerateDrawnElements(double newScale) } _contentGeometry.Transform = new ScaleTransform(newScale, newScale); - cullingOriginX = X; - cullingOriginY = Y; } #endregion diff --git a/src/FEntwumS.Common/Controls/GraphPortControl.cs b/src/FEntwumS.Common/Controls/GraphPortControl.cs index 7841d58..c671165 100644 --- a/src/FEntwumS.Common/Controls/GraphPortControl.cs +++ b/src/FEntwumS.Common/Controls/GraphPortControl.cs @@ -89,6 +89,14 @@ public override void Render(DrawingContext context) } } + protected override void UpdateScale(double newScale) + { + base.UpdateScale(newScale); + + cullingOriginX = X; + cullingOriginY = Y; + } + protected override void RegenerateDrawnElements(double newScale) { if (!this.IsVisible) @@ -97,8 +105,6 @@ protected override void RegenerateDrawnElements(double newScale) } _contentGeometry.Transform = new ScaleTransform(newScale, newScale); - cullingOriginX = X; - cullingOriginY = Y; } #endregion From ad3c35e18e87021fb02038054570b2034b6996af Mon Sep 17 00:00:00 2001 From: Flojo101 Date: Thu, 10 Sep 2026 15:53:36 +0200 Subject: [PATCH 13/15] Re-measure child labels for cel nodes when they become visible --- src/FEntwumS.Common/Controls/GraphNodeControl.cs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/FEntwumS.Common/Controls/GraphNodeControl.cs b/src/FEntwumS.Common/Controls/GraphNodeControl.cs index a024f1b..1e211f9 100644 --- a/src/FEntwumS.Common/Controls/GraphNodeControl.cs +++ b/src/FEntwumS.Common/Controls/GraphNodeControl.cs @@ -136,6 +136,19 @@ protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) base.OnAttachedToVisualTree(e); } + protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) + { + if (this is not GraphEntityNodeControl && change.Property == IsVisibleProperty) + { + foreach (PositionableSubControl childControl in Items) + { + childControl.InvalidateMeasure(); + } + } + + base.OnPropertyChanged(change); + } + private void ItemsChanged(object? sender, NotifyCollectionChangedEventArgs e) { AddChildrenToVisualTree(); From e2d0ad4f63350988e55c075337a94ade3cfa01eb Mon Sep 17 00:00:00 2001 From: Flojo101 Date: Thu, 10 Sep 2026 16:08:29 +0200 Subject: [PATCH 14/15] Invalidate measure after viewport update has been processed when executing ZoomToRect --- src/FEntwumS.Common/Controls/PanningControl.cs | 2 +- src/FEntwumS.Common/Controls/PanningNetlistControl.cs | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/FEntwumS.Common/Controls/PanningControl.cs b/src/FEntwumS.Common/Controls/PanningControl.cs index 84c9cb5..9875bf1 100644 --- a/src/FEntwumS.Common/Controls/PanningControl.cs +++ b/src/FEntwumS.Common/Controls/PanningControl.cs @@ -272,7 +272,7 @@ public void ZoomToFit() } } - private void ZoomToRect(Rect shownBounds) + protected virtual void ZoomToRect(Rect shownBounds) { if (Child is null) { diff --git a/src/FEntwumS.Common/Controls/PanningNetlistControl.cs b/src/FEntwumS.Common/Controls/PanningNetlistControl.cs index 1a9f0d7..1d1cace 100644 --- a/src/FEntwumS.Common/Controls/PanningNetlistControl.cs +++ b/src/FEntwumS.Common/Controls/PanningNetlistControl.cs @@ -66,6 +66,14 @@ protected override void OnPointerMoved(PointerEventArgs e) } } + protected override void ZoomToRect(Rect shownBounds) + { + base.ZoomToRect(shownBounds); + + UpdateViewPort(); + InvalidateMeasure(); + } + private void UpdateViewPort() { if (Child is GraphRootNodeControl graphRootNodeControl) From 048b5687d05fafb434ae2a313705f723be1086ea Mon Sep 17 00:00:00 2001 From: Flojo101 Date: Thu, 10 Sep 2026 16:36:03 +0200 Subject: [PATCH 15/15] Zoom to fit only on initial load --- .../Controls/PanningNetlistControl.cs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/FEntwumS.Common/Controls/PanningNetlistControl.cs b/src/FEntwumS.Common/Controls/PanningNetlistControl.cs index 1d1cace..3cc488a 100644 --- a/src/FEntwumS.Common/Controls/PanningNetlistControl.cs +++ b/src/FEntwumS.Common/Controls/PanningNetlistControl.cs @@ -32,6 +32,12 @@ public string ProjectRootFolder #endregion + #region Variables + + private bool hasBeenLoadedBefore = false; + + #endregion + #region Event Handling protected override void OnLoaded(RoutedEventArgs e) @@ -43,9 +49,11 @@ protected override void OnLoaded(RoutedEventArgs e) Child = null; } - if (Child is not null) + if (!hasBeenLoadedBefore && Child is not null) { - // ZoomToFit(); + hasBeenLoadedBefore = true; + + ZoomToFit(); } }