Skip to content

Commit f8272c6

Browse files
committed
Add initial focus change events - may be a bit unstable ATM.
1 parent f9a115d commit f8272c6

File tree

8 files changed

+136
-16
lines changed

8 files changed

+136
-16
lines changed

RetailCoder.VBE/App.cs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ public App(IVBE vbe,
6464
_checkVersionCommand = checkVersionCommand;
6565

6666
VBEEvents.SelectionChanged += _vbe_SelectionChanged;
67-
VBEEvents.ForgroundWindowChanged += _vbe_ForegroundWindowChanged;
67+
VBEEvents.WindowFocusChange += _vbe_FocusChanged;
6868

6969
_configService.SettingsChanged += _configService_SettingsChanged;
7070
_parser.State.StateChanged += Parser_StateChanged;
@@ -90,9 +90,12 @@ private void _vbe_SelectionChanged(object sender, SelectionChangedEventArgs e)
9090
RefreshSelection(e.CodePane);
9191
}
9292

93-
private void _vbe_ForegroundWindowChanged(object sender, WindowChangedEventArgs e)
93+
private void _vbe_FocusChanged(object sender, WindowChangedEventArgs e)
9494
{
95-
RefreshSelection(e.Window);
95+
if (e.EventType == WindowChangedEventArgs.FocusType.GotFocus)
96+
{
97+
RefreshSelection(e.Window);
98+
}
9699
}
97100

98101
private ParserState _lastStatus;
@@ -356,7 +359,7 @@ public void Dispose()
356359
}
357360

358361
VBEEvents.SelectionChanged += _vbe_SelectionChanged;
359-
VBEEvents.ForgroundWindowChanged += _vbe_ForegroundWindowChanged;
362+
VBEEvents.WindowFocusChange += _vbe_FocusChanged;
360363

361364
if (_configService != null)
362365
{

Rubberduck.VBEEditor/Events/VBEEvents.cs

Lines changed: 43 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using System.Linq;
55
using System.Text;
66
using Rubberduck.VBEditor.SafeComWrappers.Abstract;
7+
using Rubberduck.VBEditor.SafeComWrappers.MSForms;
78
using Rubberduck.VBEditor.WindowsApi;
89

910
namespace Rubberduck.VBEditor.Events
@@ -13,19 +14,22 @@ public static class VBEEvents
1314
private static User32.WinEventProc _eventProc;
1415
private static IntPtr _eventHandle;
1516
private static IVBE _vbe;
16-
17+
1718
public struct WindowInfo
1819
{
1920
private readonly IntPtr _handle;
2021
private readonly IWindow _window;
22+
private readonly IWindowEventProvider _subclass;
2123

2224
public IntPtr Hwnd { get { return _handle; } }
2325
public IWindow Window { get { return _window; } }
26+
internal IWindowEventProvider Subclass { get { return _subclass; } }
2427

25-
public WindowInfo(IntPtr handle, IWindow window)
28+
internal WindowInfo(IntPtr handle, IWindow window, IWindowEventProvider source)
2629
{
2730
_handle = handle;
28-
_window = window;
31+
_window = window;
32+
_subclass = source;
2933
}
3034
}
3135

@@ -48,7 +52,15 @@ public static void HookEvents(IVBE vbe)
4852

4953
public static void UnhookEvents()
5054
{
51-
User32.UnhookWinEvent(_eventHandle);
55+
lock (ThreadLock)
56+
{
57+
User32.UnhookWinEvent(_eventHandle);
58+
foreach (var info in TrackedWindows.Values)
59+
{
60+
info.Subclass.FocusChange -= FocusDispatcher;
61+
info.Subclass.Dispose();
62+
}
63+
}
5264
}
5365

5466
public static void VbeEventCallback(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild,
@@ -80,7 +92,9 @@ private static void AttachWindow(IntPtr hwnd)
8092
Debug.Assert(!TrackedWindows.ContainsKey(hwnd));
8193
var window = GetWindowFromHwnd(hwnd);
8294
if (window == null) return;
83-
var info = new WindowInfo(hwnd, window);
95+
var source = window.Type == WindowKind.CodeWindow ? new CodePaneSubclass(hwnd) as IWindowEventProvider: new DesignerWindowSubclass(hwnd);
96+
var info = new WindowInfo(hwnd, window, source);
97+
source.FocusChange += FocusDispatcher;
8498
TrackedWindows.Add(hwnd, info);
8599
}
86100
}
@@ -90,10 +104,30 @@ private static void DetachWindow(IntPtr hwnd)
90104
lock (ThreadLock)
91105
{
92106
Debug.Assert(TrackedWindows.ContainsKey(hwnd));
107+
var info = TrackedWindows[hwnd];
108+
info.Subclass.FocusChange -= FocusDispatcher;
109+
info.Subclass.Dispose();
93110
TrackedWindows.Remove(hwnd);
94111
}
95112
}
96113

114+
private static void FocusDispatcher(object sender, WindowChangedEventArgs eventArgs)
115+
{
116+
OnWindowFocusChange(sender, eventArgs);
117+
}
118+
119+
public static WindowInfo? GetWindowInfoFromHwnd(IntPtr hwnd)
120+
{
121+
lock (ThreadLock)
122+
{
123+
if (!TrackedWindows.ContainsKey(hwnd))
124+
{
125+
return null;
126+
}
127+
return TrackedWindows[hwnd];
128+
}
129+
}
130+
97131
public static event EventHandler<SelectionChangedEventArgs> SelectionChanged;
98132
private static void OnSelectionChanged(IntPtr hwnd)
99133
{
@@ -104,13 +138,12 @@ private static void OnSelectionChanged(IntPtr hwnd)
104138
}
105139
}
106140

