Skip to content

Commit 0918878

Browse files
committed
Chore: Copilot feedback
1 parent 8078fbe commit 0918878

3 files changed

Lines changed: 62 additions & 7 deletions

File tree

Source/NETworkManager.Localization/Resources/Strings.Designer.cs

Lines changed: 9 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Source/NETworkManager.Localization/Resources/Strings.resx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -582,6 +582,9 @@
582582
<data name="ScrollToZoomDragToPan" xml:space="preserve">
583583
<value>Scroll = Zoom, Drag = Pan</value>
584584
</data>
585+
<data name="Unknown" xml:space="preserve">
586+
<value>Unknown</value>
587+
</data>
585588
<data name="Maximum" xml:space="preserve">
586589
<value>Maximum</value>
587590
</data>

Source/NETworkManager/Controls/TracerouteMapControl.xaml.cs

Lines changed: 50 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -224,8 +224,38 @@ private static void OnIsExpandedChanged(DependencyObject d, DependencyPropertyCh
224224

225225
#region Load, draw
226226

227+
// Guards EnsureMapDataInitialized so the (per-instance) ~900-element label/city build below
228+
// only ever runs once per control, regardless of how many times Loaded or IsVisibleChanged fire.
229+
private bool _mapDataInitialized;
230+
227231
private void TracerouteMapControl_Loaded(object sender, RoutedEventArgs e)
228232
{
233+
// TracerouteView binds this control's Visibility to ShowMap, which is false whenever IP
234+
// geolocation checking is disabled - the map can then never show anything (no hop has a
235+
// location to plot), so building ~900 city/country WPF elements for it here would be pure
236+
// waste. Defer until it's actually shown; if it already is, this fires immediately below.
237+
if (IsVisible)
238+
EnsureMapDataInitialized();
239+
else
240+
IsVisibleChanged += TracerouteMapControl_IsVisibleChanged;
241+
}
242+
243+
private void TracerouteMapControl_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
244+
{
245+
if (!IsVisible)
246+
return;
247+
248+
IsVisibleChanged -= TracerouteMapControl_IsVisibleChanged;
249+
EnsureMapDataInitialized();
250+
}
251+
252+
private void EnsureMapDataInitialized()
253+
{
254+
if (_mapDataInitialized)
255+
return;
256+
257+
_mapDataInitialized = true;
258+
229259
// The embedded world map is ~1.4 MB of JSON; parsing it and building the (frozen)
230260
// country geometry synchronously here would briefly hitch the UI thread the first time a
231261
// Traceroute tab is opened. Materialize the shared Lazy caches on a background thread
@@ -894,6 +924,13 @@ private Action<bool> DrawArrow((List<TracerouteHopInfo> Hops, ClusteredPoint Pos
894924
(List<TracerouteHopInfo> Hops, ClusteredPoint Position, IPGeolocationInfo Info) to,
895925
Action<bool> highlightFromMarker, Action<bool> highlightToMarker)
896926
{
927+
// A hop between these two groups was dropped for lacking geolocation (see the filter in
928+
// RedrawHops) - the direct line drawn below isn't the actual route then, just the closest
929+
// honest approximation. Rendered in a muted gray (see below) and labelled "Unknown" in the
930+
// tooltip instead of the usual accent color, so it doesn't read as an equally-confident,
931+
// fully observed segment.
932+
var isGap = to.Hops[0].Hop != from.Hops[^1].Hop + 1;
933+
897934
// Skip hops that resolve to (nearly) the same on-screen position. Judged on the resolved,
898935
// cluster-displaced positions (at the route's own just-computed fit scale) rather than the
899936
// raw geo anchors - two cluster members of the same location group (see AssignClusterIds)
@@ -909,7 +946,9 @@ private Action<bool> DrawArrow((List<TracerouteHopInfo> Hops, ClusteredPoint Pos
909946
if (Math.Sqrt(resolvedDx * resolvedDx + resolvedDy * resolvedDy) < 0.01)
910947
return static _ => { };
911948

912-
var tooltip = $"{FormatLocation(from.Info, includeRegion: true)}\n\n{FormatLocation(to.Info, includeRegion: true)}";
949+
var tooltip = isGap
950+
? $"{FormatLocation(from.Info, includeRegion: true)}\n\n{Strings.Unknown}\n\n{FormatLocation(to.Info, includeRegion: true)}"
951+
: $"{FormatLocation(from.Info, includeRegion: true)}\n\n{FormatLocation(to.Info, includeRegion: true)}";
913952

914953
var geometry = new PathGeometry();
915954
var figure = new PathFigure { IsClosed = false };
@@ -946,7 +985,7 @@ private Action<bool> DrawArrow((List<TracerouteHopInfo> Hops, ClusteredPoint Pos
946985
IsHitTestVisible = false
947986
};
948987

949-
path.SetResourceReference(Shape.StrokeProperty, "MahApps.Brushes.Accent");
988+
path.SetResourceReference(Shape.StrokeProperty, isGap ? "MahApps.Brushes.Gray3" : "MahApps.Brushes.Accent");
950989

951990
HopsCanvas.Children.Add(path);
952991

@@ -959,7 +998,7 @@ private Action<bool> DrawArrow((List<TracerouteHopInfo> Hops, ClusteredPoint Pos
959998
IsHitTestVisible = false
960999
};
9611000

962-
arrowHead.SetResourceReference(Shape.FillProperty, "MahApps.Brushes.Accent");
1001+
arrowHead.SetResourceReference(Shape.FillProperty, isGap ? "MahApps.Brushes.Gray3" : "MahApps.Brushes.Accent");
9631002

9641003
HopsCanvas.Children.Add(arrowHead);
9651004

@@ -1100,7 +1139,7 @@ private static void UpdateArrowGeometry(ArrowVisual arrow, double inverseScale)
11001139
}
11011140

11021141
/// <summary>
1103-
/// Shows the fixed info panel (top-left) with the given text - see the XAML comment on
1142+
/// Shows the fixed info panel (bottom-right) with the given text - see the XAML comment on
11041143
/// InfoPanel for why this replaces a WPF ToolTip popup for hop markers/arrows.
11051144
/// </summary>
11061145
private void ShowInfoPanel(string text)
@@ -1258,9 +1297,13 @@ private void FitToHops(IReadOnlyList<Point> points, double extraPaddingScreenPix
12581297
var centerX = (rawMinX + rawMaxX) / 2;
12591298
var centerY = (rawMinY + rawMaxY) / 2;
12601299

1261-
return (scale,
1262-
BorderHost.ActualWidth / 2 - centerX * scale,
1263-
BorderHost.ActualHeight / 2 - centerY * scale);
1300+
// Clamped the same way as FitToView/wheel zoom/drag-pan - otherwise a route near the map's
1301+
// edge (e.g. close to +/-180 deg longitude or a pole) can center a pan that leaves blank
1302+
// canvas past the map's real edge in the viewport.
1303+
var panX = ClampPan(BorderHost.ActualWidth / 2 - centerX * scale, MapWidth * scale, BorderHost.ActualWidth);
1304+
var panY = ClampPan(BorderHost.ActualHeight / 2 - centerY * scale, MapHeight * scale, BorderHost.ActualHeight);
1305+
1306+
return (scale, panX, panY);
12641307
}
12651308

12661309
private void ApplyTransform()

0 commit comments

Comments
 (0)