#include "StdAfx.h" #include namespace ui { typedef struct tagFINDTABINFO { Control* pFocus; Control* pLast; bool bForward; bool bNextIsIt; } FINDTABINFO; typedef struct tagFINDSHORTCUT { TCHAR ch; bool bPickNext; } FINDSHORTCUT; ////////////////////////////////////////////////////////////////////////// /// Window::Window() : m_hWnd(nullptr), m_pRoot(nullptr), OnEvent(), m_OldWndProc(::DefWindowProc), m_bSubclassed(false), m_nAlpha(255), m_renderOffset(), m_hDcPaint(nullptr), m_hwndTooltip(nullptr), m_ToolTip(), m_pFocus(nullptr), m_pNewHover(nullptr), m_pEventHover(nullptr), m_pEventClick(nullptr), m_pEventKey(nullptr), m_ptLastMousePos(-1, -1), m_pEventTouch(nullptr), m_ptLastTouchPos(-1, -1), m_pEventPointer(nullptr), m_bHandlePointer(true), m_szMinWindow(), m_szMaxWindow(), m_szInitWindowSize(), m_rcMaximizeInfo(0, 0, 0, 0), m_rcSizeBox(), m_rcAlphaFix(0, 0, 0, 0), m_szRoundCorner(), m_rcCaption(), m_uTimerID(0x1000), m_bFirstLayout(true), m_bIsArranged(false), m_bFocusNeeded(false), m_bMouseTracking(false), m_bMouseCapture(false), m_bIsLayeredWindow(true), m_aPreMessageFilters(), m_aMessageFilters(), m_aDelayedCleanup(), m_aFoundControls(), m_mNameHash(), m_mOptionGroup(), m_defaultFontInfo(), m_defaultAttrHash(), m_strWindowResourcePath(), m_aTranslateAccelerator(), m_heightPercent(0), m_closeFlag() { LOGFONT lf = { 0 }; ::GetObject(::GetStockObject(DEFAULT_GUI_FONT), sizeof(LOGFONT), &lf); lf.lfCharSet = DEFAULT_CHARSET; if (GlobalManager::GetDefaultFontName().length() > 0) _tcscpy_s(lf.lfFaceName, LF_FACESIZE, GlobalManager::GetDefaultFontName().c_str()); HFONT hDefaultFont = ::CreateFontIndirect(&lf); m_defaultFontInfo.hFont = hDefaultFont; m_defaultFontInfo.sFontName = lf.lfFaceName; m_defaultFontInfo.iSize = -lf.lfHeight; m_defaultFontInfo.bBold = (lf.lfWeight >= FW_BOLD); m_defaultFontInfo.bUnderline = (lf.lfUnderline == TRUE); m_defaultFontInfo.bItalic = (lf.lfItalic == TRUE); ::ZeroMemory(&m_defaultFontInfo.tm, sizeof(m_defaultFontInfo.tm)); } Window::~Window() { // Delete the control-tree structures for (auto it = m_aDelayedCleanup.begin(); it != m_aDelayedCleanup.end(); it++) delete *it; delete m_pRoot; ::DeleteObject(m_defaultFontInfo.hFont); RemoveAllClass(); RemoveAllOptionGroups(); // Reset other parts... if (m_hwndTooltip != NULL) ::DestroyWindow(m_hwndTooltip); if (m_hDcPaint != NULL) ::ReleaseDC(m_hWnd, m_hDcPaint); GlobalManager::RemovePreMessage(this); } HWND Window::GetHWND() const { return m_hWnd; } bool Window::RegisterWindowClass() { WNDCLASS wc = { 0 }; wc.style = GetClassStyle(); wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hIcon = NULL; wc.lpfnWndProc = Window::__WndProc; wc.hInstance = ::GetModuleHandle(NULL); wc.hCursor = ::LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = NULL; wc.lpszMenuName = NULL; std::wstring className = GetWindowClassName(); wc.lpszClassName = className.c_str(); ATOM ret = ::RegisterClass(&wc); ASSERT(ret != NULL || ::GetLastError() == ERROR_CLASS_ALREADY_EXISTS); return ret != NULL || ::GetLastError() == ERROR_CLASS_ALREADY_EXISTS; } bool Window::RegisterSuperClass() { // Get the class information from an existing // window so we can subclass it later on... WNDCLASSEX wc = { 0 }; wc.cbSize = sizeof(WNDCLASSEX); std::wstring superClassName = GetSuperClassName(); if (!::GetClassInfoEx(NULL, superClassName.c_str(), &wc)) { if (!::GetClassInfoEx(::GetModuleHandle(NULL), superClassName.c_str(), &wc)) { ASSERT(!"Unable to locate window class"); return false; } } m_OldWndProc = wc.lpfnWndProc; wc.lpfnWndProc = Window::__ControlProc; wc.hInstance = ::GetModuleHandle(NULL); std::wstring className = GetWindowClassName(); wc.lpszClassName = className.c_str(); ATOM ret = ::RegisterClassEx(&wc); ASSERT(ret != NULL || ::GetLastError() == ERROR_CLASS_ALREADY_EXISTS); return ret != NULL || ::GetLastError() == ERROR_CLASS_ALREADY_EXISTS; } std::wstring Window::GetWindowClassName() const { ASSERT(FALSE); return L""; } std::wstring Window::GetSuperClassName() const { return std::wstring(); } UINT Window::GetClassStyle() const { return 0; } HWND Window::Subclass(HWND hWnd) { ASSERT(::IsWindow(hWnd)); ASSERT(m_hWnd == NULL); m_OldWndProc = SubclassWindow(hWnd, __WndProc); if (m_OldWndProc == NULL) return NULL; m_bSubclassed = true; m_hWnd = hWnd; ::SetWindowLongPtr(hWnd, GWLP_USERDATA, reinterpret_cast(this)); return m_hWnd; } void Window::Unsubclass() { ASSERT(::IsWindow(m_hWnd)); if (!::IsWindow(m_hWnd)) return; if (!m_bSubclassed) return; SubclassWindow(m_hWnd, m_OldWndProc); m_OldWndProc = ::DefWindowProc; m_bSubclassed = false; } HWND Window::Create(HWND hwndParent, LPCTSTR pstrName, DWORD dwStyle, DWORD dwExStyle, bool isLayeredWindow, const UiRect& rc) { if( !GetSuperClassName().empty() && !RegisterSuperClass() ) return NULL; if( GetSuperClassName().empty() && !RegisterWindowClass() ) return NULL; std::wstring className = GetWindowClassName(); m_bIsLayeredWindow = isLayeredWindow; if (m_bIsLayeredWindow) { dwExStyle |= WS_EX_LAYERED; } m_hWnd = ::CreateWindowEx(dwExStyle, className.c_str(), pstrName, dwStyle, rc.left, rc.top, rc.GetWidth(), rc.GetHeight(), hwndParent, NULL, ::GetModuleHandle(NULL), this); LONG nWindowLong = GetWindowLong( m_hWnd, GWL_STYLE ); if( nWindowLong & WS_CAPTION ) { SetWindowLong( m_hWnd, GWL_STYLE, nWindowLong & ~WS_CAPTION ); } ASSERT(m_hWnd!=NULL); return m_hWnd; } void Window::Close(UINT nRet) { if (m_bFakeModal) { auto parent_hwnd = GetWindowOwner(m_hWnd); ASSERT(::IsWindow(parent_hwnd)); ::EnableWindow(parent_hwnd, TRUE); ::SetFocus(parent_hwnd); m_bFakeModal = false; } ASSERT(::IsWindow(m_hWnd)); if (!::IsWindow(m_hWnd)) return; if (m_pRoot && IsWindowsVistaOrGreater()) { m_pRoot->SetVisible(false); auto closeCallback = [this, nRet]() { this->PostMessage(WM_CLOSE, (WPARAM)nRet, 0L); }; TimerManager::GetInstance()->AddCancelableTimer(m_closeFlag.GetWeakFlag(), closeCallback, 300, 1); } else { PostMessage(WM_CLOSE, (WPARAM)nRet, 0L); } } void Window::ShowWindow(bool bShow /*= true*/, bool bTakeFocus /*= false*/) { ASSERT(::IsWindow(m_hWnd)); if( !::IsWindow(m_hWnd) ) return; ::ShowWindow(m_hWnd, bShow ? (bTakeFocus ? SW_SHOWNORMAL : SW_SHOWNOACTIVATE) : SW_HIDE); } UINT Window::ShowModal() { ASSERT(::IsWindow(m_hWnd)); UINT nRet = 0; HWND hWndParent = GetWindowOwner(m_hWnd); ::ShowWindow(m_hWnd, SW_SHOWNORMAL); ::EnableWindow(hWndParent, FALSE); MSG msg = { 0 }; HWND hTempWnd = m_hWnd; while( ::IsWindow(hTempWnd) && ::GetMessage(&msg, NULL, 0, 0) ) { if( msg.message == WM_CLOSE && msg.hwnd == m_hWnd ) { nRet = msg.wParam; ::EnableWindow(hWndParent, TRUE); ::SetFocus(hWndParent); } if( !GlobalManager::TranslateMessage(&msg) ) { ::TranslateMessage(&msg); ::DispatchMessage(&msg); } if( msg.message == WM_QUIT ) break; } ::EnableWindow(hWndParent, TRUE); ::SetFocus(hWndParent); if( msg.message == WM_QUIT ) ::PostQuitMessage(msg.wParam); return nRet; } void Window::ShowModalFake(HWND parent_hwnd) { ASSERT(::IsWindow(m_hWnd)); ASSERT(::IsWindow(parent_hwnd)); auto p_hwnd = GetWindowOwner(m_hWnd); ASSERT(::IsWindow(p_hwnd)); ASSERT(p_hwnd == parent_hwnd); ::EnableWindow(parent_hwnd, FALSE); ShowWindow(); m_bFakeModal = true; } void Window::CenterWindow() { ASSERT(::IsWindow(m_hWnd)); ASSERT((GetWindowStyle(m_hWnd)&WS_CHILD)==0); UiRect rcDlg; ::GetWindowRect(m_hWnd, &rcDlg); UiRect rcArea; UiRect rcCenter; HWND hWnd = GetHWND(); HWND hWndCenter = ::GetWindowOwner(m_hWnd); if (hWndCenter!=NULL) hWnd=hWndCenter; // 处理多显示器模式下屏幕居中 MONITORINFO oMonitor = {}; oMonitor.cbSize = sizeof(oMonitor); ::GetMonitorInfo(::MonitorFromWindow(hWnd, MONITOR_DEFAULTTONEAREST), &oMonitor); rcArea = oMonitor.rcWork; if( hWndCenter == NULL ) rcCenter = rcArea; else if( ::IsIconic(hWndCenter) ) rcCenter = rcArea; else ::GetWindowRect(hWndCenter, &rcCenter); int DlgWidth = rcDlg.right - rcDlg.left; int DlgHeight = rcDlg.bottom - rcDlg.top; // Find dialog's upper left based on rcCenter int xLeft = (rcCenter.left + rcCenter.right) / 2 - DlgWidth / 2; int yTop = (rcCenter.top + rcCenter.bottom) / 2 - DlgHeight / 2; // The dialog is outside the screen, move it inside if( xLeft < rcArea.left ) xLeft = rcArea.left; else if( xLeft + DlgWidth > rcArea.right ) xLeft = rcArea.right - DlgWidth; if( yTop < rcArea.top ) yTop = rcArea.top; else if( yTop + DlgHeight > rcArea.bottom ) yTop = rcArea.bottom - DlgHeight; ::SetWindowPos(m_hWnd, NULL, xLeft, yTop, -1, -1, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE); } void Window::SetIcon(UINT nRes) { HICON hIcon = (HICON)::LoadImage(::GetModuleHandle(NULL), MAKEINTRESOURCE(nRes), IMAGE_ICON, ::GetSystemMetrics(SM_CXICON), ::GetSystemMetrics(SM_CYICON), LR_DEFAULTCOLOR | LR_SHARED); ASSERT(hIcon); ::SendMessage(m_hWnd, WM_SETICON, (WPARAM) TRUE, (LPARAM) hIcon); hIcon = (HICON)::LoadImage(::GetModuleHandle(NULL), MAKEINTRESOURCE(nRes), IMAGE_ICON, ::GetSystemMetrics(SM_CXSMICON), ::GetSystemMetrics(SM_CYSMICON), LR_DEFAULTCOLOR | LR_SHARED); ASSERT(hIcon); ::SendMessage(m_hWnd, WM_SETICON, (WPARAM) FALSE, (LPARAM) hIcon); } LRESULT Window::SendMessage(UINT uMsg, WPARAM wParam /*= 0*/, LPARAM lParam /*= 0*/) { ASSERT(::IsWindow(m_hWnd)); return ::SendMessage(m_hWnd, uMsg, wParam, lParam); } LRESULT Window::PostMessage(UINT uMsg, WPARAM wParam /*= 0*/, LPARAM lParam /*= 0*/) { ASSERT(::IsWindow(m_hWnd)); return ::PostMessage(m_hWnd, uMsg, wParam, lParam); } void Window::AttachWindowClose(const EventCallback& callback) { OnEvent[kEventWindowClose] += callback; } void Window::OnFinalMessage(HWND hWnd) { UnregisterTouchWindowWrapper(m_hWnd); SendNotify(kEventWindowClose); } LRESULT CALLBACK Window::__WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { Window* pThis = NULL; if( uMsg == WM_NCCREATE ) { LPCREATESTRUCT lpcs = reinterpret_cast(lParam); pThis = static_cast(lpcs->lpCreateParams); pThis->m_hWnd = hWnd; ::SetWindowLongPtr(hWnd, GWLP_USERDATA, reinterpret_cast(pThis)); } else { pThis = reinterpret_cast(::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->OnFinalMessage(hWnd); return lRes; } } if( pThis != NULL) { return pThis->HandleMessage(uMsg, wParam, lParam); } else { return ::DefWindowProc(hWnd, uMsg, wParam, lParam); } } LRESULT CALLBACK Window::__ControlProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { Window* pThis = NULL; if( uMsg == WM_NCCREATE ) { LPCREATESTRUCT lpcs = reinterpret_cast(lParam); pThis = static_cast(lpcs->lpCreateParams); ::SetProp(hWnd, _T("WndX"), (HANDLE) pThis); pThis->m_hWnd = hWnd; } else { pThis = reinterpret_cast(::GetProp(hWnd, _T("WndX"))); if( uMsg == WM_NCDESTROY && pThis != NULL ) { LRESULT lRes = ::CallWindowProc(pThis->m_OldWndProc, hWnd, uMsg, wParam, lParam); if( pThis->m_bSubclassed ) pThis->Unsubclass(); ::SetProp(hWnd, _T("WndX"), NULL); 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); } } ///////////////////////////////////////////////////////////////////////////////////// // // void Window::Init(HWND hWnd) { m_hWnd = hWnd; ASSERT(::IsWindow(hWnd)); // Remember the window context we came from m_hDcPaint = ::GetDC(hWnd); // We'll want to filter messages globally too GlobalManager::AddPreMessage(this); m_renderContext = GlobalManager::CreateRenderContext(); RegisterTouchWindowWrapper(hWnd, 0); } bool Window::AttachDialog(Box* pRoot) { ASSERT(::IsWindow(m_hWnd)); // Reset any previous attachment SetFocus(NULL); m_pEventKey = NULL; m_pNewHover = NULL; m_pEventHover = NULL; m_pEventClick = NULL; m_pEventTouch = NULL; m_pEventPointer = NULL; // Remove the existing control-tree. We might have gotten inside this function as // a result of an event fired or similar, so we cannot just delete the objects and // pull the internal memory of the calling code. We'll delay the cleanup. if (m_pRoot != NULL) { AddDelayedCleanup(m_pRoot); } // Set the dialog root element m_pRoot = pRoot; // Go ahead... m_bIsArranged = true; m_bFirstLayout = true; m_bFocusNeeded = true; // Initiate all control return InitControls(m_pRoot); } bool Window::InitControls(Control* pControl, Box* pParent /*= NULL*/) { ASSERT(pControl); if (pControl == NULL) return false; pControl->SetWindow(this, pParent != NULL ? pParent : pControl->GetParent(), true); pControl->FindControl(__FindControlFromNameHash, this, UIFIND_ALL); return true; } void Window::ReapObjects(Control* pControl) { if (!pControl) { return; } if (pControl == m_pEventKey) m_pEventKey = NULL; if (pControl == m_pEventHover) m_pEventHover = NULL; if (pControl == m_pNewHover) m_pNewHover = NULL; if (pControl == m_pEventClick) m_pEventClick = NULL; if (pControl == m_pEventTouch) m_pEventTouch = NULL; if (pControl == m_pEventPointer) m_pEventPointer = NULL; if (pControl == m_pFocus) m_pFocus = NULL; std::wstring sName = pControl->GetName(); if (!sName.empty()) { auto it = m_mNameHash.find(sName); if (it != m_mNameHash.end()) { m_mNameHash.erase(it); } } } std::wstring Window::GetWindowResourcePath() { return m_strWindowResourcePath; } void Window::SetWindowResourcePath(const std::wstring& strPath) { m_strWindowResourcePath = strPath; if (m_strWindowResourcePath.empty()) return; TCHAR cEnd = m_strWindowResourcePath.at(m_strWindowResourcePath.length() - 1); if (cEnd != _T('\\') && cEnd != _T('/')) m_strWindowResourcePath += _T('\\'); } TFontInfo* Window::GetDefaultFontInfo() { if (m_defaultFontInfo.tm.tmHeight == 0) { HFONT hOldFont = (HFONT) ::SelectObject(m_hDcPaint, m_defaultFontInfo.hFont); ::GetTextMetrics(m_hDcPaint, &m_defaultFontInfo.tm); ::SelectObject(m_hDcPaint, hOldFont); } return &m_defaultFontInfo; } void Window::AddClass(const std::wstring& strClassName, const std::wstring& strControlAttrList) { ASSERT(!strClassName.empty()); ASSERT(!strControlAttrList.empty()); m_defaultAttrHash[strClassName] = strControlAttrList; } const std::map* Window::GetClassMap() { return &m_defaultAttrHash; } std::wstring Window::GetClassAttributes(const std::wstring& strClassName) const { auto it = m_defaultAttrHash.find(strClassName); if (it != m_defaultAttrHash.end()) { return it->second; } return L""; } bool Window::RemoveClass(const std::wstring& strClassName) { auto it = m_defaultAttrHash.find(strClassName); if (it != m_defaultAttrHash.end()) { m_defaultAttrHash.erase(it); return true; } return false; } void Window::RemoveAllClass() { m_defaultAttrHash.clear(); } bool Window::AddOptionGroup(const std::wstring& strGroupName, Control* pControl) { auto it = m_mOptionGroup.find(strGroupName); if (it != m_mOptionGroup.end()) { auto it2 = std::find(it->second.begin(), it->second.end(), pControl); if (it2 != it->second.end()) { return false; } it->second.push_back(pControl); } else { m_mOptionGroup[strGroupName].push_back(pControl); } return true; } std::vector* Window::GetOptionGroup(const std::wstring& strGroupName) { auto it = m_mOptionGroup.find(strGroupName); if (it != m_mOptionGroup.end()) return &(it->second); return NULL; } void Window::RemoveOptionGroup(const std::wstring& strGroupName, Control* pControl) { ASSERT(!strGroupName.empty()); ASSERT(pControl); auto it = m_mOptionGroup.find(strGroupName); if (it != m_mOptionGroup.end()) { auto it2 = std::find(it->second.begin(), it->second.end(), pControl); if (it2 != it->second.end()) { it->second.erase(it2); } if (it->second.empty()) { m_mOptionGroup.erase(it); } } } void Window::RemoveAllOptionGroups() { m_mOptionGroup.clear(); } void Window::ClearImageCache() { Control *pRoot = m_shadow.GetRoot(); if (pRoot) pRoot->ClearImageCache(); else m_pRoot->ClearImageCache(); } POINT Window::GetMousePos() const { return m_ptLastMousePos; } UiRect Window::GetSizeBox() { return m_rcSizeBox; } void Window::SetSizeBox(const UiRect& rcSizeBox) { m_rcSizeBox = rcSizeBox; } UiRect Window::GetCaptionRect() const { return m_rcCaption; } void Window::SetCaptionRect(UiRect& rcCaption) { DpiManager::GetInstance()->ScaleRect(rcCaption); m_rcCaption = rcCaption; } CSize Window::GetRoundCorner() const { return m_szRoundCorner; } void Window::SetRoundCorner(int cx, int cy) { DpiManager::GetInstance()->ScaleInt(cx); DpiManager::GetInstance()->ScaleInt(cy); m_szRoundCorner.cx = cx; m_szRoundCorner.cy = cy; } UiRect Window::GetMaximizeInfo() const { return m_rcMaximizeInfo; } void Window::SetMaximizeInfo(UiRect& rcMaximize) { DpiManager::GetInstance()->ScaleRect(rcMaximize); m_rcMaximizeInfo = rcMaximize; } UiRect Window::GetAlphaFixCorner() const { return m_rcAlphaFix; } void Window::SetAlphaFixCorner(UiRect& rc) { DpiManager::GetInstance()->ScaleRect(rc); m_rcAlphaFix = rc; } double Window::GetHeightPercent() const { return m_heightPercent; } void Window::SetHeightPercent(double heightPercent) { m_heightPercent = heightPercent; } void Window::SetTextId(const std::wstring& strTextId) { ::SetWindowText(m_hWnd, MutiLanSupport::GetInstance()->GetStringViaID(strTextId).c_str()); } void Window::SetShadowAttached(bool bShadowAttached) { m_shadow.SetShadowAttached(bShadowAttached); } std::wstring Window::GetShadowImage() const { return m_shadow.GetShadowImage(); } void Window::SetShadowImage(const std::wstring &strImage) { m_shadow.SetShadowImage(strImage); } UiRect Window::GetShadowCorner() const { return m_shadow.GetShadowCorner(); } void Window::SetShadowCorner(const UiRect rect) { m_shadow.SetShadowCorner(rect); } UiRect Window::GetPos(bool bContainShadow) const { UiRect rcPos; ::GetWindowRect(m_hWnd, &rcPos); if (!bContainShadow) { UiRect padding = m_shadow.GetShadowCorner(); rcPos.left += padding.left; rcPos.right -= padding.right; rcPos.top += padding.top; rcPos.bottom -= padding.bottom; } return rcPos; } void Window::SetPos(const UiRect& rc, bool bNeedDpiScale, UINT uFlags, HWND hWndInsertAfter, bool bContainShadow) { UiRect rcNewPos = rc; if (bNeedDpiScale) DpiManager::GetInstance()->ScaleRect(rcNewPos); ASSERT(::IsWindow(m_hWnd)); if (!bContainShadow) { rcNewPos.Inflate(m_shadow.GetShadowCorner()); } ::SetWindowPos(m_hWnd, hWndInsertAfter, rcNewPos.left, rcNewPos.top, rcNewPos.GetWidth(), rcNewPos.GetHeight(), uFlags); } CSize Window::GetMinInfo(bool bContainShadow) const { CSize xy = m_szMinWindow; if (!bContainShadow) { UiRect rcShadow = m_shadow.GetShadowCorner(); if (xy.cx != 0) { xy.cx -= rcShadow.left + rcShadow.right; } if (xy.cy != 0) { xy.cy -= rcShadow.top + rcShadow.bottom; } } return xy; } void Window::SetMinInfo(int cx, int cy, bool bContainShadow) { DpiManager::GetInstance()->ScaleInt(cx); DpiManager::GetInstance()->ScaleInt(cy); ASSERT(cx >= 0 && cy >= 0); if (!bContainShadow) { UiRect rcShadow = m_shadow.GetShadowCorner(); if (cx != 0) { cx += rcShadow.left + rcShadow.right; } if (cy != 0) { cy += rcShadow.top + rcShadow.bottom; } } m_szMinWindow.cx = cx; m_szMinWindow.cy = cy; } CSize Window::GetMaxInfo(bool bContainShadow) const { CSize xy = m_szMaxWindow; if (!bContainShadow) { UiRect rcShadow = m_shadow.GetShadowCorner(); if (xy.cx != 0) { xy.cx -= rcShadow.left + rcShadow.right; } if (xy.cy != 0) { xy.cy -= rcShadow.top + rcShadow.bottom; } } return xy; } void Window::SetMaxInfo(int cx, int cy, bool bContainShadow) { DpiManager::GetInstance()->ScaleInt(cx); DpiManager::GetInstance()->ScaleInt(cy); ASSERT(cx >= 0 && cy >= 0); if (!bContainShadow) { UiRect rcShadow = m_shadow.GetShadowCorner(); if (cx != 0) { cx += rcShadow.left + rcShadow.right; } if (cy != 0) { cy += rcShadow.top + rcShadow.bottom; } } m_szMaxWindow.cx = cx; m_szMaxWindow.cy = cy; } CSize Window::GetInitSize(bool bContainShadow) const { CSize xy = m_szInitWindowSize; if (!bContainShadow) { UiRect rcShadow = m_shadow.GetShadowCorner(); if (xy.cx != 0) { xy.cx -= rcShadow.left + rcShadow.right; } if (xy.cy != 0) { xy.cy -= rcShadow.top + rcShadow.bottom; } } return xy; } void Window::SetInitSize(int cx, int cy, bool bContainShadow, bool bNeedDpiScale) { if (bNeedDpiScale) { DpiManager::GetInstance()->ScaleInt(cy); DpiManager::GetInstance()->ScaleInt(cx); } if (!bContainShadow) { UiRect rcShadow = m_shadow.GetShadowCorner(); cx += rcShadow.left + rcShadow.right; cy += rcShadow.top + rcShadow.bottom; } m_szInitWindowSize.cx = cx; m_szInitWindowSize.cy = cy; if( m_pRoot == NULL && m_hWnd != NULL ) { ::SetWindowPos(m_hWnd, NULL, 0, 0, m_szInitWindowSize.cx, m_szInitWindowSize.cy, SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE); } } bool Window::AddPreMessageFilter(IUIMessageFilter* pFilter) { ASSERT(std::find(m_aPreMessageFilters.begin(), m_aPreMessageFilters.end(), pFilter) == m_aPreMessageFilters.end()); m_aPreMessageFilters.push_back(pFilter); return true; } bool Window::RemovePreMessageFilter(IUIMessageFilter* pFilter) { for (auto it = m_aPreMessageFilters.begin(); it != m_aPreMessageFilters.end(); it++) { if (*it == pFilter) { m_aPreMessageFilters.erase(it); return true; } } return false; } bool Window::AddMessageFilter(IUIMessageFilter* pFilter) { ASSERT(std::find(m_aMessageFilters.begin(), m_aMessageFilters.end(), pFilter) == m_aMessageFilters.end()); m_aMessageFilters.push_back(pFilter); return true; } bool Window::RemoveMessageFilter(IUIMessageFilter* pFilter) { for (auto it = m_aMessageFilters.begin(); it != m_aMessageFilters.end(); it++) { if (*it == pFilter) { m_aMessageFilters.erase(it); return true; } } return false; } bool Window::AddControlFromPointFinder(IControlFromPointFinder* pFinder) { ASSERT(std::find(m_aIControlFromPointFinder.begin(), m_aIControlFromPointFinder.end(), pFinder) == m_aIControlFromPointFinder.end()); m_aIControlFromPointFinder.emplace_back(pFinder); return true; } bool Window::RemoveControlFromPointFinder(IControlFromPointFinder* pFinder) { auto it = std::find_if(m_aIControlFromPointFinder.begin(), m_aIControlFromPointFinder.end(), [&](IControlFromPointFinder* item) { return pFinder == item; }); if (it != m_aIControlFromPointFinder.end()) { m_aIControlFromPointFinder.erase(it); return true; } return false; } bool Window::AddTranslateAccelerator(ITranslateAccelerator *pTranslateAccelerator) { ASSERT(std::find(m_aTranslateAccelerator.begin(), m_aTranslateAccelerator.end(), pTranslateAccelerator) == m_aTranslateAccelerator.end()); m_aTranslateAccelerator.push_back(pTranslateAccelerator); return true; } bool Window::RemoveTranslateAccelerator(ITranslateAccelerator *pTranslateAccelerator) { for (auto it = m_aTranslateAccelerator.begin(); it != m_aTranslateAccelerator.end(); it++) { if (*it == pTranslateAccelerator) { m_aTranslateAccelerator.erase(it); return true; } } return false; } bool Window::TranslateAccelerator(LPMSG pMsg) { for (auto it = m_aTranslateAccelerator.begin(); it != m_aTranslateAccelerator.end(); it++) { LRESULT lResult = (*it)->TranslateAccelerator(pMsg); if (lResult == S_OK) return true; } return false; } bool Window::PreMessageHandler(UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT& /*lRes*/) { for (auto it = m_aPreMessageFilters.begin(); it != m_aPreMessageFilters.end(); it++) { BOOL bHandled = false; (*it)->MessageHandler(uMsg, wParam, lParam, bHandled); if( bHandled ) { return true; } } switch( uMsg ) { case WM_SYSKEYDOWN: { if( m_pFocus != NULL ) { m_pFocus->HandleMessageTemplate(kEventSystemKey, 0, 0, wParam); } } break; } return false; } LRESULT Window::HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam) { bool handled = false; LRESULT ret = DoHandlMessage(uMsg, wParam, lParam, handled); if (handled) { return ret; } else { return CallWindowProc(uMsg, wParam, lParam); } } LRESULT Window::DoHandlMessage(UINT uMsg, WPARAM wParam, LPARAM lParam, bool& handled) { handled = false; // Cycle through listeners for (auto it = m_aMessageFilters.begin(); it != m_aMessageFilters.end(); it++) { BOOL bHandled = false; LRESULT lResult = (*it)->MessageHandler(uMsg, wParam, lParam, bHandled); if (bHandled && uMsg != WM_MOUSEMOVE) { handled = true; return lResult; } } // Custom handling of events switch (uMsg) { case WM_APP + 1: { for (auto it = m_aDelayedCleanup.begin(); it != m_aDelayedCleanup.end(); it++) delete *it; m_aDelayedCleanup.clear(); } break; case WM_CLOSE: { // Make sure all matching "closing" events are sent if (m_pEventHover != NULL) { m_pEventHover->HandleMessageTemplate(kEventMouseLeave); } if (m_pEventClick != NULL) { m_pEventClick->HandleMessageTemplate(kEventMouseButtonUp); } if (m_pEventTouch != NULL) { m_pEventTouch->HandleMessageTemplate(kEventMouseButtonUp); } if (m_pEventPointer != NULL) { m_pEventPointer->HandleMessageTemplate(kEventMouseButtonUp); } SetFocus(NULL); // Hmmph, the usual Windows tricks to avoid // focus loss... HWND hwndParent = GetWindowOwner(m_hWnd); if (hwndParent != NULL) ::SetFocus(hwndParent); } break; case WM_ERASEBKGND: { handled = true; return 1; } case WM_PAINT: { Paint(); handled = true; return 0; } // If any of the painting requested a resize again, we'll need // to invalidate the entire window once more. if (m_bIsArranged) { ::InvalidateRect(m_hWnd, NULL, FALSE); } handled = true; return 0; case WM_SIZE: { if (m_pFocus != NULL) { m_pFocus->HandleMessageTemplate(kEventWindowSize); } if (m_pRoot != NULL) m_pRoot->Arrange(); if (wParam == SIZE_MAXIMIZED) { m_shadow.MaximizedOrRestored(true); } else if (wParam == SIZE_RESTORED) { m_shadow.MaximizedOrRestored(false); } } handled = true; return 0; case WM_MOUSEHOVER: { m_bMouseTracking = false; POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) }; Control* pHover = FindControl(pt); if (pHover == NULL) break; // Generate mouse hover event if (m_pEventHover != NULL) { m_pEventHover->HandleMessageTemplate(kEventMouseHover, 0, 0, 0, pt); } // Create tooltip information std::wstring sToolTip = pHover->GetToolTipText(); //if( sToolTip.empty() ) { // handled = true; // return 0; //} ::ZeroMemory(&m_ToolTip, sizeof(TOOLINFO)); m_ToolTip.cbSize = sizeof(TOOLINFO); m_ToolTip.uFlags = TTF_IDISHWND; m_ToolTip.hwnd = m_hWnd; m_ToolTip.uId = (UINT_PTR)m_hWnd; m_ToolTip.hinst = ::GetModuleHandle(NULL); m_ToolTip.lpszText = const_cast((LPCTSTR)sToolTip.c_str()); 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_hWnd, NULL, ::GetModuleHandle(NULL), NULL); ::SendMessage(m_hwndTooltip, TTM_ADDTOOL, 0, (LPARAM)&m_ToolTip); } ::SendMessage(m_hwndTooltip, TTM_SETMAXTIPWIDTH, 0, pHover->GetToolTipWidth()); ::SendMessage(m_hwndTooltip, TTM_SETTOOLINFO, 0, (LPARAM)&m_ToolTip); ::SendMessage(m_hwndTooltip, TTM_TRACKACTIVATE, TRUE, (LPARAM)&m_ToolTip); } handled = true; return 0; case WM_MOUSELEAVE: { if (m_hwndTooltip != NULL) ::SendMessage(m_hwndTooltip, TTM_TRACKACTIVATE, FALSE, (LPARAM)&m_ToolTip); if (m_bMouseTracking) ::SendMessage(m_hWnd, WM_MOUSEMOVE, 0, (LPARAM)-1); m_bMouseTracking = false; } break; case WM_MOUSEMOVE: { if (m_pEventTouch != NULL || m_pEventPointer != NULL) break; // 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_hWnd; tme.dwHoverTime = m_hwndTooltip == NULL ? 400UL : (DWORD) ::SendMessage(m_hwndTooltip, TTM_GETDELAYTIME, TTDT_INITIAL, 0L); _TrackMouseEvent(&tme); m_bMouseTracking = true; } // Generate the appropriate mouse messages POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) }; m_ptLastMousePos = pt; if (!HandleMouseEnterLeave(pt, wParam, lParam)) break; if (m_pEventClick != NULL) { m_pEventClick->HandleMessageTemplate(kEventMouseMove, wParam, lParam, 0, pt); } else if (m_pNewHover != NULL) { m_pNewHover->HandleMessageTemplate(kEventMouseMove, wParam, lParam, 0, pt); } } break; case WM_LBUTTONDOWN: { if (m_pEventTouch != NULL || m_pEventPointer != NULL) break; // 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_hWnd); POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) }; m_ptLastMousePos = pt; Control* pControl = FindControl(pt); if (pControl == NULL) break; if (pControl->GetWindow() != this) break; m_pEventClick = pControl; pControl->SetFocus(); SetCapture(); pControl->HandleMessageTemplate(kEventMouseButtonDown, wParam, lParam, 0, pt); } break; case WM_RBUTTONDOWN: { if (m_pEventTouch != NULL || m_pEventPointer != NULL) break; ::SetFocus(m_hWnd); POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) }; m_ptLastMousePos = pt; Control* pControl = FindControl(pt); if (pControl == NULL) break; if (pControl->GetWindow() != this) break; m_pEventClick = pControl; pControl->SetFocus(); SetCapture(); pControl->HandleMessageTemplate(kEventMouseRightButtonDown, wParam, lParam, 0, pt); } break; case WM_LBUTTONDBLCLK: { if (m_pEventTouch != NULL || m_pEventPointer != NULL) break; ::SetFocus(m_hWnd); POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) }; m_ptLastMousePos = pt; Control* pControl = FindControl(pt); if (pControl == NULL) break; if (pControl->GetWindow() != this) break; m_pEventClick = pControl; SetCapture(); pControl->HandleMessageTemplate(kEventInternalDoubleClick, wParam, lParam, 0, pt); } break; case WM_LBUTTONUP: { POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) }; m_ptLastMousePos = pt; ReleaseCapture(); if (m_pEventClick == NULL) break; m_pEventClick->HandleMessageTemplate(kEventMouseButtonUp, wParam, lParam, 0, pt); m_pEventClick = NULL; } break; case WM_RBUTTONUP: { POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) }; m_ptLastMousePos = pt; ReleaseCapture(); if (m_pEventClick == NULL) break; m_pEventClick->HandleMessageTemplate(kEventMouseRightButtonUp, wParam, lParam, 0, pt); //m_pEventClick = NULL; } break; case WM_IME_STARTCOMPOSITION: { if (m_pFocus == NULL) break; m_pFocus->HandleMessageTemplate(kEventImeStartComposition, wParam, lParam, wParam); } break; case WM_IME_ENDCOMPOSITION: { if (m_pFocus == NULL) break; m_pFocus->HandleMessageTemplate(kEventImeEndComposition, wParam, lParam, wParam); } break; case WM_MOUSEWHEEL: { POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) }; ::ScreenToClient(m_hWnd, &pt); m_ptLastMousePos = pt; Control* pControl = FindControl(pt); if (pControl == NULL) break; if (pControl->GetWindow() != this) break; int zDelta = (int)(short)HIWORD(wParam); pControl->HandleMessageTemplate(kEventMouseScrollWheel, zDelta, lParam); } break; case WM_TOUCH: { unsigned int nNumInputs = (int)wParam; TOUCHINPUT* pInputs = new TOUCHINPUT[nNumInputs]; // 只关心第一个触摸位置 if (nNumInputs >= 1 && GetTouchInputInfoWrapper((HTOUCHINPUT)lParam, nNumInputs, pInputs, sizeof(TOUCHINPUT))) { if (pInputs[0].dwID != 0) { POINT pt; pt.x = TOUCH_COORD_TO_PIXEL(pInputs[0].x); pt.y = TOUCH_COORD_TO_PIXEL(pInputs[0].y); ScreenToClient(m_hWnd, &pt); if (pInputs[0].dwFlags & TOUCHEVENTF_DOWN) { if (m_pEventClick != NULL || m_pEventPointer != NULL) break; ::SetFocus(m_hWnd); m_ptLastTouchPos = pt; Control *pControl = FindControl(pt); if (pControl == NULL) break; if (pControl->GetWindow() != this) break; m_pEventTouch = pControl; pControl->SetFocus(); SetCapture(); pControl->HandleMessageTemplate(kEventTouchDown, 0, lParam, 0, pt); } else if (pInputs[0].dwFlags & TOUCHEVENTF_MOVE) { if (m_pEventClick != NULL || m_pEventPointer != NULL) break; if (m_ptLastTouchPos.x == pt.x && m_ptLastTouchPos.y == pt.y) break; m_ptLastTouchPos = pt; if (m_pEventTouch == NULL) break; if (!HandleMouseEnterLeave(pt, wParam, lParam)) break; m_pEventTouch->HandleMessageTemplate(kEventTouchMove, 0, 0, 0, pt); } else if (pInputs[0].dwFlags & TOUCHEVENTF_UP) { m_ptLastTouchPos = pt; ReleaseCapture(); if (m_pEventTouch == NULL) break; m_pEventTouch->HandleMessageTemplate(kEventTouchUp, 0, lParam, 0, pt); m_pEventTouch = NULL; } } } CloseTouchInputHandleWrapper((HTOUCHINPUT)lParam); delete[] pInputs; } break; case WM_POINTERDOWN: case WM_POINTERUP: case WM_POINTERUPDATE: case WM_POINTERLEAVE: case WM_POINTERCAPTURECHANGED: { if (!m_bHandlePointer) { break; } // 只关心第一个触摸点 if (!IS_POINTER_PRIMARY_WPARAM(wParam)) { handled = true; break; } UINT32 pointerId = GET_POINTERID_WPARAM(wParam); POINTER_INPUT_TYPE type; if (!GetPointerTypeWrapper(pointerId, &type)) { return 0; } POINT pt = { 0 }; FLOAT pressure = 0.0f; switch (type) { case PT_TOUCH: POINTER_TOUCH_INFO touchInfo; if (!GetPointerTouchInfoWrapper(pointerId, &touchInfo)) { return 0; } pt = touchInfo.pointerInfo.ptPixelLocationRaw; pressure = (float)touchInfo.pressure / 1024; break; case PT_PEN: POINTER_PEN_INFO penInfo; if (!GetPointerPenInfoWrapper(pointerId, &penInfo)) { return 0; } pt = penInfo.pointerInfo.ptPixelLocationRaw; pressure = (float)penInfo.pressure / 1024; break; default: return 0; break; } ScreenToClient(m_hWnd, &pt); switch (uMsg) { case WM_POINTERDOWN: { if (m_pEventClick != NULL || m_pEventTouch != NULL) { handled = true; break; } ::SetFocus(m_hWnd); m_ptLastMousePos = pt; Control *pControl = FindControl(pt); if (pControl == NULL) break; if (pControl->GetWindow() != this) break; m_pEventPointer = pControl; pControl->SetFocus(); SetCapture(); pControl->HandleMessageTemplate(kEventPointDown, 0, lParam, 0, pt, pressure); // 如果控件不支持处理WM_POINTERUPDATE消息,则不设置handled,程序会进入WM_BUTTON处理流程 if (m_pEventPointer && m_pEventPointer->IsReceivePointerMsg()) { handled = true; } } break; case WM_POINTERUPDATE: if (m_pEventClick != NULL || m_pEventTouch != NULL) { handled = true; break; } m_ptLastMousePos = pt; // 如果没有按下,则不设置handled,程序会转换为WM_BUTTON类消息 if (m_pEventPointer == NULL) break; if (!HandleMouseEnterLeave(pt, wParam, lParam)) break; m_pEventPointer->HandleMessageTemplate(kEventPointMove, 0, 0, 0, pt, pressure); // 如果控件不支持处理WM_POINTERUPDATE消息,则不设置handled,程序会进入WM_MOUSEMOVE处理流程 if (m_pEventPointer && m_pEventPointer->IsReceivePointerMsg()) { handled = true; } break; case WM_POINTERUP: case WM_POINTERLEAVE: case WM_POINTERCAPTURECHANGED: m_ptLastMousePos = pt; // 如果没有按下,则不设置handled,程序会转换为WM_BUTTON类消息 ReleaseCapture(); if (m_pEventPointer == NULL) break; m_pEventPointer->HandleMessageTemplate(kEventPointUp, 0, lParam, 0, pt, pressure); // 如果控件不支持处理WM_POINTERUPDATE消息,则不设置handled,程序会进入WM_BUTTON处理流程 if (m_pEventPointer && m_pEventPointer->IsReceivePointerMsg()) { handled = true; } m_pEventPointer = NULL; break; default: break; } return 0; } break; case WM_SETFOCUS: { } break; case WM_KILLFOCUS: { Control *pControl = m_pEventClick ? m_pEventClick : NULL; pControl = m_pEventTouch ? m_pEventTouch : pControl; pControl = m_pEventPointer ? m_pEventPointer : pControl; POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) }; m_ptLastMousePos = pt; if (pControl == NULL || !pControl->IsNeedButtonUpWhenKillFocus()) break; ReleaseCapture(); pControl->HandleMessageTemplate(kEventMouseButtonUp, wParam, lParam, 0, pt); m_pEventClick = NULL; m_pEventTouch = NULL; m_pEventPointer = NULL; } break; case WM_CONTEXTMENU: { POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) }; ::ScreenToClient(m_hWnd, &pt); m_ptLastMousePos = pt; if (m_pEventClick == NULL) break; ReleaseCapture(); m_pEventClick->HandleMessageTemplate(kEventInternalMenu, wParam, (LPARAM)m_pEventClick, 0, pt); m_pEventClick = NULL; } break; case WM_CHAR: { if (m_pFocus == NULL) break; m_pFocus->HandleMessageTemplate(kEventChar, wParam, lParam, wParam); } break; case WM_KEYDOWN: { if (m_pFocus == NULL) break; m_pEventKey = m_pFocus; m_pFocus->HandleMessageTemplate(kEventKeyDown, wParam, lParam, wParam); } break; case WM_KEYUP: { if (m_pEventKey == NULL) break; m_pEventKey->HandleMessageTemplate(kEventKeyUp, wParam, lParam, wParam); m_pEventKey = NULL; } break; case WM_SETCURSOR: { if (LOWORD(lParam) != HTCLIENT) break; if (m_pEventClick != NULL || m_pEventTouch != NULL || m_pEventPointer != NULL) { handled = true; return 0; } POINT pt = { 0 }; ::GetCursorPos(&pt); ::ScreenToClient(m_hWnd, &pt); m_ptLastMousePos = pt; Control* pControl = FindControl(pt); if (pControl == NULL) break; //if( (pControl->GetControlFlags() & UIFLAG_SETCURSOR) == 0 ) break; pControl->HandleMessageTemplate(kEventSetCursor, wParam, lParam, 0, pt); } handled = true; return 0; case WM_NOTIFY: { LPNMHDR lpNMHDR = (LPNMHDR)lParam; if (lpNMHDR != NULL) return ::SendMessage(lpNMHDR->hwndFrom, OCM__BASE + uMsg, wParam, lParam); handled = true; return 0; } break; case WM_COMMAND: { if (lParam == 0) break; HWND hWndChild = (HWND)lParam; handled = true; return ::SendMessage(hWndChild, OCM__BASE + uMsg, wParam, lParam); } 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; handled = true; return ::SendMessage(hWndChild, OCM__BASE + uMsg, wParam, lParam); } break; default: break; } return 0; } LRESULT Window::CallWindowProc(UINT uMsg, WPARAM wParam, LPARAM lParam) { return ::CallWindowProc(m_OldWndProc, m_hWnd, uMsg, wParam, lParam); } bool Window::HandleMouseEnterLeave(const POINT &pt, WPARAM wParam, LPARAM lParam) { m_pNewHover = FindControl(pt); if (m_pNewHover != NULL && m_pNewHover->GetWindow() != this) return false;; if (m_pNewHover != m_pEventHover && m_pEventHover != NULL) { m_pEventHover->HandleMessageTemplate(kEventMouseLeave, 0, 0, 0, pt); m_pEventHover = NULL; if (m_hwndTooltip != NULL) ::SendMessage(m_hwndTooltip, TTM_TRACKACTIVATE, FALSE, (LPARAM)&m_ToolTip); } if (m_pNewHover != m_pEventHover && m_pNewHover != NULL) { m_pNewHover->HandleMessageTemplate(kEventMouseEnter, wParam, lParam, 0, pt); m_pEventHover = m_pNewHover; } return true; } Control* Window::GetFocus() const { return m_pFocus; } void Window::SetFocus(Control* pControl) { // Paint manager window has focus? HWND hFocusWnd = ::GetFocus(); if (hFocusWnd != m_hWnd && pControl != m_pFocus && pControl != NULL) ::SetFocus(m_hWnd); // Already has focus? if (pControl == m_pFocus) return; // Remove focus from old control if (m_pFocus != NULL) { m_pFocus->HandleMessageTemplate(kEventInternalKillFocus); Control* tmp = m_pFocus; m_pFocus = NULL; SendNotify(tmp, kEventKillFocus); } if (pControl == NULL) return; // Set focus to new control if (pControl != NULL && pControl->GetWindow() == this && pControl->IsVisible() && pControl->IsEnabled()) { m_pFocus = pControl; if (m_pFocus) { m_pFocus->HandleMessageTemplate(kEventInternalSetFocus); } if (m_pFocus) { SendNotify(m_pFocus, kEventSetFocus); } } } void Window::SetFocusNeeded(Control* pControl) { ::SetFocus(m_hWnd); if (pControl == NULL) return; if (m_pFocus != NULL) { m_pFocus->HandleMessageTemplate(kEventInternalKillFocus); SendNotify(m_pFocus, kEventKillFocus); m_pFocus = NULL; } FINDTABINFO info = { 0 }; info.pFocus = pControl; info.bForward = false; m_pFocus = nullptr; //m_pRoot->FindControl(__FindControlFromTab, &info, UIFIND_VISIBLE | UIFIND_ENABLED | UIFIND_ME_FIRST); m_bFocusNeeded = true; if (m_pRoot != NULL) m_pRoot->Arrange(); } void Window::KillFocus() { if (m_pFocus != NULL) { m_pFocus->HandleMessageTemplate(kEventInternalKillFocus); SendNotify(m_pFocus, kEventKillFocus); m_pFocus = NULL; } } void Window::SetCapture() { ::SetCapture(m_hWnd); m_bMouseCapture = true; } void Window::ReleaseCapture() { ::ReleaseCapture(); m_bMouseCapture = false; } bool Window::IsCaptureControl(const ui::Control* pControl) { return m_pEventClick == pControl || m_pEventTouch == pControl || m_pEventPointer == pControl; } bool Window::IsCaptured() const { return m_bMouseCapture; } ui::Control* Window::GetNewHover() { return m_pNewHover; } POINT Window::GetLastMousePos() const { return m_ptLastMousePos; } HWND Window::GetTooltipWindow() const { return m_hwndTooltip; } //bool Window::SetNextTabControl(bool bForward) //{ // // If we're in the process of restructuring the layout we can delay the // // focus calulation until the next repaint. // if( m_bIsArranged && bForward ) { // m_bFocusNeeded = true; // ::InvalidateRect(m_hWnd, NULL, FALSE); // return true; // } // // Find next/previous tabbable control // FINDTABINFO info1 = { 0 }; // info1.pFocus = m_pFocus; // info1.bForward = bForward; // Control* pControl = m_pRoot->FindControl(__FindControlFromTab, &info1, UIFIND_VISIBLE | UIFIND_ENABLED | UIFIND_ME_FIRST); // if( pControl == NULL ) { // if( bForward ) { // // Wrap around // FINDTABINFO info2 = { 0 }; // info2.pFocus = bForward ? NULL : info1.pLast; // info2.bForward = bForward; // pControl = m_pRoot->FindControl(__FindControlFromTab, &info2, UIFIND_VISIBLE | UIFIND_ENABLED | UIFIND_ME_FIRST); // } // else { // pControl = info1.pLast; // } // } // if( pControl != NULL ) SetFocus(pControl); // m_bFocusNeeded = false; // return true; //} Control* Window::GetRoot() const { return m_pRoot; } void Window::SetArrange(bool bArrange) { m_bIsArranged = bArrange; } void Window::AddDelayedCleanup(Control* pControl) { pControl->SetWindow(this, NULL, false); m_aDelayedCleanup.push_back(pControl); ::PostMessage(m_hWnd, WM_APP + 1, 0, 0L); } Control* Window::FindControl(POINT pt) const { ASSERT(m_pRoot); Control* pFindedControl = NULL; for (auto it : m_aIControlFromPointFinder) { if ((pFindedControl = it->FindControlFromPoint(pt)) != NULL) return pFindedControl; } return m_pRoot->FindControl(__FindControlFromPoint, &pt, UIFIND_VISIBLE | UIFIND_HITTEST | UIFIND_TOP_FIRST); } Control* Window::FindControl(const std::wstring& strName) const { ASSERT(m_pRoot); Control* pFindedControl = NULL; auto it = m_mNameHash.find(strName); if (it != m_mNameHash.end()) { pFindedControl = it->second; } return pFindedControl; } Control* Window::FindSubControlByPoint(Control* pParent, POINT pt) const { if (pParent == NULL) pParent = GetRoot(); ASSERT(pParent); return pParent->FindControl(__FindControlFromPoint, &pt, UIFIND_VISIBLE | UIFIND_HITTEST | UIFIND_TOP_FIRST); } Control* Window::FindSubControlByName(Control* pParent, const std::wstring& strName) const { if (pParent == NULL) pParent = GetRoot(); ASSERT(pParent); return pParent->FindControl(__FindControlFromName, (LPVOID)strName.c_str(), UIFIND_ALL); } Control* Window::FindSubControlByClass(Control* pParent, const type_info& typeinfo, int iIndex) { if (pParent == NULL) pParent = GetRoot(); ASSERT(pParent); m_aFoundControls.clear(); m_aFoundControls.resize(iIndex + 1); return pParent->FindControl(__FindControlFromClass, (LPVOID)&typeinfo, UIFIND_ALL); } std::vector* Window::FindSubControlsByClass(Control* pParent, const type_info& typeinfo) { if (pParent == NULL) pParent = GetRoot(); ASSERT(pParent); m_aFoundControls.clear(); pParent->FindControl(__FindControlsFromClass, (LPVOID)&typeinfo, UIFIND_ALL); return &m_aFoundControls; } std::vector* Window::GetSubControlsByClass() { return &m_aFoundControls; } bool Window::SendNotify(EventType eventType, WPARAM wParam, LPARAM lParam) { EventArgs msg; msg.pSender = nullptr; msg.Type = eventType; msg.ptMouse = GetMousePos(); msg.dwTimestamp = ::GetTickCount(); msg.wParam = wParam; msg.lParam = lParam; auto callback = OnEvent.find(msg.Type); if (callback != OnEvent.end()) { callback->second(&msg); } callback = OnEvent.find(kEventAll); if (callback != OnEvent.end()) { callback->second(&msg); } return true; } bool Window::SendNotify(Control* pControl, EventType msgType, WPARAM wParam, LPARAM lParam) { EventArgs msg; msg.pSender = pControl; msg.Type = msgType; msg.ptMouse = GetMousePos(); msg.dwTimestamp = ::GetTickCount(); msg.wParam = wParam; msg.lParam = lParam; pControl->HandleMessageTemplate(msg); return true; } HDC Window::GetPaintDC() const { return m_hDcPaint; } ui::IRenderContext* Window::GetRenderContext() const { return m_renderContext.get(); } void Window::Invalidate(const UiRect& rcItem) { ::InvalidateRect(m_hWnd, &rcItem, FALSE); // Invalidating a layered window will not trigger a WM_PAINT message, // thus we have to post WM_PAINT by ourselves. if (m_bIsLayeredWindow) { ::PostMessage(m_hWnd, WM_PAINT, (LPARAM)&rcItem, (WPARAM)FALSE); } } void Window::Paint() { if (::IsIconic(m_hWnd) || !m_pRoot) { PAINTSTRUCT ps = { 0 }; ::BeginPaint(m_hWnd, &ps); ::EndPaint(m_hWnd, &ps); return; } if (m_bIsArranged && m_pRoot->IsArranged() && (m_pRoot->GetFixedWidth() == DUI_LENGTH_AUTO || m_pRoot->GetFixedHeight() == DUI_LENGTH_AUTO)) { CSize maxSize(99999, 99999); CSize needSize = m_pRoot->EstimateSize(maxSize); if (needSize.cx < m_pRoot->GetMinWidth()) needSize.cx = m_pRoot->GetMinWidth(); if (m_pRoot->GetMaxWidth() >= 0 && needSize.cx > m_pRoot->GetMaxWidth()) needSize.cx = m_pRoot->GetMaxWidth(); if (needSize.cy < m_pRoot->GetMinHeight()) needSize.cy = m_pRoot->GetMinHeight(); if (needSize.cy > m_pRoot->GetMaxHeight()) needSize.cy = m_pRoot->GetMaxHeight(); UiRect rect; ::GetWindowRect(m_hWnd, &rect); ::MoveWindow(m_hWnd, rect.left, rect.top, needSize.cx, needSize.cy, TRUE); } // Should we paint? UiRect rcPaint; if (!::GetUpdateRect(m_hWnd, &rcPaint, FALSE) && !m_bFirstLayout) { return; } UiRect rcClient; ::GetClientRect(m_hWnd, &rcClient); UiRect rcWindow; ::GetWindowRect(m_hWnd, &rcWindow); //使用层窗口时,窗口部分在屏幕外时,获取到的无效区域仅仅是屏幕内的部分,这里做修正处理 if (m_bIsLayeredWindow) { int xScreen = GetSystemMetrics(SM_XVIRTUALSCREEN); int yScreen = GetSystemMetrics(SM_YVIRTUALSCREEN); int cxScreen = GetSystemMetrics(SM_CXVIRTUALSCREEN); int cyScreen = GetSystemMetrics(SM_CYVIRTUALSCREEN); if (rcWindow.left < xScreen && rcWindow.left + rcPaint.left == xScreen) rcPaint.left = rcClient.left; if (rcWindow.top < yScreen && rcWindow.top + rcPaint.top == yScreen) rcPaint.top = rcClient.top; if (rcWindow.right > cxScreen && rcWindow.left + rcPaint.right == xScreen + cxScreen) rcPaint.right = rcClient.right; if (rcWindow.bottom > cyScreen && rcWindow.top + rcPaint.bottom == yScreen + cyScreen) rcPaint.bottom = rcClient.bottom; } PAINTSTRUCT ps = { 0 }; ::BeginPaint(m_hWnd, &ps); if (m_bIsArranged) { m_bIsArranged = false; if (!::IsRectEmpty(&rcClient)) { if (m_pRoot->IsArranged()) { m_pRoot->SetPos(rcClient); } else { Control* pControl = NULL; while ((pControl = m_pRoot->FindControl(__FindControlFromUpdate, NULL, UIFIND_VISIBLE | UIFIND_ME_FIRST)) != nullptr) { pControl->SetPos(pControl->GetPos()); } } if (m_bFirstLayout) { m_bFirstLayout = false; OnInitLayout(); } } } int width = rcClient.right - rcClient.left; int height = rcClient.bottom - rcClient.top; if (m_renderContext->Resize(width, height)) { // 使用阴影后会修补alpha通道,就不需要设置透明属性了 m_renderContext->SetRenderTransparent(!m_shadow.IsShadowAttached()); rcPaint.left = 0; rcPaint.top = 0; rcPaint.right = width; rcPaint.bottom = height; } // 去掉alpha通道 if (m_bIsLayeredWindow) { m_renderContext->ClearAlpha(rcPaint); } // 绘制 AutoClip rectClip(m_renderContext.get(), rcPaint, true); CPoint ptOldWindOrg = m_renderContext->OffsetWindowOrg(m_renderOffset); m_pRoot->Paint(m_renderContext.get(), rcPaint); m_pRoot->PaintChild(m_renderContext.get(), rcPaint); m_renderContext->SetWindowOrg(ptOldWindOrg); // alpha修复 if (m_bIsLayeredWindow) { if (m_shadow.IsShadowAttached() && m_renderOffset.x == 0 && m_renderOffset.y == 0) { //补救由于Gdi绘制造成的alpha通道为0 UiRect rcNewPaint = rcPaint; rcNewPaint.Intersect(m_pRoot->GetPaddingPos()); UiRect rcRootPadding = m_pRoot->GetLayout()->GetPadding(); //考虑圆角 rcRootPadding.left += 1; rcRootPadding.top += 1; rcRootPadding.right += 1; rcRootPadding.bottom += 1; m_renderContext->RestoreAlpha(rcNewPaint, rcRootPadding); } else { UiRect rcAlphaFixCorner = GetAlphaFixCorner(); if (rcAlphaFixCorner.left > 0 || rcAlphaFixCorner.top > 0 || rcAlphaFixCorner.right > 0 || rcAlphaFixCorner.bottom > 0) { UiRect rcNewPaint = rcPaint; UiRect rcRootPaddingPos = m_pRoot->GetPaddingPos(); rcRootPaddingPos.Deflate(rcAlphaFixCorner); rcNewPaint.Intersect(rcRootPaddingPos); UiRect rcRootPadding; m_renderContext->RestoreAlpha(rcNewPaint, rcRootPadding); } } } // 渲染到窗口 if (m_bIsLayeredWindow) { CPoint pt(rcWindow.left, rcWindow.top); CSize szWindow(rcClient.right - rcClient.left, rcClient.bottom - rcClient.top); CPoint ptSrc; BLENDFUNCTION bf = { AC_SRC_OVER, 0, m_nAlpha, AC_SRC_ALPHA }; ::UpdateLayeredWindow(m_hWnd, NULL, &pt, &szWindow, m_renderContext->GetDC(), &ptSrc, 0, &bf, ULW_ALPHA); } else { ::BitBlt(ps.hdc, rcPaint.left, rcPaint.top, rcPaint.GetWidth(), rcPaint.GetHeight(), m_renderContext->GetDC(), rcPaint.left, rcPaint.top, SRCCOPY); } ::EndPaint(m_hWnd, &ps); } void Window::SetAlpha(int nAlpha) { ASSERT(nAlpha >= 0 && nAlpha <= 255); if (m_pRoot) { m_nAlpha = nAlpha; Invalidate(m_pRoot->GetPos()); } } bool Window::IsRenderTransparent() const { return m_renderContext->IsRenderTransparent(); } bool Window::SetRenderTransparent(bool bCanvasTransparent) { return m_renderContext->SetRenderTransparent(bCanvasTransparent); } void Window::SetRenderOffset(CPoint renderOffset) { if (m_pRoot) { m_renderOffset = renderOffset; Invalidate(m_pRoot->GetPos()); } } void Window::SetRenderOffsetX(int renderOffsetX) { if (m_pRoot) { m_renderOffset.x = renderOffsetX; Invalidate(m_pRoot->GetPos()); } } void Window::SetRenderOffsetY(int renderOffsetY) { if (m_pRoot) { m_renderOffset.y = renderOffsetY; Invalidate(m_pRoot->GetPos()); } } void Window::OnInitLayout() { if (m_pRoot && IsWindowsVistaOrGreater()) { AnimationPlayer* animationArgs = m_pRoot->GetAnimationManager().SetFadeAlpha(true); m_nAlpha = 0; std::function playCallback = nbase::Bind(&Window::SetAlpha, this, std::placeholders::_1); animationArgs->SetCallback(playCallback); m_pRoot->SetVisible(true); } //auto fromLeftAnimationPlayer = m_pRoot->GetAnimationManager().GetAnimationPlayer(kAnimationInoutXFromLeft); //if (!fromLeftAnimationPlayer && m_shadow.IsShadowAttached()) { // Control* tmpRoot = m_pRoot->GetItemAt(0); // fromLeftAnimationPlayer = tmpRoot->GetAnimationManager().GetAnimationPlayer(kAnimationInoutXFromLeft); //} //auto fromRightAnimationPlayer = m_pRoot->GetAnimationManager().GetAnimationPlayer(kAnimationInoutXFromRight); //if (!fromRightAnimationPlayer && m_shadow.IsShadowAttached()) { // Control* tmpRoot = m_pRoot->GetItemAt(0); // fromRightAnimationPlayer = tmpRoot->GetAnimationManager().GetAnimationPlayer(kAnimationInoutXFromRight); //} //AnimationPlayer* fadeInoutXPlayer = nullptr; //if (fromLeftAnimationPlayer) { // fadeInoutXPlayer = m_pRoot->GetAnimationManager().SetFadeInOutX(true, false); //} //if (fromRightAnimationPlayer) { // fadeInoutXPlayer = m_pRoot->GetAnimationManager().SetFadeInOutX(true, true); //} //if (fadeInoutXPlayer) { // std::function playCallback = nbase::Bind(&Window::SetRenderOffsetX, this, std::placeholders::_1); // fadeInoutXPlayer->SetCallback(playCallback); // fadeInoutXPlayer->SetMaxTotalMillSeconds(250); // m_pRoot->SetVisible(true); //} } Control* CALLBACK Window::__FindControlFromNameHash(Control* pThis, LPVOID pData) { Window* pManager = static_cast(pData); std::wstring sName = pThis->GetName(); if( sName.empty() ) return NULL; // Add this control to the hash list pManager->m_mNameHash[sName] = pThis; return NULL; // Attempt to add all controls } Control* CALLBACK Window::__FindControlFromCount(Control* /*pThis*/, LPVOID pData) { int* pnCount = static_cast(pData); (*pnCount)++; return NULL; // Count all controls } Control* CALLBACK Window::__FindControlFromPoint(Control* pThis, LPVOID pData) { LPPOINT pPoint = static_cast(pData); UiRect pos = pThis->GetPos(); return ::PtInRect(&pos, *pPoint) ? pThis : NULL; } //Control* CALLBACK Window::__FindControlFromTab(Control* pThis, LPVOID pData) //{ // FINDTABINFO* pInfo = static_cast(pData); // if( pInfo->pFocus == pThis ) { // if( pInfo->bForward ) pInfo->bNextIsIt = true; // return pInfo->bForward ? NULL : pInfo->pLast; // } // if( (pThis->GetControlFlags() & UIFLAG_TABSTOP) == 0 ) return NULL; // pInfo->pLast = pThis; // if( pInfo->bNextIsIt ) return pThis; // if( pInfo->pFocus == NULL ) return pThis; // return NULL; // Examine all controls //} //Control* CALLBACK Window::__FindControlFromShortcut(Control* pThis, LPVOID pData) //{ // if( !pThis->IsVisible() ) return NULL; // FINDSHORTCUT* pFS = static_cast(pData); // if( pFS->ch == toupper(pThis->GetShortcut()) ) pFS->bPickNext = true; // if( typeid(*pThis) == typeid(Label) ) return NULL; // Labels never get focus! // return pFS->bPickNext ? pThis : NULL; //} Control* CALLBACK Window::__FindControlFromUpdate(Control* pThis, LPVOID pData) { return pThis->IsArranged() ? pThis : NULL; } Control* CALLBACK Window::__FindControlFromName(Control* pThis, LPVOID pData) { LPCTSTR pstrName = static_cast(pData); const std::wstring& sName = pThis->GetName(); if( sName.empty() ) return NULL; return (_tcsicmp(sName.c_str(), pstrName) == 0) ? pThis : NULL; } Control* CALLBACK Window::__FindControlFromClass(Control* pThis, LPVOID pData) { type_info* pTypeInfo = static_cast(pData); std::vector* pFoundControls = pThis->GetWindow()->GetSubControlsByClass(); if( typeid(*pThis) == *pTypeInfo ) { int iIndex = -1; while( (*pFoundControls)[++iIndex] != NULL ) ; if( (std::size_t)iIndex < pFoundControls->size() ) (*pFoundControls)[iIndex] = pThis; } if( (*pFoundControls)[pFoundControls->size() - 1] != NULL ) return pThis; return NULL; } Control* CALLBACK Window::__FindControlsFromClass(Control* pThis, LPVOID pData) { type_info* pTypeInfo = static_cast(pData); if( typeid(*pThis) == *pTypeInfo ) pThis->GetWindow()->GetSubControlsByClass()->push_back(pThis); return NULL; } } // namespace ui