Skip to content

Commit 3ffcb4d

Browse files
committed
Add PopupList scroll bar checking
IntelliSenseServer v 0.0.7
1 parent 50ba35a commit 3ffcb4d

File tree

4 files changed

+45
-112
lines changed

4 files changed

+45
-112
lines changed

Source/ExcelDna.IntelliSense/IntelliSenseDisplay.cs

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -150,15 +150,15 @@ void StateUpdate(object sender, UIStateUpdate update)
150150
// TODO: TEMP
151151
_functionListWindow = fls.FunctionListWindow;
152152
FunctionListShow();
153-
FunctionListSelectedItemChange(fls.SelectedItemText, fls.SelectedItemBounds);
153+
FunctionListSelectedItemChange(fls.SelectedItemText, fls.SelectedItemBounds, fls.HasScrollBar);
154154
break;
155155
case UIStateUpdate.UpdateType.FunctionListMove:
156156
var flm = (UIState.FunctionList)update.NewState;
157-
FunctionListMove(flm.SelectedItemBounds);
157+
FunctionListMove(flm.SelectedItemBounds, flm.HasScrollBar);
158158
break;
159159
case UIStateUpdate.UpdateType.FunctionListSelectedItemChange:
160160
var fl = (UIState.FunctionList)update.NewState;
161-
FunctionListSelectedItemChange(fl.SelectedItemText, fl.SelectedItemBounds);
161+
FunctionListSelectedItemChange(fl.SelectedItemText, fl.SelectedItemBounds, fl.HasScrollBar);
162162
break;
163163
case UIStateUpdate.UpdateType.FunctionListHide:
164164
FunctionListHide();
@@ -306,9 +306,9 @@ void FunctionListHide()
306306
}
307307

