source: trunk/ab5.0/ablib/src/Classes/ActiveBasic/Windows/UI/Control.ab@ 564

Last change on this file since 564 was 564, checked in by イグトランス (egtra), 16 years ago

MouseLeave, MouseHoverが動作するようにした。

File size: 14.0 KB
Line 
1'Classes/ActiveBasic/Windows/UI/Control.ab
2
3#require <Classes/ActiveBasic/Windows/UI/EventArgs.ab>
4
5Namespace ActiveBasic
6Namespace Windows
7Namespace UI
8
9Namespace Detail
10 TypeDef PTrackMouseEvent = *Function(ByRef tme As TRACKMOUSEEVENT) As BOOL
11End Namespace
12
13Class Control
14 Inherits WindowHandle
15Public
16 /*!
17 @brief Destroyイベントよりも後に呼ばれるDestroyイベントデリゲート
18 @date 2008/07/16
19 */
20 finalDestroy As ActiveBasic.Windows.UI.Handler
21
22 Sub Control()
23 End Sub
24
25 Virtual Sub ~Control()
26 End Sub
27
28 Function Handle() As HWND
29 Handle = hwnd
30 End Function
31
32 Static Function FromHWnd(hwnd As HWND) As Control
33 FromHWnd = Nothing
34 If _System_IsWindow(hwnd) Then
35 FromHWnd = FromHWndCore(hwnd)
36 End If
37 End Function
38
39Private
40 Static Function FromHWndCore(hwnd As HWND) As Control
41 Dim gchValue = GetProp(hwnd, PropertyInstance As ULONG_PTR As PCTSTR) As ULONG_PTR
42 If gchValue <> 0 Then
43 Dim gch = System.Runtime.InteropServices.GCHandle.FromIntPtr(gchValue)
44 FromHWndCore = gch.Target As Control
45 Exit Function
46 End If
47 End Function
48
49'--------------------------------
50' ウィンドウ作成
51' Function Create(
52' parent As HWND,
53' rect As RECT,
54' name As String,
55' style As DWord,
56' exStyle = 0 As DWord,
57' menu = 0 As HMENU) As HWND
58
59Public
60 Sub Create(parent = Nothing As Control, style = 0 As DWord, exStyle = 0 As DWord, hmenu = 0 As HMENU)
61 Dim cs As CREATESTRUCT
62 With cs
63 .dwExStyle = exStyle
64 .lpszClass = (atom As ULONG_PTR) As LPCTSTR
65 .lpszName = 0
66 .style = style Or WS_CHILD Or WS_VISIBLE
67 .x = 0
68 .y = 0
69 .cx = 0
70 .cy = 0
71 If IsNothing(parent) Then
72 .hwndParent = 0
73 Else
74 .hwndParent = parent As HWND
75 .style Or= WS_CHILD
76 End If
77 .hMenu = hmenu
78 .hInstance = hInstance
79 End With
80 GetCreateStruct(cs)
81 createImpl(cs, parent)
82 End Sub
83
84 Sub Create(parent As Control, style As DWord, exStyle As DWord, id As Long)
85 Create(parent, style, exStyle, id As HMENU)
86 End Sub
87Protected
88 Abstract Sub GetCreateStruct(ByRef cs As CREATESTRUCT)
89
90 Sub createImpl(ByRef cs As CREATESTRUCT, parent As Control)
91 Imports System.Runtime.InteropServices
92
93 If hwnd <> 0 Then
94 Throw New System.InvalidOperationException("Window already created.")
95 End If
96
97 Dim gch = GCHandle.Alloc(This)
98 TlsSetValue(tlsIndex, GCHandle.ToIntPtr(gch) As VoidPtr)
99
100 StartWndProc()
101
102 With cs
103 'よそのウィンドウクラスの場合、WndProcFirst内のhwndの代入が行われないため、ここでも行っておく。
104 hwnd = CreateWindowEx(.dwExStyle, .lpszClass, .lpszName, .style,
105 .x, .y, .cx, .cy, .hwndParent, .hMenu, .hInstance, .lpCreateParams)
106 If hwnd = 0 Then
107 Debug
108 ActiveBasic.Windows.ThrowByWindowsError(GetLastError())
109 End If
110
111 If IsNothing(FromHWndCore(hwnd)) <> False Then
112 AssociateHWnd(gch, hwnd)
113 TlsSetValue(tlsIndex, 0)
114 End If
115 End With
116
117 If IsNothing(parent) = False Then
118 RegisterUnassociateHWnd(parent)
119 End If
120 End Sub
121
122'--------------------------------
123' ウィンドウプロシージャ
124'Protected
125Public
126 Virtual Function WndProc(msg As DWord, wp As WPARAM, lp As LPARAM) As LRESULT
127 Dim h = Nothing As MessageHandler
128 Dim b = messageMap.TryGetValue(Hex$(msg), h)
129 If b Then
130 If Not IsNothing(h) Then
131 Dim a = New MessageArgs(hwnd, msg, wp, lp)
132 h(This, a)
133 WndProc = a.LResult
134 Exit Function
135 End If
136 End If
137 WndProc = DefWndProc(msg, wp, lp)
138 End Function
139
140 Virtual Function DefWndProc(msg As DWord, wp As WPARAM, lp As LPARAM) As LRESULT
141 DefWndProc = DefWindowProc(hwnd, msg, wp, lp)
142 End Function
143
144Private
145 Static Function makeKeysFormMsg(e As MessageArgs) As Keys
146 Dim t As DWord
147 t = e.WParam And Keys.KeyCode
148 t Or= (GetKeyState(VK_SHIFT) As Word And &h8000) << 1
149 t Or= (GetKeyState(VK_CONTROL) As Word And &h8000) << 2
150 t Or= (GetKeyState(VK_MENU) As Word And &h8000) << 3
151 makeKeysFormMsg = t As Keys
152 End Function
153
154 Static Function makeMouseEventFromMsg(e As MessageArgs) As MouseArgs
155 Dim wp = e.WParam
156 Dim lp = e.LParam
157 makeMouseEventFromMsg = New MouseArgs(LOWORD(wp) As MouseButtons, 1, GET_X_LPARAM(lp), GET_Y_LPARAM(lp), 0)
158 End Function
159
160 /*!
161 @brief 最初にウィンドウプロシージャが呼ばれるときに実行される関数
162 ここでは、主なメッセージハンドラの登録を行っている。
163 @date 2008/07/11
164 */
165 Sub StartWndProc()
166 AddMessageEvent(WM_ERASEBKGND, AddressOf(OnEraseBackground))
167 Dim md = New MessageHandler(AddressOf(OnMouseDownBase))
168 AddMessageEvent(WM_LBUTTONDOWN, md)
169 AddMessageEvent(WM_RBUTTONDOWN, md)
170 AddMessageEvent(WM_MBUTTONDOWN, md)
171 AddMessageEvent(WM_XBUTTONDOWN, md)
172 Dim mu = New MessageHandler(AddressOf(OnMouseUpBase))
173 AddMessageEvent(WM_LBUTTONUP, mu)
174 AddMessageEvent(WM_RBUTTONUP, mu)
175 AddMessageEvent(WM_MBUTTONUP, mu)
176 AddMessageEvent(WM_XBUTTONUP, mu)
177 Dim mb = New MessageHandler(AddressOf(OnMouseDblClkBase))
178 AddMessageEvent(WM_LBUTTONDBLCLK, mb)
179 AddMessageEvent(WM_RBUTTONDBLCLK, mb)
180 AddMessageEvent(WM_MBUTTONDBLCLK, mb)
181 AddMessageEvent(WM_XBUTTONDBLCLK, mb)
182
183 AddMessageEvent(WM_MOUSEMOVE, AddressOf(OnMouseMoveBase))
184 AddMessageEvent(WM_MOUSELEAVE, AddressOf(OnMouseLeaveBase))
185 AddMessageEvent(WM_MOUSEHOVER, AddressOf(OnMouseHoverBase))
186 AddMessageEvent(WM_PAINT, AddressOf(OnPaintBase))
187 AddMessageEvent(WM_KEYDOWN, AddressOf(OnKeyDownBase))
188 AddMessageEvent(WM_CHAR, AddressOf(OnChar))
189 AddMessageEvent(WM_CREATE, AddressOf(OnCreateBase))
190 AddMessageEvent(WM_SIZE, AddressOf(OnSize))
191 End Sub
192
193 Sub OnEraseBackground(sender As Object, e As MessageArgs)
194 If IsNothing(paintBackground) Then
195 Dim rc = ClientRect
196 FillRect(e.WParam As HDC, rc, (COLOR_3DFACE + 1) As HBRUSH)
197 Else
198 OnPaintBackground(New PaintBackgroundArgs(e.WParam, e.LParam))
199 End If
200 e.LResult = TRUE
201 End Sub
202
203 Sub OnMouseDownBase(sender As Object, e As MessageArgs)
204 OnMouseDown(makeMouseEventFromMsg(e))
205 End Sub
206
207 Sub OnMouseUpBase(sender As Object, e As MessageArgs)
208 Dim me = makeMouseEventFromMsg(e)
209 If doubleClickFired = False Then
210 OnClick(Args.Empty)
211 OnMouseClick(me)
212 doubleClickFired = False
213 End If
214 OnMouseUp(me)
215 End Sub
216
217 Sub OnMouseDblClkBase(sender As Object, e As MessageArgs)
218 Dim me = makeMouseEventFromMsg(e)
219 doubleClickFired = True
220 OnMouseDown(me)
221 OnDoubleClick(Args.Empty)
222 OnMouseDoubleClick(me)
223 End Sub
224
225 Sub OnMouseMoveBase(sender As Object, e As MessageArgs)
226 Dim me = makeMouseEventFromMsg(e)
227 If mouseEntered Then
228 OnMouseMove(me)
229 Else
230 mouseEntered = True
231 OnMouseEnter(me)
232 trackMouseEvent(TME_LEAVE Or TME_HOVER)
233 End If
234 End Sub
235
236 Sub OnMouseLeaveBase(sender As Object, e As MessageArgs)
237 OnMouseLeave(Args.Empty)
238 mouseEntered = False
239 End Sub
240
241 Sub OnMouseHoverBase(sender As Object, e As MessageArgs)
242 Dim me = makeMouseEventFromMsg(e)
243 OnMouseHover(me)
244 End Sub
245
246 Sub OnPaintBase(sender As Object, e As MessageArgs)
247 Dim ps As PAINTSTRUCT
248 BeginPaint(ps)
249 Try
250 OnPaintDC(New PaintDCArgs(ps.hdc, ps.rcPaint))
251 Finally
252 EndPaint(ps)
253 End Try
254 End Sub
255
256 Sub OnKeyDownBase(sender As Object, e As MessageArgs)
257 OnKeyDown(New KeyArgs(makeKeysFormMsg(e)))
258 End Sub
259
260 Sub OnKeyUpBase(sender As Object, e As MessageArgs)
261 OnKeyUp(New KeyArgs(makeKeysFormMsg(e)))
262 End Sub
263
264 Sub OnChar(sender As Object, e As MessageArgs)
265 OnKeyPress(New KeyPressArgs(e.WParam As Char))
266 End Sub
267
268 Sub OnCreateBase(sender As Object, e As MessageArgs)
269 OnCreate(New CreateArgs(e.LParam As *CREATESTRUCT))
270 End Sub
271
272 Sub OnSize(sender As Object, e As MessageArgs)
273 OnResize(New ResizeArgs(e.WParam, e.LParam))
274 End Sub
275
276 messageMap As System.Collections.Generic.Dictionary<Object /*DWord*/, MessageHandler>
277
278Public
279 /*!
280 @biref メッセージイベントハンドラを登録する。
281 @date 2007/12/04
282 */
283 Sub AddMessageEvent(message As DWord, h As MessageHandler)
284 If Not IsNothing(h) Then
285 If IsNothing(messageMap) Then
286 messageMap = New System.Collections.Generic.Dictionary<Object, MessageHandler>
287 End If
288 Dim msg = Hex$(message)
289 Dim m = Nothing As MessageHandler
290 If messageMap.TryGetValue(msg, m) Then
291 messageMap.Item[msg] = m + h
292 Else
293 messageMap.Item[msg] = h
294 End If
295 End If
296 End Sub
297
298 /*!
299 @biref メッセージイベントハンドラ登録を解除する。
300 @date 2007/12/04
301 */
302 Sub RemoveMessageEvent(message As DWord, a As MessageHandler)
303 If Not IsNothing(a) Then
304 If Not IsNothing(messageMap) Then
305 Dim msg = Hex$(message)
306 Dim m = messageMap.Item[msg]
307 If Not IsNothing(m) Then
308 messageMap.Item[msg] = m - a
309 End If
310 End If
311 End If
312 End Sub
313
314'--------------------------------
315' ウィンドウメッセージ処理
316
317
318'--------
319'イベント
320
321#include "ControlEvent.sbp"
322
323'--------------------------------
324' インスタンスメンバ変数
325Private
326 /*!
327 @brief マウスカーソルがクライアント領域に入っているかどうかのフラグ
328 外から中に入ってきたときにMouseEnterイベントを発生させるために用意している。
329 */
330 mouseEntered As Boolean
331 /*!
332 @brief ダブルクリックが起こったかどうかのフラグ
333 Click/MouseClickイベントのために用意している。
334 @date 2008/07/12
335 */
336 doubleClickFired As Boolean
337
338'--------------------------------
339' 初期ウィンドウクラス
340Private
341 Static Function WndProcFirst(hwnd As HWND, msg As DWord, wp As WPARAM, lp As LPARAM) As LRESULT
342 Imports System.Runtime.InteropServices
343
344 Dim rThis = FromHWndCore(hwnd)
345 If IsNothing(rThis) Then
346 Dim gchValue = TlsGetValue(tlsIndex) As LONG_PTR
347 TlsSetValue(tlsIndex, 0)
348 If gchValue = 0 Then
349 Goto *InstanceIsNotFound
350 End If
351 Dim gch = GCHandle.FromIntPtr(gchValue)
352 rThis = gch.Target As Control
353 ' ウィンドウが作られて最初にWndProcFirstが呼ばれたとき
354
355 AssociateHWnd(gch, hwnd)
356 End If
357 If msg = WM_NCDESTROY Then
358 rThis.UnassociateHWnd()
359 End If
360 If msg = WM_DESTROY And IsNothing(rThis.finalDestroy) = False Then
361 Dim f = rThis.finalDestroy
362 f(rThis, Args.Empty)
363' finalDestroy(This, Args.Empty)
364 End If
365 WndProcFirst = rThis.WndProc(msg, wp, lp)
366 Exit Function
367
368 *InstanceIsNotFound
369 Dim err = "ActiveBasic.Windows.UI.Control.WndProcFirst: The attached instance is not found. msg = &h" _
370 + Hex$(msg) + Ex"\r\n"
371 OutputDebugString(ToTCStr(err))
372 WndProcFirst = DefWindowProc(hwnd, msg, wp, lp)
373 End Function
374
375 /*!
376 @brief Controlインスタンスとウィンドウハンドルを結び付ける。
377 @param[in] 結び付けられるControlインスタンスを格納したGCHandle
378 @param[in] hwnd 結び付けるウィンドウハンドル
379 @date 2008/07/16
380 これを実行することで、UnassociateHWndされるまでControlがGC対象にならなくなる。
381 */
382 Static Sub AssociateHWnd(gch As System.Runtime.InteropServices.GCHandle, hwnd As HWND)
383 Imports System.Runtime.InteropServices
384 Dim rThis = gch.Target As Control
385 If IsNothing(rThis) Then
386 Exit Sub
387 End If
388 rThis.hwnd = hwnd
389 rThis.Prop[PropertyInstance] = GCHandle.ToIntPtr(gch) As HANDLE
390 End Sub
391
392 /*!
393 @brief オーナーの破棄時にこのControlインスタンスとウィンドウハンドルの結び付けを解除するよう登録する。
394 @param[in] owner 結び付けの解除を連動させるControl
395 @date 2008/07/16
396 ownerのFinalDestoryでこのControlとHWNDとの結び付けを解除する。
397 */
398 Sub RegisterUnassociateHWnd(owner As Control)
399 If IsNothing(owner) = False Then
400 Dim e = New Handler(AddressOf(UnassociateHWndOnEvent))
401 If IsNothing(finalDestroy) Then
402 owner.finalDestroy = e
403 Else
404 owner.finalDestroy += e
405 End If
406 End If
407 End Sub
408
409 Sub UnassociateHWndOnEvent(sender As Object, e As Args)
410 UnassociateHWnd()
411 End Sub
412
413 Sub UnassociateHWnd()
414 Imports System.Runtime.InteropServices
415 Dim gchValue = Prop(PropertyInstance) As ULONG_PTR
416 If gchValue <> 0 Then
417 GCHandle.FromIntPtr(gchValue).Free()
418 End If
419 End Sub
420
421' Static Const WM_CONTROL_INVOKE = WM_USER + 0 As DWord
422' Static Const WM_CONTROL_BEGININVOKE = WM_USER + 1 As DWord
423
424'--------------------------------
425' その他の補助関数
426Private
427 Function trackMouseEvent(flags As DWord) As BOOL
428 If pTrackMouseEvent <> 0 Then
429 Dim tme As TRACKMOUSEEVENT
430 With tme
431 .cbSize = Len(tme)
432 .dwFlags = flags
433 .hwndTrack = hwnd
434 .dwHoverTime = HOVER_DEFAULT
435 End With
436 trackMouseEvent = pTrackMouseEvent(tme)
437 End If
438 End Function
439
440'--------------------------------
441' 初期化終了関連(特にウィンドウクラス)
442Private
443 'ウィンドウ作成時にウィンドウプロシージャへThisを伝えるためのもの
444 Static tlsIndex As DWord
445
446 Static hInstance As HINSTANCE
447 Static atom As ATOM
448 Static hmodComctl As HMODULE
449 Static pTrackMouseEvent As Detail.PTrackMouseEvent
450
451 Static Const WindowClassName = "ActiveBasic.Windows.UI.Control"
452 Static Const PropertyInstance = 0 As ATOM
453Public
454 Static Sub Initialize(hinst As HINSTANCE)
455 tlsIndex = TlsAlloc()
456 hInstance = hinst
457 hmodComctl = LoadLibrary("comctl32.dll")
458 pTrackMouseEvent = GetProcAddress(hmodComctl, ToMBStr("_TrackMouseEvent")) As Detail.PTrackMouseEvent
459
460 Dim PropertyInstanceString = WindowClassName + " " + Hex$(GetCurrentProcessId())
461 PropertyInstance = GlobalAddAtom(ToTCStr(PropertyInstanceString))
462
463 Dim wcx As WNDCLASSEX
464 With wcx
465 .cbSize = Len (wcx)
466 .style = CS_HREDRAW Or CS_VREDRAW Or CS_DBLCLKS
467 .lpfnWndProc = AddressOf (WndProcFirst)
468 .cbClsExtra = 0
469 .cbWndExtra = 0
470 .hInstance = hinst
471 .hIcon = 0
472 .hCursor = LoadImage(0, MAKEINTRESOURCE(IDC_ARROW), IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE Or LR_SHARED) As HCURSOR
473 .hbrBackground = 0
474 .lpszMenuName = 0
475 .lpszClassName = ToTCStr(WindowClassName)
476 .hIconSm = 0
477 End With
478 atom = RegisterClassEx(wcx)
479 If atom = 0 Then
480 Dim buf[1023] As TCHAR
481 wsprintf(buf, Ex"ActiveBasic.Windows.UI.Control.Control: RegisterClasseEx failed. Error code: &h%08X\r\n", GetLastError())
482 OutputDebugString(buf)
483 Debug
484 ExitThread(0)
485 End If
486 End Sub
487
488 Static Sub Uninitialize()
489 If atom <> 0 Then
490 UnregisterClass(atom As ULONG_PTR As PCSTR, hInstance)
491 End If
492 If tlsIndex <> 0 And tlsIndex <> &hffffffff Then
493 TlsFree(tlsIndex)
494 End If
495 If hmodComctl <> 0 Then
496 FreeLibrary(hmodComctl)
497 End If
498 If PropertyInstance <> 0 Then
499 GlobalDeleteAtom(PropertyInstance)
500 End If
501 End Sub
502
503End Class
504
505End Namespace 'UI
506End Namespace 'Widnows
507End Namespace 'ActiveBasic
Note: See TracBrowser for help on using the repository browser.