| 1 | #nullable enable |
| 2 | using CascadeIDE.Cockpit.Graph; |
| 3 | using CascadeIDE.Cockpit.Graph.Layout; |
| 4 | using CascadeIDE.Models; |
| 5 | using CascadeIDE.Services.CodeNavigation; |
| 6 | using CascadeIDE.Services.SkiaInstruments; |
| 7 | using CascadeIDE.ViewModels; |
| 8 | |
| 9 | namespace CascadeIDE.Features.WorkspaceNavigation.Application; |
| 10 | |
| 11 | /// <summary>Композитор graph-backed карты намерений (Intent → Declutter → Layout), ADR 0055 / 0115.</summary> |
| 12 | public sealed class CodeNavigationMapCompositor : ICodeNavigationMapCompositor |
| 13 | { |
| 14 | public const double DefaultWidth = GraphViewportMetrics.DefaultWidth; |
| 15 | public const double DefaultHeightFile = GraphViewportMetrics.DefaultHeightFile; |
| 16 | public const double DefaultHeightControlFlow = GraphViewportMetrics.DefaultHeightControlFlow; |
| 17 | public const double MaxHeightControlFlow = GraphViewportMetrics.MaxHeightControlFlow; |
| 18 | |
| 19 | private readonly ICodeNavigationMapIntentStage _intentStage; |
| 20 | private readonly ICodeNavigationMapDeclutterStage _declutterStage; |
| 21 | private readonly ICodeNavigationMapLayoutStage _layoutStage; |
| 22 | |
| 23 | public CodeNavigationMapCompositor( |
| 24 | Func<string, IGraphLayoutEngine>? fileLayoutResolver = null, |
| 25 | IGraphLayoutEngine? controlFlowLayout = null, |
| 26 | ICodeNavigationMapIntentStage? intentStage = null, |
| 27 | ICodeNavigationMapDeclutterStage? declutterStage = null, |
| 28 | ICodeNavigationMapLayoutStage? layoutStage = null) |
| 29 | { |
| 30 | _intentStage = intentStage ?? new CodeNavigationMapIntentStage(); |
| 31 | _declutterStage = declutterStage ?? new CodeNavigationMapDeclutterStage(_intentStage); |
| 32 | _layoutStage = layoutStage ?? new CodeNavigationMapLayoutStage(fileLayoutResolver, controlFlowLayout); |
| 33 | } |
| 34 | |
| 35 | public CodeNavigationMapCompositionResult Compose(CodeNavigationMapCompositionIntent intent, in SkiaInstrumentViewport viewport) |
| 36 | { |
| 37 | var context = new CodeNavigationMapPipelineContext( |
| 38 | intent.Subgraph, |
| 39 | intent.MapLevel, |
| 40 | viewport, |
| 41 | intent.DetailLevel, |
| 42 | intent.RelatedGraphLayout, |
| 43 | intent.ControlFlowMainAxis); |
| 44 | var resolved = _intentStage.Resolve(context); |
| 45 | var decluttered = _declutterStage.Apply(resolved); |
| 46 | return WithCodeNavigationInstrumentBlocks(_layoutStage.Layout(decluttered), decluttered); |
| 47 | } |
| 48 | |
| 49 | public CodeNavigationMapCompositionResult Compose( |
| 50 | GraphDocument doc, |
| 51 | string mapLevel, |
| 52 | double availableWidth, |
| 53 | double availableHeight, |
| 54 | CodeNavigationMapDetailLevel detailLevel = CodeNavigationMapDetailLevel.Normal) |
| 55 | { |
| 56 | var context = new CodeNavigationMapPipelineContext( |
| 57 | doc, |
| 58 | mapLevel, |
| 59 | new SkiaInstrumentViewport(availableWidth, availableHeight), |
| 60 | detailLevel, |
| 61 | CodeNavigationMapRelatedGraphLayoutKind.Radial, |
| 62 | CodeNavigationMapControlFlowMainAxisKind.Auto); |
| 63 | var resolved = _intentStage.Resolve(context); |
| 64 | var decluttered = _declutterStage.Apply(resolved); |
| 65 | return WithCodeNavigationInstrumentBlocks(_layoutStage.Layout(decluttered), decluttered); |
| 66 | } |
| 67 | |
| 68 | private static CodeNavigationMapCompositionResult WithCodeNavigationInstrumentBlocks( |
| 69 | CodeNavigationMapCompositionResult laidOut, |
| 70 | in CodeNavigationMapPipelineState state) |
| 71 | { |
| 72 | var vw = state.Viewport.Width > 0 ? state.Viewport.Width : DefaultWidth; |
| 73 | var vh = laidOut.PreferredHeight; |
| 74 | var blocks = CodeNavigationMapInstrumentBlockCompositor.Compose(laidOut.ToSceneVm(vw, vh), vw, vh); |
| 75 | return laidOut with { CodeNavigationMapInstrumentBlocks = blocks }; |
| 76 | } |
| 77 | } |
| 78 | |