107-
//Pending location of a suitable event - might need a subclass here instead.
108-
public static event EventHandler<WindowChangedEventArgs> ForgroundWindowChanged;
109-
private static void OnForgroundWindowChanged(WindowInfo info)
141+
public static event EventHandler<WindowChangedEventArgs> WindowFocusChange;
142+
private static void OnWindowFocusChange(object sender, WindowChangedEventArgs eventArgs)
110143
{
111-
if (ForgroundWindowChanged != null)
144+
if (WindowFocusChange != null)
112145
{
113-
ForgroundWindowChanged.Invoke(_vbe, new WindowChangedEventArgs(info.Hwnd, info.Window));
146+
WindowFocusChange.Invoke(sender, eventArgs);
114147
}
115148
}
116149

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,25 @@
11
using System;
22
using Rubberduck.VBEditor.SafeComWrappers.Abstract;
3-
using Rubberduck.VBEditor.SafeComWrappers.MSForms;
43

54
namespace Rubberduck.VBEditor.Events
65
{
76
public class WindowChangedEventArgs : EventArgs
87
{
8+
public enum FocusType
9+
{
10+
GotFocus,
11+
LostFocus
12+
}
13+
914
public IntPtr Hwnd { get; private set; }
1015
public IWindow Window { get; private set; }
16+
public FocusType EventType { get; private set; }
1117

12-
public WindowChangedEventArgs(IntPtr hwnd, IWindow window)
18+
public WindowChangedEventArgs(IntPtr hwnd, IWindow window, FocusType type)
1319
{
1420
Hwnd = hwnd;
1521
Window = window;
22+
EventType = type;
1623
}
1724
}
1825
}

Rubberduck.VBEEditor/Rubberduck.VBEditor.csproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,10 @@
227227
<Compile Include="SafeComWrappers\SafeComWrapper.cs" />
228228
<Compile Include="SafeComWrappers\VBA\VBE.cs" />
229229
<Compile Include="WindowsApi\ChildWindowFinder.cs" />
230+
<Compile Include="WindowsApi\CodePaneSubclass.cs" />
231+
<Compile Include="WindowsApi\DesignerWindowSubclass.cs" />
232+
<Compile Include="WindowsApi\FocusSource.cs" />
233+
<Compile Include="WindowsApi\IWindowEventProvider.cs" />
230234
<Compile Include="WindowsApi\NativeMethods.cs" />
231235
<Compile Include="QualifiedMemberName.cs" />
232236
<Compile Include="QualifiedModuleName.cs" />
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
using System;
2+
3+
namespace Rubberduck.VBEditor.WindowsApi
4+
{
5+
internal class CodePaneSubclass : FocusSource
6+
{
7+
//Stub for code pane replacement. :-)
8+
internal CodePaneSubclass(IntPtr hwnd) : base(hwnd) { }
9+
}
10+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
using System;
2+
3+
namespace Rubberduck.VBEditor.WindowsApi
4+
{
5+
internal class DesignerWindowSubclass : FocusSource
6+
{
7+
//Stub for designer window replacement. :-)
8+
internal DesignerWindowSubclass(IntPtr hwnd) : base(hwnd) { }
9+
}
10+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
using System;
2+
using Rubberduck.Common.WinAPI;
3+
using Rubberduck.VBEditor.Events;
4+
5+
namespace Rubberduck.VBEditor.WindowsApi
6+
{
7+
internal abstract class FocusSource : SubclassingWindow, IWindowEventProvider
8+
{
9+
protected FocusSource(IntPtr hwnd) : base(hwnd, hwnd) { }
10+
11+
public event EventHandler<WindowChangedEventArgs> FocusChange;
12+
private void OnFocusChange(WindowChangedEventArgs.FocusType type)
13+
{
14+
if (FocusChange != null)
15+
{
16+
var window = VBEEvents.GetWindowInfoFromHwnd(Hwnd);
17+
if (window == null)
18+
{
19+
return;
20+
}
21+
FocusChange.Invoke(this, new WindowChangedEventArgs(window.Value.Hwnd, window.Value.Window, type));
22+
}
23+
}
24+
25+
public override int SubClassProc(IntPtr hWnd, IntPtr msg, IntPtr wParam, IntPtr lParam, IntPtr uIdSubclass, IntPtr dwRefData)
26+
{
27+
switch ((uint)msg)
28+
{
29+
case (uint)WM.SETFOCUS:
30+
OnFocusChange(WindowChangedEventArgs.FocusType.GotFocus);
31+
break;
32+
case (uint)WM.KILLFOCUS:
33+
OnFocusChange(WindowChangedEventArgs.FocusType.LostFocus);
34+
break;
35+
}
36+
return base.SubClassProc(hWnd, msg, wParam, lParam, uIdSubclass, dwRefData);
37+
}
38+
}
39+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using System.Threading.Tasks;
6+
using Rubberduck.VBEditor.Events;
7+
8+
namespace Rubberduck.VBEditor.WindowsApi
9+
{
10+
public interface IWindowEventProvider : IDisposable
11+
{
12+
event EventHandler<WindowChangedEventArgs> FocusChange;
13+
}
14+
}

0 commit comments

Comments
 (0)