Duilib 窗口流程

2024-05-31 05:08
文章标签 流程 窗口 duilib

本文主要是介绍Duilib 窗口流程,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

从GameDemo.cpp看起

 

1回顾通常的sdk窗口程序流程:注册窗口-创建窗口-显示窗口-启动消息循环

 

1.1注册窗口类

Duilib中最平凡的真实窗口类是:CWindowWnd,关于窗口注册提供了两个函数,严格的说应该是几个:

RegisterWindowClass()

RegisterSuperclass()

GetWindowClassName()

GetSuperClassName()

GetClassStyle()

在我的理解中,后面两个虚函数的意义应该是:上面这些接口分两组,一组是用于正常注册使用,一组用于扩展。

使用的时候用自定义的窗口对象从CWindowWnd继承下来,然后定制自己需要的window class

1.2创建窗口

    CGameFrameWnd* pFrame = new CGameFrameWnd();if( pFrame == NULL ) return 0;pFrame->Create(NULL, _T(""), UI_WNDSTYLE_FRAME, 0L, 0, 0, 1024, 738);
.csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }

CWindowWnd带有mfc CWnd类似的Create接口用于创建窗口,同事注册窗口类也通过虚函数的方式延后到子类实现,super机制(如果有super优先注册)也带进来。

    if( GetSuperClassName() != NULL && !RegisterSuperclass() ) return NULL;if( GetSuperClassName() == NULL && !RegisterWindowClass() ) return NULL;
.csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }

1.3显示窗口

和sdk是一样的

::ShowWindow(*pFrame, SW_SHOWMAXIMIZED);

.csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; } 1.4消息循环

CPaintManagerUI::MessageLoop();
.csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }

1.5消息回调函数

在窗口类注册的时候应该要注册窗口回调函数指针,Duilib中默认是在CWindowWnd::RegisterWindow注册:

bool CWindowWnd::RegisterWindowClass()
{WNDCLASS wc = { 0 };wc.style = GetClassStyle();wc.cbClsExtra = 0;wc.cbWndExtra = 0;wc.hIcon = NULL;wc.lpfnWndProc = CWindowWnd::__WndProc;

__WndProc是CWindowWnd的一个静态成员,这个函数值得看一下:

LRESULT CALLBACK CWindowWnd::__WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{CWindowWnd* pThis = NULL;if( uMsg == WM_NCCREATE ) {LPCREATESTRUCT lpcs = reinterpret_cast<LPCREATESTRUCT>(lParam);pThis = static_cast<CWindowWnd*>(lpcs->lpCreateParams);pThis->m_hWnd = hWnd;::SetWindowLongPtr(hWnd, GWLP_USERDATA, reinterpret_cast<LPARAM>(pThis));} else {pThis = reinterpret_cast<CWindowWnd*>(::GetWindowLongPtr(hWnd, GWLP_USERDATA));if( uMsg == WM_NCDESTROY && pThis != NULL ) {LRESULT lRes = ::CallWindowProc(pThis->m_OldWndProc, hWnd, uMsg, wParam, lParam);::SetWindowLongPtr(pThis->m_hWnd, GWLP_USERDATA, 0L);if( pThis->m_bSubclassed ) pThis->Unsubclass();pThis->m_hWnd = NULL;pThis->OnFinalMessage(hWnd);return lRes;}}if( pThis != NULL ) {return pThis->HandleMessage(uMsg, wParam, lParam);} else {return ::DefWindowProc(hWnd, uMsg, wParam, lParam);}
}
.csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }

针对WM_NCCREATE这个消息有一个管用技巧,先复习下这个消息:

The WM_NCCREATE message is sent prior to the WM_CREATE message when a window is first created.

我的理解中,这个消息应该是窗口收到的第一个消息。

还有WM_NCDESTROY这个特殊的消息。

当然了,还有这个牛逼的戏法:

::SetWindowLongPtr(hWnd, GWLP_USERDATA, reinterpret_cast<LPARAM>(pThis));