308308
// Runs on the main thread
309-
void FunctionListSelectedItemChange(string selectedItemText, Rect selectedItemBounds)
309+
void FunctionListSelectedItemChange(string selectedItemText, Rect selectedItemBounds, bool hasScrollBar)
310310
{
311-
Logger.Display.Verbose($"IntelliSenseDisplay - PopupListSelectedItemChanged - New text - {selectedItemText}, Thread {Thread.CurrentThread.ManagedThreadId}");
311+
Logger.Display.Verbose($"IntelliSenseDisplay - PopupListSelectedItemChanged - New text - {selectedItemText} ({(hasScrollBar ? "Scroll" : "NoScroll")})");
312312

313313
IntelliSenseFunctionInfo functionInfo;
314314
if (_functionInfoMap.TryGetValue(selectedItemText, out functionInfo))
@@ -317,9 +317,10 @@ void FunctionListSelectedItemChange(string selectedItemText, Rect selectedItemBo
317317
var descriptionLines = GetFunctionDescriptionOrNull(functionInfo);
318318
if (descriptionLines != null)
319319
{
320+
var leftMargin = hasScrollBar ? 21 : 4;
320321
_descriptionToolTip.ShowToolTip(
321322
text: new FormattedText { GetFunctionDescriptionOrNull(functionInfo) },
322-
left: (int)selectedItemBounds.Right + 21,
323+
left: (int)selectedItemBounds.Right + leftMargin,
323324
top: (int)selectedItemBounds.Top);
324325
return;
325326
}
@@ -329,9 +330,10 @@ void FunctionListSelectedItemChange(string selectedItemText, Rect selectedItemBo
329330
_descriptionToolTip.Hide();
330331
}
331332

332-
void FunctionListMove(Rect selectedItemBounds)
333+
void FunctionListMove(Rect selectedItemBounds, bool hasScrollBar)
333334
{
334-
_descriptionToolTip.MoveToolTip((int)selectedItemBounds.Right + 21, (int)selectedItemBounds.Top);
335+
var leftMargin = hasScrollBar ? 21 : 4;
336+
_descriptionToolTip.MoveToolTip((int)selectedItemBounds.Right + leftMargin, (int)selectedItemBounds.Top);
335337
}
336338

337339
// TODO: Performance / efficiency - cache these somehow

Source/ExcelDna.IntelliSense/IntelliSenseServer.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ namespace ExcelDna.IntelliSense
4747
// REMEMBER: COM events are not necessarily safe macro contexts.
4848
public static class IntelliSenseServer
4949
{
50-
const string ServerVersion = "0.0.6"; // TODO: Define and manage this somewhere else
50+
const string ServerVersion = "0.0.7"; // TODO: Define and manage this somewhere else
5151

5252
// NOTE: Do not change these constants in custom versions.
5353
// They are part of the co-operative safety mechanism allowing different add-ins providing IntelliSense to work together safely.

Source/ExcelDna.IntelliSense/UIMonitor/PopupListWatcher.cs

Lines changed: 16 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ class PopupListWatcher : IDisposable
2020
public bool IsVisible{ get; set; } = false;
2121
public string SelectedItemText { get; set; } = string.Empty;
2222
public Rect SelectedItemBounds { get; set; } = Rect.Empty;
23+
public bool HasVerticalScrollBar { get; set; } = false;
2324
public IntPtr PopupListHandle => _hwndPopupList;
2425

2526
SynchronizationContext _syncContextAuto;
@@ -85,7 +86,7 @@ void _windowWatcher_PopupListWindowChanged(object sender, WindowWatcher.WindowCh
8586
case WindowWatcher.WindowChangedEventArgs.ChangeType.Hide:
8687
Logger.WindowWatcher.Verbose($"PopupList window hide");
8788
IsVisible = false;
88-
UpdateSelectedItem(_selectedItem);
89+
UpdateSelectedItem(_selectedItem, false);
8990
break;
9091
case WindowWatcher.WindowChangedEventArgs.ChangeType.Focus:
9192
case WindowWatcher.WindowChangedEventArgs.ChangeType.Unfocus:
@@ -128,7 +129,8 @@ void PopupListBoundsChanged(object sender, AutomationPropertyChangedEventArgs e)
128129
if (IsVisible && _selectedItem != null)
129130
{
130131
// Debug.Print($">>>> PopupListWatcher.LocationChanged on thread {Thread.CurrentThread.ManagedThreadId}");
131-
UpdateSelectedItem(_selectedItem);
132+
// We assume the scroll bar presence has not changed
133+
UpdateSelectedItem(_selectedItem, HasVerticalScrollBar);
132134
}
133135

134136
//_syncContextAuto.Post(delegate
@@ -165,7 +167,12 @@ void PopupListBoundsChanged(object sender, AutomationPropertyChangedEventArgs e)
165167
void PopupListElementSelectedHandler(object sender, AutomationEventArgs e)
166168
{
167169
Logger.WindowWatcher.Verbose($"PopupList PopupListElementSelectedHandler on thread {Thread.CurrentThread.ManagedThreadId}");
168-
UpdateSelectedItem(sender as AutomationElement);
170+
var selectedItem = (AutomationElement)sender;
171+
// CONSIDER: Maybe monitor changes to scrollbar as an event? (for performance)
172+
var walker = TreeWalker.ControlViewWalker;
173+
var list = walker.GetParent(walker.GetParent(selectedItem));
174+
var hasVerticalScrollBar = (bool)list.GetCurrentPropertyValue(AutomationElement.IsScrollPatternAvailableProperty);
175+
UpdateSelectedItem(selectedItem, hasVerticalScrollBar);
169176
}
170177

171178
// Runs on our automation thread
@@ -202,12 +209,14 @@ void UpdateSelectedItem()
202209
if (listElement != null)
203210
{
204211
var selectionPattern = listElement.GetCurrentPattern(SelectionPattern.Pattern) as SelectionPattern;
212+
// CONSIDER: We might dig in a big more (e.g. is horizontal scrolling possible?)
213+
var hasVerticalScrollBar = (bool)listElement.GetCurrentPropertyValue(AutomationElement.IsScrollPatternAvailableProperty);
205214
var currentSelection = selectionPattern.Current.GetSelection();
206215
if (currentSelection.Length > 0)
207216
{
208217
try
209218
{
210-
UpdateSelectedItem(currentSelection[0]);
219+
UpdateSelectedItem(currentSelection[0], hasVerticalScrollBar);
211220
}
212221
catch (Exception ex)
213222
{
@@ -221,98 +230,9 @@ void UpdateSelectedItem()
221230
}
222231
}
223232

224-
//// TODO: This should be exposed as an event and popup resize should be elsewhere
225-
//// Runs on an automation event thread
226-
//void PopupListStructureChangedHandler(object sender, StructureChangedEventArgs e)
227-
//{
228-
// Debug.Print($">>>> PopupListWatcher.PopupListStructureChangedHandler ({e.StructureChangeType}) on thread {Thread.CurrentThread.ManagedThreadId}");
229-
// // Debug.WriteLine($">>> PopupList structure changed - {e.StructureChangeType}");
230-
// // CONSIDER: Others too?
231-
// if (e.StructureChangeType == StructureChangeType.ChildAdded)
232-
// {
233-
// var functionList = sender as AutomationElement;
234-
235-
// //var listRect = (Rect)functionList.GetCurrentPropertyValue(AutomationElement.BoundingRectangleProperty);
236-
237-
// var listElement = functionList.FindFirst(TreeScope.Children, Condition.TrueCondition);
238-
// if (listElement != null)
239-
// {
240-
// // Debug.Print($">>>> PopupListWatcher.PopupListStructureChangedHandler Post - Children Found !!!");
241-
242-
// _popupListList = listElement;
243-
244-
// // TODO: Move this code!
245-
// // TestMoveWindow(_popupListList, (int)listRect.X, (int)listRect.Y);
246-
// // TestMoveWindow(functionList, 0, 0);
247-
248-
// // If the _popupListList automation element is no plonger valid then we seem to get a InvalidPattern exception here.
249-
// var selPat = _popupListList.GetCurrentPattern(SelectionPattern.Pattern) as SelectionPattern;
250-
// Debug.Assert(selPat != null);
251-
252-
// // CONSIDER: Send might be a bit disruptive here / might not be necessary...
253-
// // _syncContextAuto.Send( _ =>
254-
// //{
255-
// try
256-
// {
257-
// Debug.Print("POPUPLISTWATCHER WINDOW SELECTION EVENT HANDLER ADDED");
258-
// Automation.AddAutomationEventHandler(
259-
// SelectionItemPattern.ElementSelectedEvent, _popupListList, TreeScope.Descendants /* was .Children */, PopupListElementSelectedHandler);
260-
// }
261-
// catch (Exception ex)
262-
// {
263-
// Debug.Print("Error during AddAutomationEventHandler! " + ex);
264-
// }
265-
// //}, null);
266-
267-
// // Update the current selection, if any
268-
// var curSel = selPat.Current.GetSelection();
269-
// if (curSel.Length > 0)
270-
// {
271-
// try
272-
// {
273-
// UpdateSelectedItem(curSel[0]);
274-
// }
275-
// catch (Exception ex)
276-
// {
277-
// Debug.Print("Error during UpdateSelected! " + ex);
278-
// }
279-
// }
280-
// }
281-
// else
282-
// {
283-
// Debug.Print($">>>> PopupListWatcher.PopupListStructureChangedHandler Post - No Children Found ??? ");
284-
// Debug.WriteLine("ERROR!!! Structure changed but no children anymore.");
285-
// }
286-
// }
287-
// else if (e.StructureChangeType == StructureChangeType.ChildRemoved)
288-
// {
289-
// if (_popupListList != null)
290-
// {
291-
// var selPat = _popupListList.GetCurrentPattern(SelectionPattern.Pattern) as SelectionPattern;
292-
// Debug.Assert(selPat != null);
293-
// // CONSIDER: Send might be a bit disruptive here / might not be necessary...
294-
// //_syncContextAuto.Send( _ =>
295-
// //{
296-
// try
297-
// {
298-
// Debug.Print("POPUPLISTWATCHER WINDOW SELECTION EVENT HANDLER REMOVED");
299-
// Automation.RemoveAutomationEventHandler(SelectionItemPattern.ElementSelectedEvent, _popupListList, PopupListElementSelectedHandler);
300-
// }
301-
// catch (Exception ex)
302-
// {
303-
// Debug.Print("Error during RemoveAutomationEventHandler! " + ex);
304-
// }
305-
// //}, null);
306-
// _popupListList = null;
307-
// }
308-
// _selectedItem = null;
309-
// OnSelectedItemChanged();
310-
// }
311-
//}
312-
313233
// Can run on our automation thread or on any automation event thread (which is also allowed to read properties)
314234
// But might fail, if the newSelectedItem is already gone by the time we run...
315-
void UpdateSelectedItem(AutomationElement newSelectedItem)
235+
void UpdateSelectedItem(AutomationElement newSelectedItem, bool hasVerticalScrollBar)
316236
{
317237
// Debug.Print($"POPUPLISTWATCHER WINDOW CURRENT SELECTION {newSelectedItem}");
318238

@@ -353,13 +273,14 @@ void UpdateSelectedItem(AutomationElement newSelectedItem)
353273
SelectedItemBounds = selectedItemBounds;
354274
// Debug.Print($"SelectedItemBounds: {SelectedItemBounds}");
355275
}
276+
HasVerticalScrollBar = hasVerticalScrollBar;
356277
OnSelectedItemChanged();
357278
}
358279

359280
// Raises the event on the automation thread (but the SyncContext.Post here is redundant)
360281
void OnSelectedItemChanged()
361282
{
362-
Logger.WindowWatcher.Verbose($"PopupList SelectedItemChanged {SelectedItemText}");
283+
Logger.WindowWatcher.Verbose($"PopupList SelectedItemChanged {SelectedItemText} ({(HasVerticalScrollBar ? "Scroll" : "NoScroll")})");
363284
_syncContextAuto.Post(_ => SelectedItemChanged(this, EventArgs.Empty), null);
364285
}
365286

Source/ExcelDna.IntelliSense/UIMonitor/UIMonitor.cs

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ public class FunctionList : FormulaEdit // CONSIDER: I'm not sure the hierarchy
6161
public IntPtr FunctionListWindow;
6262
public string SelectedItemText;
6363
public Rect SelectedItemBounds;
64+
public bool HasScrollBar;
6465

6566
public override FormulaEdit WithFormulaEditWindow(IntPtr newFormulaEditWindow)
6667
{
@@ -73,7 +74,8 @@ public override FormulaEdit WithFormulaEditWindow(IntPtr newFormulaEditWindow)
7374

7475
FunctionListWindow = this.FunctionListWindow,
7576
SelectedItemText = this.SelectedItemText,
76-
SelectedItemBounds = this.SelectedItemBounds
77+
SelectedItemBounds = this.SelectedItemBounds,
78+
HasScrollBar = this.HasScrollBar
7779
};
7880
}
7981

@@ -88,7 +90,8 @@ public FunctionList WithFunctionListWindow(IntPtr newFunctionListWindow)
8890

8991
FunctionListWindow = newFunctionListWindow,
9092
SelectedItemText = this.SelectedItemText,
91-
SelectedItemBounds = this.SelectedItemBounds
93+
SelectedItemBounds = this.SelectedItemBounds,
94+
HasScrollBar = this.HasScrollBar
9295
};
9396
}
9497

@@ -103,7 +106,8 @@ public override FormulaEdit WithFormulaPrefix(string newFormulaPrefix)
103106

104107
FunctionListWindow = this.FunctionListWindow,
105108
SelectedItemText = this.SelectedItemText,
106-
SelectedItemBounds = this.SelectedItemBounds
109+
SelectedItemBounds = this.SelectedItemBounds,
110+
HasScrollBar = this.HasScrollBar
107111
};
108112
}
109113

