11using System ;
2+ using System . Collections . Generic ;
3+ using System . Diagnostics ;
24using System . Linq ;
35using System . Windows ;
46using System . Windows . Controls ;
@@ -17,9 +19,8 @@ public sealed class ShellInjectionService : IShellInjectionService
1719 private readonly ILaunchService _launchService ;
1820
1921 private LaunchyBarControl ? _barControl ;
20- private Grid ? _injectedGrid ;
21- private FrameworkElement ? _originalContent ;
22- private ContentPresenter ? _targetPresenter ;
22+ private Grid ? _targetGrid ;
23+ private ColumnDefinition ? _injectedColumn ;
2324
2425 private const double BarWidth = 48 ;
2526
@@ -35,56 +36,91 @@ public bool Inject()
3536 {
3637 ThreadHelper . ThrowIfNotOnUIThread ( ) ;
3738
39+ Debug . WriteLine ( "LaunchyBar: Inject() called" ) ;
40+
3841 if ( IsInjected )
42+ {
43+ Debug . WriteLine ( "LaunchyBar: Already injected" ) ;
3944 return true ;
45+ }
4046
4147 try
4248 {
4349 var mainWindow = Application . Current . MainWindow ;
4450 if ( mainWindow == null )
51+ {
52+ Debug . WriteLine ( "LaunchyBar: MainWindow is null" ) ;
4553 return false ;
54+ }
55+
56+ Debug . WriteLine ( $ "LaunchyBar: MainWindow found - { mainWindow . GetType ( ) . FullName } , Size: { mainWindow . ActualWidth } x{ mainWindow . ActualHeight } ") ;
4657
47- // Find the main content area - we're looking for the area between toolbar and status bar
48- // This requires walking VS's visual tree to find the right injection point
49- var injectionTarget = FindInjectionTarget ( mainWindow ) ;
50- if ( injectionTarget == null )
58+ // Find the main layout grid
59+ _targetGrid = FindMainLayoutGrid ( mainWindow ) ;
60+ if ( _targetGrid == null )
61+ {
62+ Debug . WriteLine ( "LaunchyBar: Could not find main layout grid" ) ;
5163 return false ;
64+ }
5265
53- _targetPresenter = injectionTarget ;
54- _originalContent = injectionTarget . Content as FrameworkElement ;
66+ Debug . WriteLine ( $ "LaunchyBar: Found target grid with { _targetGrid . RowDefinitions . Count } rows, { _targetGrid . ColumnDefinitions . Count } columns" ) ;
67+ Debug . WriteLine ( $ "LaunchyBar: Grid size: { _targetGrid . ActualWidth } x { _targetGrid . ActualHeight } " ) ;
5568
56- if ( _originalContent == null )
57- return false ;
69+ // Log existing children
70+ foreach ( UIElement child in _targetGrid . Children )
71+ {
72+ var childRow = Grid . GetRow ( child ) ;
73+ var childCol = Grid . GetColumn ( child ) ;
74+ var childRowSpan = Grid . GetRowSpan ( child ) ;
75+ var childColSpan = Grid . GetColumnSpan ( child ) ;
76+ Debug . WriteLine ( $ "LaunchyBar: Child: { child . GetType ( ) . Name } at Row={ childRow } , Col={ childCol } , RowSpan={ childRowSpan } , ColSpan={ childColSpan } ") ;
77+ }
5878
5979 // Create our bar control
6080 _barControl = new LaunchyBarControl ( _configurationService , _launchService ) ;
6181 _barControl . Width = BarWidth ;
6282 _barControl . HorizontalAlignment = HorizontalAlignment . Left ;
6383 _barControl . VerticalAlignment = VerticalAlignment . Stretch ;
6484
65- // Create a new grid to hold both the bar and the original content
66- _injectedGrid = new Grid ( ) ;
67- _injectedGrid . ColumnDefinitions . Add ( new ColumnDefinition { Width = new GridLength ( BarWidth ) } ) ;
68- _injectedGrid . ColumnDefinitions . Add ( new ColumnDefinition { Width = new GridLength ( 1 , GridUnitType . Star ) } ) ;
85+ // Check if grid originally had column definitions
86+ var hadColumns = _targetGrid . ColumnDefinitions . Count > 0 ;
87+ Debug . WriteLine ( $ "LaunchyBar: Grid originally had { _targetGrid . ColumnDefinitions . Count } columns") ;
6988
70- // Remove original content from its parent
71- injectionTarget . Content = null ;
89+ // If the grid had no columns, we need to add one for the existing content
90+ if ( ! hadColumns )
91+ {
92+ // Add a Star column for existing content first
93+ _targetGrid . ColumnDefinitions . Add ( new ColumnDefinition { Width = new GridLength ( 1 , GridUnitType . Star ) } ) ;
94+ Debug . WriteLine ( "LaunchyBar: Added Star column for existing content" ) ;
95+ }
7296
73- // Add bar to column 0
74- Grid . SetColumn ( _barControl , 0 ) ;
75- _injectedGrid . Children . Add ( _barControl ) ;
97+ // Insert our bar column at the beginning
98+ _injectedColumn = new ColumnDefinition { Width = new GridLength ( BarWidth ) } ;
99+ _targetGrid . ColumnDefinitions . Insert ( 0 , _injectedColumn ) ;
100+
101+ // Shift all existing children to the right by incrementing their column
102+ foreach ( UIElement child in _targetGrid . Children )
103+ {
104+ var currentCol = Grid . GetColumn ( child ) ;
105+ Grid . SetColumn ( child , currentCol + 1 ) ;
106+ Debug . WriteLine ( $ "LaunchyBar: Shifted { child . GetType ( ) . Name } from col { currentCol } to { currentCol + 1 } ") ;
107+ }
76108
77- // Add original content to column 1
78- Grid . SetColumn ( _originalContent , 1 ) ;
79- _injectedGrid . Children . Add ( _originalContent ) ;
109+ // Add our bar at column 0, spanning all rows (top to bottom)
110+ Grid . SetColumn ( _barControl , 0 ) ;
111+ Grid . SetRow ( _barControl , 0 ) ;
112+ Grid . SetRowSpan ( _barControl , Math . Max ( 1 , _targetGrid . RowDefinitions . Count ) ) ;
113+ _targetGrid . Children . Add ( _barControl ) ;
80114
81- // Set our grid as the new content
82- injectionTarget . Content = _injectedGrid ;
115+ Debug . WriteLine ( "LaunchyBar: Injection successful!" ) ;
116+ Debug . WriteLine ( $ "LaunchyBar: Bar at column 0, spanning { Grid . GetRowSpan ( _barControl ) } rows" ) ;
83117
84118 return true ;
85119 }
86- catch ( Exception )
120+ catch ( Exception ex )
87121 {
122+ Debug . WriteLine ( $ "LaunchyBar: Exception during injection - { ex . Message } ") ;
123+ Debug . WriteLine ( $ "LaunchyBar: Stack trace: { ex . StackTrace } ") ;
88124 Remove ( ) ;
89125 return false ;
90126 }
@@ -94,12 +130,25 @@ public void Remove()
94130 {
95131 ThreadHelper . ThrowIfNotOnUIThread ( ) ;
96132
97- if ( _targetPresenter != null && _originalContent != null && _injectedGrid != null )
133+ if ( _targetGrid != null && _barControl != null && _injectedColumn != null )
98134 {
99135 try
100136 {
101- _injectedGrid . Children . Clear ( ) ;
102- _targetPresenter . Content = _originalContent ;
137+ // Remove our bar
138+ _targetGrid . Children . Remove ( _barControl ) ;
139+
140+ // Shift all children back
141+ foreach ( UIElement child in _targetGrid . Children )
142+ {
143+ var currentCol = Grid . GetColumn ( child ) ;
144+ if ( currentCol > 0 )
145+ {
146+ Grid . SetColumn ( child , currentCol - 1 ) ;
147+ }
148+ }
149+
150+ // Remove our column
151+ _targetGrid . ColumnDefinitions . Remove ( _injectedColumn ) ;
103152 }
104153 catch
105154 {
@@ -108,108 +157,79 @@ public void Remove()
108157 }
109158
110159 _barControl = null ;
111- _injectedGrid = null ;
112- _originalContent = null ;
113- _targetPresenter = null ;
160+ _targetGrid = null ;
161+ _injectedColumn = null ;
114162 }
115163
116164 /// <summary>
117- /// Finds the ContentPresenter that contains the main VS content area .
118- /// This is the area between the toolbar and status bar .
165+ /// Finds the main layout Grid in VS's visual tree .
166+ /// This should be the grid that contains the toolbar and main content area .
119167 /// </summary>
120- private ContentPresenter ? FindInjectionTarget ( Window mainWindow )
168+ private Grid ? FindMainLayoutGrid ( Window mainWindow )
121169 {
122- // Strategy: Walk the visual tree looking for a ContentPresenter
123- // that contains the main dock/editor area.
124- // This is fragile and may need adjustment for different VS versions.
125-
126- // Look for a ContentPresenter with a specific name or structure
127- // VS typically has a structure like:
128- // MainWindow > ... > DockPanel > [Toolbar, ContentPresenter (main area), StatusBar]
129-
130- return FindContentPresenterRecursive ( mainWindow , 0 ) ;
170+ // Walk the visual tree to find the main layout grid
171+ // We're looking for a large grid that contains the main VS content
172+ return FindMainGridRecursive ( mainWindow , 0 ) ;
131173 }
132174
133- private ContentPresenter ? FindContentPresenterRecursive ( DependencyObject parent , int depth )
175+ private Grid ? FindMainGridRecursive ( DependencyObject parent , int depth )
134176 {
135- if ( depth > 20 ) // Prevent infinite recursion
177+ if ( depth > 15 )
136178 return null ;
137179
138180 var childCount = VisualTreeHelper . GetChildrenCount ( parent ) ;
181+ var indent = new string ( ' ' , depth * 2 ) ;
139182
140183 for ( int i = 0 ; i < childCount ; i ++ )
141184 {
142185 var child = VisualTreeHelper . GetChild ( parent , i ) ;
143186
144- // Look for ContentPresenter that might be our target
145- if ( child is ContentPresenter cp )
187+ if ( depth < 5 )
146188 {
147- // Check if this ContentPresenter's content looks like the main content area
148- if ( IsMainContentArea ( cp ) )
149- {
150- return cp ;
151- }
189+ Debug . WriteLine ( $ "LaunchyBar: { indent } [{ depth } ] { child . GetType ( ) . Name } ") ;
152190 }
153191
154- // Also check for Grid with DockPanel children - common VS structure
155192 if ( child is Grid grid )
156193 {
157- // Look for the main content grid that has the editor/tool area
158- var result = FindContentPresenterRecursive ( grid , depth + 1 ) ;
159- if ( result != null )
160- return result ;
161- }
162-
163- if ( child is DockPanel dockPanel )
164- {
165- var result = FindContentPresenterRecursive ( dockPanel , depth + 1 ) ;
166- if ( result != null )
167- return result ;
168- }
169-
170- if ( child is Border border )
171- {
172- var result = FindContentPresenterRecursive ( border , depth + 1 ) ;
173- if ( result != null )
174- return result ;
194+ // Look for a grid that:
195+ // 1. Is large enough
196+ // 2. Has row definitions (VS uses rows for title/toolbar/content/statusbar)
197+ // 3. Contains relevant VS controls
198+ if ( grid . ActualWidth > 400 && grid . ActualHeight > 300 )
199+ {
200+ Debug . WriteLine ( $ "LaunchyBar: { indent } Grid candidate: { grid . ActualWidth } x{ grid . ActualHeight } , Rows={ grid . RowDefinitions . Count } , Cols={ grid . ColumnDefinitions . Count } ") ;
201+
202+ // Check if this grid contains VsToolBarHostControl or similar
203+ if ( ContainsVsContent ( grid ) )
204+ {
205+ Debug . WriteLine ( $ "LaunchyBar: { indent } ** MATCHED - contains VS content **") ;
206+ return grid ;
207+ }
208+ }
175209 }
176210
177- if ( child is Decorator decorator )
178- {
179- var result = FindContentPresenterRecursive ( decorator , depth + 1 ) ;
180- if ( result != null )
181- return result ;
182- }
211+ // Continue searching
212+ var result = FindMainGridRecursive ( child , depth + 1 ) ;
213+ if ( result != null )
214+ return result ;
183215 }
184216
185217 return null ;
186218 }
187219
188- private bool IsMainContentArea ( ContentPresenter cp )
220+ private bool ContainsVsContent ( Grid grid )
189221 {
190- // Heuristics to identify the main content area:
191- // 1. Should be reasonably large
192- // 2. Should contain dock-related content
193-
194- if ( cp . ActualWidth < 400 || cp . ActualHeight < 300 )
195- return false ;
196-
197- // Check if the content's type name contains dock-related keywords
198- var content = cp . Content ;
199- if ( content == null )
200- return false ;
201-
202- var typeName = content . GetType ( ) . FullName ?? "" ;
203-
204- // VS's main content area typically has these in the type hierarchy
205- if ( typeName . Contains ( "Dock" ) ||
206- typeName . Contains ( "ViewManager" ) ||
207- typeName . Contains ( "MainWindow" ) ||
208- typeName . Contains ( "Workspace" ) )
222+ foreach ( UIElement child in grid . Children )
209223 {
210- return true ;
224+ var typeName = child . GetType ( ) . Name ;
225+ // Look for VS-specific controls that indicate this is the main layout grid
226+ if ( typeName . Contains ( "ToolBar" ) ||
227+ typeName . Contains ( "DockPanel" ) ||
228+ typeName . Contains ( "MainWindowTitleBar" ) )
229+ {
230+ return true ;
231+ }
211232 }
212-
213233 return false ;
214234 }
215235
0 commit comments