总的来说一句话,两个消息NC create destroy对应的应该有连个函数,但是原本的OnNcCreate响应隐藏在了__WndProc中,还有一个函数OnFinalMessage。this指针藏在SetWindowLongPtr(hWnd, GWLP_USERDATA,,,

好了底层需要注意的就只有这两个函数,其他的消息都应该是抛给子类去处理了。

既然是玩directUi,就重点关注一下WM_NCPAINT消息,也一个也很重要的消息WM_NCHITTEST。

不知道为什么这里用的都是NC系列消息。

NC应该是理解成none client,初步观察Duilib的size拖拉支持是使用NCHITTEST+SIZE消息来实现的。

看NCPAINT消息体里边没有任何代码这很诡异,所以还是看看完整的消息响应函数,是不是漏掉了什么:

    LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam){LRESULT lRes = 0;BOOL bHandled = TRUE;switch( uMsg ) {case WM_CREATE:        lRes = OnCreate(uMsg, wParam, lParam, bHandled); break;case WM_CLOSE:         lRes = OnClose(uMsg, wParam, lParam, bHandled); break;case WM_DESTROY:       lRes = OnDestroy(uMsg, wParam, lParam, bHandled); break;case WM_NCACTIVATE:    lRes = OnNcActivate(uMsg, wParam, lParam, bHandled); break;case WM_NCCALCSIZE:    lRes = OnNcCalcSize(uMsg, wParam, lParam, bHandled); break;case WM_NCPAINT:       lRes = OnNcPaint(uMsg, wParam, lParam, bHandled); break;case WM_NCHITTEST:     lRes = OnNcHitTest(uMsg, wParam, lParam, bHandled); break;case WM_SIZE:          lRes = OnSize(uMsg, wParam, lParam, bHandled); break;case WM_GETMINMAXINFO: lRes = OnGetMinMaxInfo(uMsg, wParam, lParam, bHandled); break;case WM_SYSCOMMAND:    lRes = OnSysCommand(uMsg, wParam, lParam, bHandled); break;default:bHandled = FALSE;}if( bHandled ) return lRes;if( m_pm.MessageHandler(uMsg, wParam, lParam, lRes) ) return lRes;return CWindowWnd::HandleMessage(uMsg, wParam, lParam);}
.csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }

乍看之下这里的消息处理至少分为三层,CWindowWnd派生类本身没有处理的消息将会被送到m_pm中去处理,如果m_pm.MessageHandler返回false消息最后还是CWindowWnd处理。