@@ -118,11 +122,12 @@ public override FormulaEdit WithBounds(Rect newEditWindowBounds, Rect newExcelTo
118122

119123
FunctionListWindow = this.FunctionListWindow,
120124
SelectedItemText = this.SelectedItemText,
121-
SelectedItemBounds = this.SelectedItemBounds
125+
SelectedItemBounds = this.SelectedItemBounds,
126+
HasScrollBar = this.HasScrollBar
122127
};
123128
}
124129

125-
public virtual FunctionList WithSelectedItem(string selectedItemText, Rect selectedItemBounds)
130+
public virtual FunctionList WithSelectedItem(string selectedItemText, Rect selectedItemBounds, bool hasScrollBar)
126131
{
127132
return new FunctionList
128133
{
@@ -133,7 +138,8 @@ public virtual FunctionList WithSelectedItem(string selectedItemText, Rect selec
133138

134139
FunctionListWindow = this.FunctionListWindow,
135140
SelectedItemText = selectedItemText,
136-
SelectedItemBounds = selectedItemBounds
141+
SelectedItemBounds = selectedItemBounds,
142+
HasScrollBar = hasScrollBar
137143
};
138144
}
139145

@@ -346,7 +352,8 @@ static IEnumerable<UIStateUpdate> GetUpdates(FunctionList oldState, FunctionList
346352
oldState = (FunctionList)tempState;
347353
}
348354
if (oldState.SelectedItemText != newState.SelectedItemText ||
349-
oldState.SelectedItemBounds != newState.SelectedItemBounds)
355+
oldState.SelectedItemBounds != newState.SelectedItemBounds ||
356+
oldState.HasScrollBar != newState.HasScrollBar)
350357
{
351358
yield return new UIStateUpdate(oldState, newState, UIStateUpdate.UpdateType.FunctionListSelectedItemChange);
352359
}
@@ -483,7 +490,9 @@ void _popupListWatcher_SelectedItemChanged(object sender, EventArgs args)
483490

