Forge
csharp4405de34
1#nullable enable
2using Avalonia;
3using Avalonia.Controls;
4using Avalonia.Controls.ApplicationLifetimes;
5using Avalonia.VisualTree;
6using CascadeIDE.Models;
7using CascadeIDE.ViewModels;
8using CascadeIDE.Views;
9
10namespace CascadeIDE.Services;
11
12/// <summary>
13/// Выбор активного <see cref="DockDocumentView"/> при нескольких <see cref="DocumentsDockView"/>
14/// (Forward + Mfd + <see cref="MfdHostWindow"/>). ADR 0120/0017.
15/// </summary>
16public static class EditorActiveDockResolver
17{
18 public static DockDocumentView? TryGetDockDocumentView(MainWindowViewModel vm, string? filePath)
19 {
20 if (string.IsNullOrWhiteSpace(filePath))
21 return null;
22
23 DockDocumentView? best = null;
24 var bestScore = int.MinValue;
25
26 foreach (var window in EnumerateHostWindows())
27 {
28 if (!ReferenceEquals(window.DataContext, vm))
29 continue;
30
31 foreach (var dockView in EnumerateDescendants<DockDocumentView>(window))
32 {
33 if (dockView.DataContext is not DockDocumentViewModel dv)
34 continue;
35
36 if (!string.Equals(dv.Doc.FilePath, filePath, StringComparison.OrdinalIgnoreCase))
37 continue;
38
39 var score = Score(vm, dockView, window);
40 if (score > bestScore)
41 {
42 bestScore = score;
43 best = dockView;
44 }
45 }
46 }
47
48 return best;
49 }
50
51 private static IEnumerable<Window> EnumerateHostWindows()
52 {
53 if (Application.Current?.ApplicationLifetime is not IClassicDesktopStyleApplicationLifetime desktop)
54 yield break;
55
56 foreach (var w in desktop.Windows)
57 {
58 if (w is Window win)
59 yield return win;
60 }
61 }
62
63 private static int Score(MainWindowViewModel vm, DockDocumentView dockView, Window window)
64 {
65 var score = 0;
66 if (IsVisuallyShown(dockView))
67 score += 100;
68 if (dockView.IsKeyboardFocusWithin)
69 score += 80;
70 if (window.IsActive)
71 score += 40;
72
73 var inMfdHost = window is MfdHostWindow;
74 var inEditorMfdPage = HasAncestor<EditorMfdPageView>(dockView);
75 var inForwardDock = HasAncestor<DocumentsDockView>(dockView) && !inMfdHost && !inEditorMfdPage;
76
77 if (vm.PrimaryWorkSurface == PrimaryWorkSurfaceKind.Intercom)
78 {
79 if (inMfdHost && vm.CurrentMfdShellPage == MfdShellPage.Editor)
80 score += 300;
81 else if (inEditorMfdPage && vm.IsMfdColumnVisible && vm.CurrentMfdShellPage == MfdShellPage.Editor)
82 score += 250;
83
84 if (inForwardDock)
85 {
86 if (!vm.IsForwardEditorHostVisible)
87 score -= 400;
88 else
89 score -= 150;
90 }
91 }
92 else if (inForwardDock && vm.IsForwardEditorHostVisible)
93 {
94 score += 300;
95 }
96
97 if (vm.IsMfdHostWindowShellOpen)
98 {
99 if (inMfdHost)
100 score += 350;
101 if (inEditorMfdPage)
102 score -= 350;
103 }
104
105 return score;
106 }
107
108 private static bool IsVisuallyShown(Visual visual)
109 {
110 for (var cur = visual; cur is not null; cur = cur.GetVisualParent())
111 {
112 if (cur is Visual v && !v.IsVisible)
113 return false;
114 }
115
116 return true;
117 }
118
119 private static bool HasAncestor<T>(Visual visual) where T : Visual
120 {
121 for (var cur = visual.GetVisualParent(); cur is not null; cur = cur.GetVisualParent())
122 {
123 if (cur is T)
124 return true;
125 }
126
127 return false;
128 }
129
130 private static IEnumerable<T> EnumerateDescendants<T>(Visual root) where T : Visual
131 {
132 foreach (var child in root.GetVisualChildren())
133 {
134 if (child is T match)
135 yield return match;
136
137 if (child is Visual childVisual)
138 {
139 foreach (var nested in EnumerateDescendants<T>(childVisual))
140 yield return nested;
141 }
142 }
143 }
144}
145
View only · write via MCP/CIDE