bool CPaintManagerUI::MessageHandler(UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT& lRes)
{
//#ifdef _DEBUG
//    switch( uMsg ) {
//    case WM_NCPAINT:
//    case WM_NCHITTEST:
//    case WM_SETCURSOR:
//       break;
//    default:
//       DUITRACE(_T("MSG: %-20s (%08ld)"), DUITRACEMSG(uMsg), ::GetTickCount());
//    }
//#endif// Not ready yet?if( m_hWndPaint == NULL ) return false;TNotifyUI* pMsg = NULL;while( pMsg = static_cast<TNotifyUI*>(m_aAsyncNotify.GetAt(0)) ) {m_aAsyncNotify.Remove(0);if( pMsg->pSender != NULL ) {if( pMsg->pSender->OnNotify ) pMsg->pSender->OnNotify(pMsg);}for( int j = 0; j < m_aNotifiers.GetSize(); j++ ) {static_cast<INotifyUI*>(m_aNotifiers[j])->Notify(*pMsg);}delete pMsg;}// Cycle through listenersfor( int i = 0; i < m_aMessageFilters.GetSize(); i++ ) {bool bHandled = false;LRESULT lResult = static_cast<IMessageFilterUI*>(m_aMessageFilters[i])->MessageHandler(uMsg, wParam, lParam, bHandled);if( bHandled ) {lRes = lResult;return true;}}// Custom handling of eventsswitch( uMsg ) {case WM_APP + 1:{for( int i = 0; i < m_aDelayedCleanup.GetSize(); i++ ) delete static_cast<CControlUI*>(m_aDelayedCleanup[i]);m_aDelayedCleanup.Empty();}break;case WM_CLOSE:{// Make sure all matching "closing" events are sentTEventUI event = { 0 };event.ptMouse = m_ptLastMousePos;event.dwTimestamp = ::GetTickCount();if( m_pEventHover != NULL ) {event.Type = UIEVENT_MOUSELEAVE;event.pSender = m_pEventHover;m_pEventHover->Event(event);}if( m_pEventClick != NULL ) {event.Type = UIEVENT_BUTTONUP;event.pSender = m_pEventClick;m_pEventClick->Event(event);}SetFocus(NULL);// Hmmph, the usual Windows tricks to avoid// focus loss...HWND hwndParent = GetWindowOwner(m_hWndPaint);if( hwndParent != NULL ) ::SetFocus(hwndParent);}break;case WM_ERASEBKGND:{// We'll do the painting here...lRes = 1;}return true;case WM_PAINT:{// Should we paint?RECT rcPaint = { 0 };if( !::GetUpdateRect(m_hWndPaint, &rcPaint, FALSE) ) return true;if( m_pRoot == NULL ) {PAINTSTRUCT ps = { 0 };::BeginPaint(m_hWndPaint, &ps);::EndPaint(m_hWndPaint, &ps);return true;}            // Do we need to resize anything?// This is the time where we layout the controls on the form.// We delay this even from the WM_SIZE messages since resizing can be// a very expensize operation.if( m_bUpdateNeeded ) {m_bUpdateNeeded = false;RECT rcClient = { 0 };::GetClientRect(m_hWndPaint, &rcClient);if( !::IsRectEmpty(&rcClient) ) {if( m_pRoot->IsUpdateNeeded() ) {m_pRoot->SetPos(rcClient);if( m_hDcOffscreen != NULL ) ::DeleteDC(m_hDcOffscreen);if( m_hDcBackground != NULL ) ::DeleteDC(m_hDcBackground);if( m_hbmpOffscreen != NULL ) ::DeleteObject(m_hbmpOffscreen);if( m_hbmpBackground != NULL ) ::DeleteObject(m_hbmpBackground);m_hDcOffscreen = NULL;m_hDcBackground = NULL;m_hbmpOffscreen = NULL;m_hbmpBackground = NULL;}else {CControlUI* pControl = NULL;while( pControl = m_pRoot->FindControl(__FindControlFromUpdate, NULL, UIFIND_VISIBLE | UIFIND_ME_FIRST) ) {pControl->SetPos( pControl->GetPos() );}}// We'll want to notify the window when it is first initialized// with the correct layout. The window form would take the time// to submit swipes/animations.if( m_bFirstLayout ) {m_bFirstLayout = false;SendNotify(m_pRoot, DUI_MSGTYPE_WINDOWINIT,  0, 0, false);}}}// Set focus to first control?if( m_bFocusNeeded ) {SetNextTabControl();}//// Render screen//// Prepare offscreen bitmap?if( m_bOffscreenPaint && m_hbmpOffscreen == NULL ){RECT rcClient = { 0 };::GetClientRect(m_hWndPaint, &rcClient);m_hDcOffscreen = ::CreateCompatibleDC(m_hDcPaint);m_hbmpOffscreen = ::CreateCompatibleBitmap(m_hDcPaint, rcClient.right - rcClient.left, rcClient.bottom - rcClient.top); ASSERT(m_hDcOffscreen);ASSERT(m_hbmpOffscreen);}// Begin Windows paintPAINTSTRUCT ps = { 0 };::BeginPaint(m_hWndPaint, &ps);if( m_bOffscreenPaint ){HBITMAP hOldBitmap = (HBITMAP) ::SelectObject(m_hDcOffscreen, m_hbmpOffscreen);int iSaveDC = ::SaveDC(m_hDcOffscreen);if( m_bAlphaBackground ) {if( m_hbmpBackground == NULL ) {RECT rcClient = { 0 };::GetClientRect(m_hWndPaint, &rcClient);m_hDcBackground = ::CreateCompatibleDC(m_hDcPaint);;m_hbmpBackground = ::CreateCompatibleBitmap(m_hDcPaint, rcClient.right - rcClient.left, rcClient.bottom - rcClient.top); ASSERT(m_hDcBackground);ASSERT(m_hbmpBackground);::SelectObject(m_hDcBackground, m_hbmpBackground);::BitBlt(m_hDcBackground, ps.rcPaint.left, ps.rcPaint.top, ps.rcPaint.right - ps.rcPaint.left,ps.rcPaint.bottom - ps.rcPaint.top, ps.hdc, ps.rcPaint.left, ps.rcPaint.top, SRCCOPY);}else::SelectObject(m_hDcBackground, m_hbmpBackground);::BitBlt(m_hDcOffscreen, ps.rcPaint.left, ps.rcPaint.top, ps.rcPaint.right - ps.rcPaint.left,ps.rcPaint.bottom - ps.rcPaint.top, m_hDcBackground, ps.rcPaint.left, ps.rcPaint.top, SRCCOPY);}m_pRoot->DoPaint(m_hDcOffscreen, ps.rcPaint);for( int i = 0; i < m_aPostPaintControls.GetSize(); i++ ) {CControlUI* pPostPaintControl = static_cast<CControlUI*>(m_aPostPaintControls[i]);pPostPaintControl->DoPostPaint(m_hDcOffscreen, ps.rcPaint);}::RestoreDC(m_hDcOffscreen, iSaveDC);::BitBlt(ps.hdc, ps.rcPaint.left, ps.rcPaint.top, ps.rcPaint.right - ps.rcPaint.left,ps.rcPaint.bottom - ps.rcPaint.top, m_hDcOffscreen, ps.rcPaint.left, ps.rcPaint.top, SRCCOPY);::SelectObject(m_hDcOffscreen, hOldBitmap);if( m_bShowUpdateRect ) {HPEN hOldPen = (HPEN)::SelectObject(ps.hdc, m_hUpdateRectPen);::SelectObject(ps.hdc, ::GetStockObject(HOLLOW_BRUSH));::Rectangle(ps.hdc, rcPaint.left, rcPaint.top, rcPaint.right, rcPaint.bottom);::SelectObject(ps.hdc, hOldPen);}}else{// A standard paint jobint iSaveDC = ::SaveDC(ps.hdc);m_pRoot->DoPaint(ps.hdc, ps.rcPaint);::RestoreDC(ps.hdc, iSaveDC);}// All Done!::EndPaint(m_hWndPaint, &ps);}// If any of the painting requested a resize again, we'll need// to invalidate the entire window once more.if( m_bUpdateNeeded ) {::InvalidateRect(m_hWndPaint, NULL, FALSE);}return true;case WM_PRINTCLIENT:{RECT rcClient;::GetClientRect(m_hWndPaint, &rcClient);HDC hDC = (HDC) wParam;int save = ::SaveDC(hDC);m_pRoot->DoPaint(hDC, rcClient);// Check for traversing children. The crux is that WM_PRINT will assume// that the DC is positioned at frame coordinates and will paint the child// control at the wrong position. We'll simulate the entire thing instead.if( (lParam & PRF_CHILDREN) != 0 ) {HWND hWndChild = ::GetWindow(m_hWndPaint, GW_CHILD);while( hWndChild != NULL ) {RECT rcPos = { 0 };::GetWindowRect(hWndChild, &rcPos);::MapWindowPoints(HWND_DESKTOP, m_hWndPaint, reinterpret_cast<LPPOINT>(&rcPos), 2);::SetWindowOrgEx(hDC, -rcPos.left, -rcPos.top, NULL);// NOTE: We use WM_PRINT here rather than the expected WM_PRINTCLIENT//       since the latter will not print the nonclient correctly for//       EDIT controls.::SendMessage(hWndChild, WM_PRINT, wParam, lParam | PRF_NONCLIENT);hWndChild = ::GetWindow(hWndChild, GW_HWNDNEXT);}}::RestoreDC(hDC, save);}break;case WM_GETMINMAXINFO:{LPMINMAXINFO lpMMI = (LPMINMAXINFO) lParam;if( m_szMinWindow.cx > 0 ) lpMMI->ptMinTrackSize.x = m_szMinWindow.cx;if( m_szMinWindow.cy > 0 ) lpMMI->ptMinTrackSize.y = m_szMinWindow.cy;if( m_szMaxWindow.cx > 0 ) lpMMI->ptMaxTrackSize.x = m_szMaxWindow.cx;if( m_szMaxWindow.cy > 0 ) lpMMI->ptMaxTrackSize.y = m_szMaxWindow.cy;}break;case WM_SIZE:{if( m_pFocus != NULL ) {TEventUI event = { 0 };event.Type = UIEVENT_WINDOWSIZE;event.pSender = m_pFocus;event.dwTimestamp = ::GetTickCount();m_pFocus->Event(event);}if( m_pRoot != NULL ) m_pRoot->NeedUpdate();}return true;case WM_TIMER:{for( int i = 0; i < m_aTimers.GetSize(); i++ ) {const TIMERINFO* pTimer = static_cast<TIMERINFO*>(m_aTimers[i]);if( pTimer->hWnd == m_hWndPaint && pTimer->uWinTimer == LOWORD(wParam) && pTimer->bKilled == false) {TEventUI event = { 0 };event.Type = UIEVENT_TIMER;event.pSender = pTimer->pSender;event.wParam = pTimer->nLocalID;event.dwTimestamp = ::GetTickCount();pTimer->pSender->Event(event);break;}}}break;case WM_MOUSEHOVER:{m_bMouseTracking = false;POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };CControlUI* pHover = FindControl(pt);if( pHover == NULL ) break;// Generate mouse hover eventif( m_pEventHover != NULL ) {TEventUI event = { 0 };event.ptMouse = pt;event.Type = UIEVENT_MOUSEHOVER;event.pSender = m_pEventHover;event.dwTimestamp = ::GetTickCount();m_pEventHover->Event(event);}// Create tooltip informationCDuiString sToolTip = pHover->GetToolTip();if( sToolTip.IsEmpty() ) return true;::ZeroMemory(&m_ToolTip, sizeof(TOOLINFO));m_ToolTip.cbSize = sizeof(TOOLINFO);m_ToolTip.uFlags = TTF_IDISHWND;m_ToolTip.hwnd = m_hWndPaint;m_ToolTip.uId = (UINT_PTR) m_hWndPaint;m_ToolTip.hinst = m_hInstance;m_ToolTip.lpszText = const_cast<LPTSTR>( (LPCTSTR) sToolTip );m_ToolTip.rect = pHover->GetPos();if( m_hwndTooltip == NULL ) {m_hwndTooltip = ::CreateWindowEx(0, TOOLTIPS_CLASS, NULL, WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, m_hWndPaint, NULL, m_hInstance, NULL);::SendMessage(m_hwndTooltip, TTM_ADDTOOL, 0, (LPARAM) &m_ToolTip);}::SendMessage(m_hwndTooltip, TTM_SETTOOLINFO, 0, (LPARAM) &m_ToolTip);::SendMessage(m_hwndTooltip, TTM_TRACKACTIVATE, TRUE, (LPARAM) &m_ToolTip);}return true;case WM_MOUSELEAVE:{if( m_hwndTooltip != NULL ) ::SendMessage(m_hwndTooltip, TTM_TRACKACTIVATE, FALSE, (LPARAM) &m_ToolTip);if( m_bMouseTracking ) ::SendMessage(m_hWndPaint, WM_MOUSEMOVE, 0, (LPARAM) -1);m_bMouseTracking = false;}break;case WM_MOUSEMOVE:{// Start tracking this entire window again...if( !m_bMouseTracking ) {TRACKMOUSEEVENT tme = { 0 };tme.cbSize = sizeof(TRACKMOUSEEVENT);tme.dwFlags = TME_HOVER | TME_LEAVE;tme.hwndTrack = m_hWndPaint;tme.dwHoverTime = m_hwndTooltip == NULL ? 400UL : (DWORD) ::SendMessage(m_hwndTooltip, TTM_GETDELAYTIME, TTDT_INITIAL, 0L);_TrackMouseEvent(&tme);m_bMouseTracking = true;}// Generate the appropriate mouse messagesPOINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };m_ptLastMousePos = pt;CControlUI* pNewHover = FindControl(pt);if( pNewHover != NULL && pNewHover->GetManager() != this ) break;TEventUI event = { 0 };event.ptMouse = pt;event.dwTimestamp = ::GetTickCount();if( pNewHover != m_pEventHover && m_pEventHover != NULL ) {event.Type = UIEVENT_MOUSELEAVE;event.pSender = m_pEventHover;m_pEventHover->Event(event);m_pEventHover = NULL;if( m_hwndTooltip != NULL ) ::SendMessage(m_hwndTooltip, TTM_TRACKACTIVATE, FALSE, (LPARAM) &m_ToolTip);}if( pNewHover != m_pEventHover && pNewHover != NULL ) {event.Type = UIEVENT_MOUSEENTER;event.pSender = pNewHover;pNewHover->Event(event);m_pEventHover = pNewHover;}if( m_pEventClick != NULL ) {event.Type = UIEVENT_MOUSEMOVE;event.pSender = m_pEventClick;m_pEventClick->Event(event);}else if( pNewHover != NULL ) {event.Type = UIEVENT_MOUSEMOVE;event.pSender = pNewHover;pNewHover->Event(event);}}break;case WM_LBUTTONDOWN:{// We alway set focus back to our app (this helps// when Win32 child windows are placed on the dialog// and we need to remove them on focus change).::SetFocus(m_hWndPaint);POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };m_ptLastMousePos = pt;CControlUI* pControl = FindControl(pt);if( pControl == NULL ) break;if( pControl->GetManager() != this ) break;m_pEventClick = pControl;pControl->SetFocus();SetCapture();TEventUI event = { 0 };event.Type = UIEVENT_BUTTONDOWN;event.pSender = pControl;event.wParam = wParam;event.lParam = lParam;event.ptMouse = pt;event.wKeyState = (WORD)wParam;event.dwTimestamp = ::GetTickCount();pControl->Event(event);}break;case WM_LBUTTONDBLCLK:{::SetFocus(m_hWndPaint);POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };m_ptLastMousePos = pt;CControlUI* pControl = FindControl(pt);if( pControl == NULL ) break;if( pControl->GetManager() != this ) break;SetCapture();TEventUI event = { 0 };event.Type = UIEVENT_DBLCLICK;event.pSender = pControl;event.ptMouse = pt;event.wKeyState = (WORD)wParam;event.dwTimestamp = ::GetTickCount();pControl->Event(event);m_pEventClick = pControl;}break;case WM_LBUTTONUP:{POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };m_ptLastMousePos = pt;if( m_pEventClick == NULL ) break;ReleaseCapture();TEventUI event = { 0 };event.Type = UIEVENT_BUTTONUP;event.pSender = m_pEventClick;event.wParam = wParam;event.lParam = lParam;event.ptMouse = pt;event.wKeyState = (WORD)wParam;event.dwTimestamp = ::GetTickCount();m_pEventClick->Event(event);m_pEventClick = NULL;}break;case WM_RBUTTONDOWN:{::SetFocus(m_hWndPaint);POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };m_ptLastMousePos = pt;CControlUI* pControl = FindControl(pt);if( pControl == NULL ) break;if( pControl->GetManager() != this ) break;pControl->SetFocus();SetCapture();TEventUI event = { 0 };event.Type = UIEVENT_RBUTTONDOWN;event.pSender = pControl;event.wParam = wParam;event.lParam = lParam;event.ptMouse = pt;event.wKeyState = (WORD)wParam;event.dwTimestamp = ::GetTickCount();pControl->Event(event);m_pEventClick = pControl;}break;case WM_CONTEXTMENU:{POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };::ScreenToClient(m_hWndPaint, &pt);m_ptLastMousePos = pt;if( m_pEventClick == NULL ) break;ReleaseCapture();TEventUI event = { 0 };event.Type = UIEVENT_CONTEXTMENU;event.pSender = m_pEventClick;event.ptMouse = pt;event.wKeyState = (WORD)wParam;event.lParam = (LPARAM)m_pEventClick;event.dwTimestamp = ::GetTickCount();m_pEventClick->Event(event);m_pEventClick = NULL;}break;case WM_MOUSEWHEEL:{POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };::ScreenToClient(m_hWndPaint, &pt);m_ptLastMousePos = pt;CControlUI* pControl = FindControl(pt);if( pControl == NULL ) break;if( pControl->GetManager() != this ) break;int zDelta = (int) (short) HIWORD(wParam);TEventUI event = { 0 };event.Type = UIEVENT_SCROLLWHEEL;event.pSender = pControl;event.wParam = MAKELPARAM(zDelta < 0 ? SB_LINEDOWN : SB_LINEUP, 0);event.lParam = lParam;event.wKeyState = MapKeyState();event.dwTimestamp = ::GetTickCount();pControl->Event(event);// Let's make sure that the scroll item below the cursor is the same as before...::SendMessage(m_hWndPaint, WM_MOUSEMOVE, 0, (LPARAM) MAKELPARAM(m_ptLastMousePos.x, m_ptLastMousePos.y));}break;case WM_CHAR:{if( m_pFocus == NULL ) break;TEventUI event = { 0 };event.Type = UIEVENT_CHAR;event.chKey = (TCHAR)wParam;event.ptMouse = m_ptLastMousePos;event.wKeyState = MapKeyState();event.dwTimestamp = ::GetTickCount();m_pFocus->Event(event);}break;case WM_KEYDOWN:{if( m_pFocus == NULL ) break;TEventUI event = { 0 };event.Type = UIEVENT_KEYDOWN;event.chKey = (TCHAR)wParam;event.ptMouse = m_ptLastMousePos;event.wKeyState = MapKeyState();event.dwTimestamp = ::GetTickCount();m_pFocus->Event(event);m_pEventKey = m_pFocus;}break;case WM_KEYUP:{if( m_pEventKey == NULL ) break;TEventUI event = { 0 };event.Type = UIEVENT_KEYUP;event.chKey = (TCHAR)wParam;event.ptMouse = m_ptLastMousePos;event.wKeyState = MapKeyState();event.dwTimestamp = ::GetTickCount();m_pEventKey->Event(event);m_pEventKey = NULL;}break;case WM_SETCURSOR:{if( LOWORD(lParam) != HTCLIENT ) break;if( m_bMouseCapture ) return true;POINT pt = { 0 };::GetCursorPos(&pt);::ScreenToClient(m_hWndPaint, &pt);CControlUI* pControl = FindControl(pt);if( pControl == NULL ) break;if( (pControl->GetControlFlags() & UIFLAG_SETCURSOR) == 0 ) break;TEventUI event = { 0 };event.Type = UIEVENT_SETCURSOR;event.wParam = wParam;event.lParam = lParam;event.ptMouse = pt;event.wKeyState = MapKeyState();event.dwTimestamp = ::GetTickCount();pControl->Event(event);}return true;case WM_NOTIFY:{LPNMHDR lpNMHDR = (LPNMHDR) lParam;if( lpNMHDR != NULL ) lRes = ::SendMessage(lpNMHDR->hwndFrom, OCM__BASE + uMsg, wParam, lParam);return true;}break;case WM_COMMAND:{if( lParam == 0 ) break;HWND hWndChild = (HWND) lParam;lRes = ::SendMessage(hWndChild, OCM__BASE + uMsg, wParam, lParam);return true;}break;case WM_CTLCOLOREDIT:case WM_CTLCOLORSTATIC:{// Refer To: http://msdn.microsoft.com/en-us/library/bb761691(v=vs.85).aspx// Read-only or disabled edit controls do not send the WM_CTLCOLOREDIT message; instead, they send the WM_CTLCOLORSTATIC message.if( lParam == 0 ) break;HWND hWndChild = (HWND) lParam;lRes = ::SendMessage(hWndChild, OCM__BASE + uMsg, wParam, lParam);return true;}break;default:break;}pMsg = NULL;while( pMsg = static_cast<TNotifyUI*>(m_aAsyncNotify.GetAt(0)) ) {m_aAsyncNotify.Remove(0);if( pMsg->pSender != NULL ) {if( pMsg->pSender->OnNotify ) pMsg->pSender->OnNotify(pMsg);}for( int j = 0; j < m_aNotifiers.GetSize(); j++ ) {static_cast<INotifyUI*>(m_aNotifiers[j])->Notify(*pMsg);}delete pMsg;}return false;
}
.csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }

我去这里代码有点多,先简单捋一下,首先处理了如下消息:

WM_CTLCOLOREDIT:
WM_CTLCOLORSTATIC
WM_COMMAND:
WM_NOTIFY:
WM_SETCURSOR:
WM_KEYUP
WM_KEYDOWN
WM_CHAR
WM_MOUSEWHEEL
WM_CONTEXTMENU
WM_RBUTTONDOWN
WM_LBUTTONUP
WM_LBUTTONDBLCLK
WM_LBUTTONDOWN
WM_MOUSEMOVE
WM_MOUSELEAVE
WM_MOUSEHOVER
WM_TIMER
WM_SIZE
WM_GETMINMAXINFO
WM_PRINTCLIENT
WM_PAINT
WM_ERASEBKGND
WM_CLOSE
WM_APP

除了这些消息还有一个特别的东西需要留意:

m_aAsyncNotify以后再分析

 

这篇关于Duilib 窗口流程的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1017419

相关文章

C#提取PDF表单数据的实现流程

《C#提取PDF表单数据的实现流程》PDF表单是一种常见的数据收集工具,广泛应用于调查问卷、业务合同等场景,凭借出色的跨平台兼容性和标准化特点,PDF表单在各行各业中得到了广泛应用,本文将探讨如何使用... 目录引言使用工具C# 提取多个PDF表单域的数据C# 提取特定PDF表单域的数据引言PDF表单是一