484491
if (CurrentState is UIState.FunctionList && _popupListWatcher.IsVisible)
485492
{
486-
var newState = ((UIState.FunctionList)CurrentState).WithSelectedItem(_popupListWatcher.SelectedItemText, _popupListWatcher.SelectedItemBounds);
493+
var newState = ((UIState.FunctionList)CurrentState).WithSelectedItem(_popupListWatcher.SelectedItemText,
494+
_popupListWatcher.SelectedItemBounds,
495+
_popupListWatcher.HasVerticalScrollBar);
487496
OnStateChanged(newState);
488497
}
489498
else
@@ -524,6 +533,7 @@ UIState ReadCurrentState()
524533
FunctionListWindow = _popupListWatcher.PopupListHandle,
525534
SelectedItemText = _popupListWatcher.SelectedItemText,
526535
SelectedItemBounds = _popupListWatcher.SelectedItemBounds,
536+
HasScrollBar = _popupListWatcher.HasVerticalScrollBar,
527537
EditWindowBounds = _formulaEditWatcher.EditWindowBounds,
528538
FormulaPrefix = _formulaEditWatcher.CurrentPrefix ?? "", // TODO: Deal with nulls here... (we're not in FormulaEdit state anymore)
529539
};

0 commit comments

Comments
 (0)