Forge
csharpdeeb25a2
1using System.Windows.Input;
2using Avalonia;
3using Avalonia.Controls;
4using Avalonia.Input;
5using Avalonia.Media;
6using CascadeIDE.ViewModels;
7using CascadeIDE.Views.SkiaKit.Graph;
8
9namespace CascadeIDE.Views;
10
11/// <summary>Мини-карта навигации по коду: рёбра и узлы (звезда); клик по узлу открывает файл; подписи строк — в списке (режим list/both) (ADR 0039).</summary>
12/// <remarks>
13/// Порядок отрисовки базовой сцены (ADR 0055 §4): рёбра → узлы (фигуры и глифы) → легенда. Подсветки TraceFlow приходят в <see cref="CodeNavigationMapGraphSceneVm"/> и рисуются вместе с рёбрами/узлами по флагам highlight.
14/// Отрисовка — <see cref="SkiaGraphSceneDrawing"/> (SkiaKit, ADR 0117).
15/// </remarks>
16public sealed class CodeNavigationMapMiniMapControl : Control
17{
18 public static readonly StyledProperty<CodeNavigationMapGraphSceneVm?> SceneProperty =
19 AvaloniaProperty.Register<CodeNavigationMapMiniMapControl, CodeNavigationMapGraphSceneVm?>(nameof(Scene));
20
21 public static readonly StyledProperty<ICommand?> OpenFileCommandProperty =
22 AvaloniaProperty.Register<CodeNavigationMapMiniMapControl, ICommand?>(nameof(OpenFileCommand));
23
24 static CodeNavigationMapMiniMapControl()
25 {
26 AffectsRender<CodeNavigationMapMiniMapControl>(SceneProperty);
27 HeightProperty.OverrideDefaultValue<CodeNavigationMapMiniMapControl>(SkiaGraphViewportMetrics.DefaultHeightFile);
28 MinWidthProperty.OverrideDefaultValue<CodeNavigationMapMiniMapControl>(200);
29 }
30
31 public CodeNavigationMapGraphSceneVm? Scene
32 {
33 get => GetValue(SceneProperty);
34 set => SetValue(SceneProperty, value);
35 }
36
37 public ICommand? OpenFileCommand
38 {
39 get => GetValue(OpenFileCommandProperty);
40 set => SetValue(OpenFileCommandProperty, value);
41 }
42
43 protected override void OnPointerPressed(PointerPressedEventArgs e)
44 {
45 base.OnPointerPressed(e);
46 var scene = Scene;
47 var cmd = OpenFileCommand;
48 if (scene is null || cmd is null)
49 return;
50 var layout = GraphLayoutSceneMapper.FromViewModel(scene);
51 var p = SkiaGraphSceneHitTesting.MapControlPointToLayout(
52 e.GetPosition(this),
53 Bounds.Width,
54 Bounds.Height,
55 scene.LayoutViewportWidth,
56 scene.LayoutViewportHeight);
57 var hit = SkiaGraphSceneHitTesting.FindNodeAt(layout, p);
58 if (hit is null || string.IsNullOrWhiteSpace(hit.FullPath))
59 return;
60
61 var vmNode = scene.Nodes.FirstOrDefault(n => string.Equals(n.Id, hit.Id, StringComparison.OrdinalIgnoreCase));
62 if (vmNode is null)
63 return;
64
65 var payload = new CodeNavigationMapNodeNavigatePayload(
66 vmNode.FullPath,
67 vmNode.LineStart,
68 vmNode.LineEnd,
69 vmNode.LegendLine,
70 vmNode.Kind);
71 if (!cmd.CanExecute(payload))
72 return;
73 try
74 {
75 cmd.Execute(payload);
76 e.Handled = true;
77 }
78 catch
79 {
80 // RelayCommand/ICommand: не роняем UI при кривом пути или сбое дока
81 }
82 }
83
84 public override void Render(DrawingContext context)
85 {
86 base.Render(context);
87 var scene = Scene;
88 if (scene is null || scene.IsEmpty)
89 return;
90
91 var w = Bounds.Width;
92 var h = Bounds.Height;
93 if (w <= 0 || h <= 0)
94 return;
95
96 var layout = GraphLayoutSceneMapper.FromViewModel(scene);
97 var theme = SkiaGraphVisualTheme.ForPresentation(layout.Presentation);
98 SkiaGraphSceneDrawing.DrawScene(context, layout, theme, w, h);
99 }
100}
101
View only · write via MCP/CIDE