PyCharm接入DeepSeek实现AI编程的操作流程

《PyCharm接入DeepSeek实现AI编程的操作流程》DeepSeek是一家专注于人工智能技术研发的公司,致力于开发高性能、低成本的AI模型,接下来,我们把DeepSeek接入到PyCharm中... 目录引言效果演示创建API key在PyCharm中下载Continue插件配置Continue引言

使用MongoDB进行数据存储的操作流程

《使用MongoDB进行数据存储的操作流程》在现代应用开发中,数据存储是一个至关重要的部分,随着数据量的增大和复杂性的增加,传统的关系型数据库有时难以应对高并发和大数据量的处理需求,MongoDB作为... 目录什么是MongoDB?MongoDB的优势使用MongoDB进行数据存储1. 安装MongoDB

Python实现NLP的完整流程介绍

《Python实现NLP的完整流程介绍》这篇文章主要为大家详细介绍了Python实现NLP的完整流程,文中的示例代码讲解详细,具有一定的借鉴价值,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1. 编程安装和导入必要的库2. 文本数据准备3. 文本预处理3.1 小写化3.2 分词(Tokenizatio

SpringBoot使用minio进行文件管理的流程步骤

《SpringBoot使用minio进行文件管理的流程步骤》MinIO是一个高性能的对象存储系统,兼容AmazonS3API,该软件设计用于处理非结构化数据,如图片、视频、日志文件以及备份数据等,本文... 目录一、拉取minio镜像二、创建配置文件和上传文件的目录三、启动容器四、浏览器登录 minio五、

bat脚本启动git bash窗口,并执行命令方式

《bat脚本启动gitbash窗口,并执行命令方式》本文介绍了如何在Windows服务器上使用cmd启动jar包时出现乱码的问题,并提供了解决方法——使用GitBash窗口启动并设置编码,通过编写s... 目录一、简介二、使用说明2.1 start.BAT脚本2.2 参数说明2.3 效果总结一、简介某些情

基于Redis有序集合实现滑动窗口限流的步骤

《基于Redis有序集合实现滑动窗口限流的步骤》滑动窗口算法是一种基于时间窗口的限流算法,通过动态地滑动窗口,可以动态调整限流的速率,Redis有序集合可以用来实现滑动窗口限流,本文介绍基于Redis... 滑动窗口算法是一种基于时间窗口的限流算法,它将时间划分为若干个固定大小的窗口,每个窗口内记录了该时间

Nginx、Tomcat等项目部署问题以及解决流程

《Nginx、Tomcat等项目部署问题以及解决流程》本文总结了项目部署中常见的four类问题及其解决方法:Nginx未按预期显示结果、端口未开启、日志分析的重要性以及开发环境与生产环境运行结果不一致... 目录前言1. Nginx部署后未按预期显示结果1.1 查看Nginx的启动情况1.2 解决启动失败的

Security OAuth2 单点登录流程

单点登录(英语:Single sign-on,缩写为 SSO),又译为单一签入,一种对于许多相互关连,但是又是各自独立的软件系统,提供访问控制的属性。当拥有这项属性时,当用户登录时,就可以获取所有系统的访问权限,不用对每个单一系统都逐一登录。这项功能通常是以轻型目录访问协议(LDAP)来实现,在服务器上会将用户信息存储到LDAP数据库中。相同的,单一注销(single sign-off)就是指

Spring Security基于数据库验证流程详解

Spring Security 校验流程图 相关解释说明(认真看哦) AbstractAuthenticationProcessingFilter 抽象类 /*** 调用 #requiresAuthentication(HttpServletRequest, HttpServletResponse) 决定是否需要进行验证操作。* 如果需要验证,则会调用 #attemptAuthentica