Files
EzUI/sources/Window.cpp

1106 lines
29 KiB
C++
Raw Permalink Normal View History

2026-01-24 22:42:46 +08:00
#include "Window.h"
#include "TabLayout.h"
#include "IFrame.h"
#include <CommCtrl.h>
namespace ezui {
Window::Window(int width, int height, HWND owner, DWORD dStyle, DWORD dwExStyle)
{
Init(width, height, owner, dStyle, dwExStyle);//设置基本数据
}
Window::~Window()
{
if (::IsWindow(Hwnd())) {
HWND hwnd = m_hWnd;//复制一份
m_hWnd = NULL;//置零
::DestroyWindow(hwnd);
}
if (m_publicData) {
delete m_publicData;
}
if (m_frame) {
delete m_frame;
}
}
void Window::Init(int width, int height, HWND owner, DWORD dStyle, DWORD dwExStyle)
{
this->m_publicData = new WindowData;
Rect rect(0, 0, width, height);
POINT cursorPos;
::GetCursorPos(&cursorPos);
for (auto& it : ezui::__EzUI__MonitorInfos) {
Rect rect = it.Rect;
rect.Width = it.Physical.Width;
rect.Height = it.Physical.Height;
if (rect.Contains(cursorPos.x, cursorPos.y)) {
rect.X = rect.X;
rect.Y = rect.Y;
this->m_publicData->Scale = it.Scale;
break;
}
}
rect.Scale(this->m_publicData->Scale);
//绑定消息过程
m_publicData->WndProc = [this](HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) ->LRESULT {
return this->WndProc(uMsg, wParam, lParam);
};
//创建窗口
m_hWnd = ::CreateWindowExW(dwExStyle | WS_EX_ACCEPTFILES, EZUI_WINDOW_CLASS, EZUI_WINDOW_CLASS, WS_CLIPSIBLINGS | WS_CLIPCHILDREN | dStyle,
rect.X, rect.Y, rect.Width, rect.Height, owner, NULL, ezui::__EzUI__HINSTANCE, NULL);
//创建主Frame
m_frame = new IFrame;
m_frame->SetHwnd(m_hWnd);
NONCLIENTMETRICS ncm = {};
ncm.cbSize = sizeof(NONCLIENTMETRICS);
if (SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICS), &ncm, 0)) {
m_frame->Style.FontFamily = ncm.lfMessageFont.lfFaceName;
// 获取菜单字体的高度(点数)
int fontHeightInPoints = std::abs(ncm.lfMenuFont.lfHeight) / this->GetScale() + 0.5;
m_frame->Style.FontSize = fontHeightInPoints;
}
//获取系统默认字体颜色
auto sysForeColor = GetSysColor(COLOR_WINDOWTEXT);
m_frame->Style.ForeColor = Color(GetRValue(sysForeColor), GetGValue(sysForeColor), GetBValue(sysForeColor));
//非layered窗口 必须设置背景色
if (((::GetWindowLongPtr(Hwnd(), GWL_EXSTYLE) & WS_EX_LAYERED) == 0)) {
auto sysBackColor = GetSysColor(COLOR_WINDOW);
m_frame->Style.BackColor = Color(GetRValue(sysBackColor), GetGValue(sysBackColor), GetBValue(sysBackColor));
}
//主Frame的OnNotify函数转到此窗口
m_frame->NotifyHandler = [this](Control* sender, EventArgs& args) {
this->OnNotify(sender, args);
};
m_publicData->Window = this;
m_publicData->MoveWindow = [this]() {
this->MoveWindow();
};
m_publicData->TitleMoveWindow = [this]() {
this->TitleMoveWindow();
};
if (owner) {
this->CenterToWindow(owner);
}
else {
this->CenterToScreen();
}
if ((dwExStyle & WS_EX_LAYERED) != WS_EX_LAYERED) {
m_publicData->InvalidateRect = [this](const Rect& rect)->void {
RECT r;
r.left = rect.GetLeft();
r.top = rect.GetTop();
r.right = rect.GetRight();
r.bottom = rect.GetBottom();
::InvalidateRect(Hwnd(), &r, FALSE);
};
m_publicData->Refresh = [this]()->void {
UpdateWindow(Hwnd());
};
}
m_publicData->CleanControl = [this](Control* delControl)->void {
if (m_focusControl == delControl) {
m_focusControl = NULL;
}
if (m_inputControl == delControl) {
m_inputControl = NULL;
}
};
//绑定窗口的数据
UI_SET_USERDATA(Hwnd(), this->m_publicData);
//触发一些逻辑
::SendMessage(Hwnd(), WM_MOVE, NULL, MAKELPARAM(rect.X, rect.Y));
::SendMessage(Hwnd(), WM_SIZE, NULL, MAKELPARAM(rect.Width, rect.Height));
}
Control* Window::FindControl(const UIString& objectName)
{
Control* layout = this->m_frame->GetLayout();
if (layout) {
return layout->FindControl(objectName);
}
return NULL;
}
HWND Window::Hwnd()
{
return m_hWnd;
}
int Window::X() {
return GetWindowRect().X;
}
int Window::Y() {
return GetWindowRect().Y;
}
int Window::Width() {
return GetWindowRect().Width;
}
int Window::Height() {
return GetWindowRect().Height;
}
const Rect& Window::GetWindowRect()
{
RECT rect;
::GetWindowRect(Hwnd(), &rect);
m_rect = { rect.left,rect.top,rect.right - rect.left,rect.bottom - rect.top };
return m_rect;
}
const Rect& Window::GetClientRect()
{
RECT rect;
::GetClientRect(Hwnd(), &rect);
m_rectClient = { rect.left,rect.top,rect.right - rect.left,rect.bottom - rect.top };
return m_rectClient;
}
void Window::SetText(const UIString& text)
{
::SetWindowTextW(Hwnd(), text.unicode().c_str());
}
UIString Window::GetText() {
WCHAR buf[513]{ 0 };
::GetWindowTextW(Hwnd(), buf, 512);
return buf;
}
void Window::SetTopMost(bool top)
{
::SetWindowPos(Hwnd(), top ? HWND_TOPMOST : HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
}
bool Window::IsTopMost()
{
LONG_PTR exStyle = ::GetWindowLongPtr(Hwnd(), GWL_EXSTYLE);
bool isTop = ((exStyle & WS_EX_TOPMOST) == WS_EX_TOPMOST);
return isTop;
}
bool Window::IsFullScreen() {
//判断是否全屏
MonitorInfo monitorInfo;
ezui::GetMontior(&monitorInfo, Hwnd());//获取窗口所在的监视器位置
auto rect = this->GetWindowRect();
bool isFullScreen = (monitorInfo.Rect.X == rect.X && monitorInfo.Rect.Y == rect.Y && monitorInfo.Rect.Width == rect.Width && monitorInfo.Rect.Height == rect.Height);
if (isFullScreen)
{
return true;
}
return false;
}
bool Window::IsMinimized()
{
return ::IsIconic(Hwnd());
}
bool Window::IsMaximized() {
return ::IsZoomed(Hwnd());
}
void Window::SetSize(const Size& size) {
const Rect& rect = GetWindowRect();
this->SetRect(Rect(rect.X, rect.Y, size.Width, size.Height));
}
void Window::SetLocation(const Point& pt) {
const Rect& rect = GetWindowRect();
this->SetRect(Rect(pt.X, pt.Y, rect.Width, rect.Height));
}
void Window::SetRect(const Rect& rect)
{
::MoveWindow(Hwnd(), rect.X, rect.Y, rect.Width, rect.Height, TRUE);
}
void Window::SetMiniSize(const Size& size)
{
m_miniSize = size;
m_miniSize.Scale(this->m_publicData->Scale);
}
void Window::SetMaxSize(const Size& size)
{
m_maxSize = size;
m_maxSize.Scale(this->m_publicData->Scale);
}
void Window::SetFixedSize(const Size& size)
{
this->SetMiniSize(size);
this->SetMaxSize(size);
this->SetSize(size);
}
void Window::SetIcon(HICON icon)
{
::SendMessage(Hwnd(), WM_SETICON, ICON_SMALL, (LPARAM)icon);
}
void Window::SetLayout(ezui::Control* layout) {
m_frame->SetLayout(layout);
}
Control* Window::GetLayout()
{
return this->m_frame->GetLayout();
}
void Window::LoadXml(const UIString& fileName) {
m_frame->LoadXml(fileName);
}
void Window::LoadXml(const char* fileData, size_t fileSize) {
m_frame->LoadXml(fileData, fileSize);
}
void Window::Close(int code) {
m_closeCode = code;
::SendMessage(Hwnd(), WM_CLOSE, 0, 0);
}
void Window::Show()
{
::ShowWindow(Hwnd(), SW_SHOW);
}
void Window::Show(int cmdShow)
{
::ShowWindow(Hwnd(), cmdShow);
}
void Window::ShowNormal()
{
::ShowWindow(Hwnd(), SW_SHOWNORMAL);
}
void Window::ShowMinimized() {
::ShowWindow(Hwnd(), SW_MINIMIZE);
}
void Window::ShowMaximized() {
::ShowWindow(Hwnd(), SW_MAXIMIZE);
}
void Window::ShowFullScreen()
{
::ShowWindow(Hwnd(), SW_MAX);
MonitorInfo monitorInfo;
ezui::GetMontior(&monitorInfo, Hwnd());
SetRect(monitorInfo.Rect);
SetTopMost(true);
::ShowWindow(Hwnd(), SW_SHOW);
}
int Window::ShowModal(bool disableOwner)
{
//此处代码不能随意更改 解决关闭窗口时,owner窗口闪烁问题
if (disableOwner) {
m_ownerWnd = ::GetWindow(Hwnd(), GW_OWNER);
}
if (m_ownerWnd) {
::EnableWindow(m_ownerWnd, FALSE);
}
this->Show();
::MSG msg{ 0 };
while (Hwnd() && ::GetMessage(&msg, NULL, 0, 0))
{
::TranslateMessage(&msg);
::DispatchMessage(&msg);
}
if (m_ownerWnd && ::IsWindow(m_ownerWnd)) {
::SetActiveWindow(m_ownerWnd);
m_ownerWnd = NULL;
}
return m_closeCode;
}
void Window::Hide() {
::ShowWindow(Hwnd(), SW_HIDE);
}
bool Window::IsVisible() {
return ::IsWindowVisible(Hwnd()) ? true : false;
}
void Window::SetVisible(bool flag) {
if (flag) {
::ShowWindow(Hwnd(), SW_RESTORE);
::SetForegroundWindow(Hwnd());
}
else {
Hide();
}
}
void Window::Invalidate()
{
m_publicData->InvalidateRect(this->GetClientRect());
}
void Window::Refresh()
{
m_publicData->Refresh();
}
void Window::CenterToScreen()
{
MonitorInfo monitorInfo;
ezui::GetMontior(&monitorInfo);
int x = monitorInfo.WorkRect.X;
int y = monitorInfo.WorkRect.Y;
int sw = monitorInfo.WorkRect.Width;//当前工作区域的宽
int sh = monitorInfo.WorkRect.Height;//当前工作区域的高
Rect rect = this->GetWindowRect();
int width = rect.Width;
int height = rect.Height;
//基于屏幕的中心点
rect.X = x + (sw - width) / 2.0f + 0.5;//保证左右居中
rect.Y = y + (sh - height) / 2.0f + 0.5;//保证上下居中
//移动窗口
SetRect({ rect.X, rect.Y, rect.Width, rect.Height });
}
void Window::CenterToWindow(HWND wnd)
{
if (wnd == NULL) {
wnd = ::GetWindow(Hwnd(), GW_OWNER);
}
if (wnd == NULL) {
wnd = ::GetParent(Hwnd());
}
if (wnd == NULL) {
return;
}
Rect rect = this->GetWindowRect();
int width = rect.Width;
int height = rect.Height;
//基于父窗口的中心点
RECT ownerRECT;
::GetWindowRect(wnd, &ownerRECT);
int onwerWidth = ownerRECT.right - ownerRECT.left;
int onwerHeight = ownerRECT.bottom - ownerRECT.top;
if (width > 0 && height > 0 && onwerWidth > 0 && onwerHeight > 0) {
rect.X = ownerRECT.left + (onwerWidth - width) / 2.0f + 0.5;
rect.Y = ownerRECT.top + (onwerHeight - height) / 2.0f + 0.5;
//移动窗口
SetRect({ rect.X, rect.Y, rect.Width, rect.Height });
}
}
void Window::SetFocus(Control* ctl)
{
if (ctl == NULL) {
return;
}
if (m_inputControl) {
KillFocusEventArgs args(ctl);
this->SendEvent(m_inputControl, args);
}
FocusEventArgs args(m_inputControl);
this->SendEvent(ctl, args);
m_inputControl = ctl;
m_focusControl = ctl;
}
Image* Window::Attach(Image* img)
{
m_imgs.Add(img);
return img;
}
void Window::Detach(Image* img)
{
m_imgs.Remove(img);
}
HWND Window::GetShadowHwnd()
{
return NULL;
}
WindowData* Window::GetPublicData()
{
return m_publicData;
}
LRESULT Window::WndProc(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
//if (WM_COMMAND == uMsg) {
// //windows子控件消息
//}
switch (uMsg)
{
case WM_SETFOCUS:
{
this->OnFocus((HWND)wParam);
break;
}
case WM_KILLFOCUS:
{
this->OnKillFocus((HWND)wParam);
break;
}
case WM_GETMINMAXINFO:
{
MINMAXINFO* pMMInfo = (MINMAXINFO*)lParam;
if (!m_miniSize.Empty()) {
pMMInfo->ptMinTrackSize.x = m_miniSize.Width;
pMMInfo->ptMinTrackSize.y = m_miniSize.Height;
}
if (!m_maxSize.Empty()) {
pMMInfo->ptMaxTrackSize.x = m_maxSize.Width;
pMMInfo->ptMaxTrackSize.y = m_maxSize.Height;
break;
}
MONITORINFO monitor;
monitor.cbSize = sizeof(monitor);
::GetMonitorInfo(::MonitorFromWindow(Hwnd(), MONITOR_DEFAULTTOPRIMARY), &monitor);
//是否为主显示器
if ((monitor.dwFlags & MONITORINFOF_PRIMARY) == MONITORINFOF_PRIMARY) {
//保证窗口在最大化的时候始终在工作区 不会遮挡任务栏
RECT& rcWork = monitor.rcWork;
pMMInfo->ptMaxPosition.x = rcWork.left;
pMMInfo->ptMaxPosition.y = rcWork.top;
pMMInfo->ptMaxSize.x = rcWork.right - rcWork.left;
pMMInfo->ptMaxSize.y = rcWork.bottom - rcWork.top;
}
else {
//获取主显示器
HMONITOR hMonitor = MonitorFromWindow(NULL, MONITOR_DEFAULTTOPRIMARY);
MONITORINFO monitorInfo;
monitorInfo.cbSize = sizeof(monitorInfo);
::GetMonitorInfo(hMonitor, &monitorInfo);
RECT& rcWork = monitorInfo.rcMonitor;
pMMInfo->ptMaxPosition.x = rcWork.left;
pMMInfo->ptMaxPosition.y = rcWork.top;
pMMInfo->ptMaxSize.x = rcWork.right - rcWork.left;
pMMInfo->ptMaxSize.y = rcWork.bottom - rcWork.top;
}
//Debug::Log("%d %d %d %d", pMMInfo->ptMaxPosition.x, pMMInfo->ptMaxPosition.y, pMMInfo->ptMaxSize.x, pMMInfo->ptMaxSize.y);
break;
}
case WM_CHAR: {
OnKeyChar(wParam, lParam);
break;
}
case WM_IME_STARTCOMPOSITION://
{
if (m_inputControl) {
HIMC hIMC = ImmGetContext(Hwnd());
Rect rect = m_inputControl->GetCareRect();
Rect inputRect = m_inputControl->GetClientRect();
int x = inputRect.X + rect.X;
int y = inputRect.Y + rect.Y;
auto winRect = this->GetWindowRect();
if (x >= winRect.Width) {
x = winRect.Width - 1;
}
if (y >= winRect.Height) {
y = winRect.Height - 1;
}
COMPOSITIONFORM cpf = {};
cpf.dwStyle = CFS_POINT;
cpf.ptCurrentPos.x = x;
cpf.ptCurrentPos.y = y;
ImmSetCompositionWindow(hIMC, &cpf);
ImmReleaseContext(Hwnd(), hIMC);
}
break;
}
case WM_IME_COMPOSITION:
{
if (lParam & GCS_RESULTSTR) {
HIMC hImc = ImmGetContext(Hwnd());
if (hImc) {
LONG bytes = ImmGetCompositionStringW(hImc, GCS_RESULTSTR, NULL, 0);
if (bytes > 0) {
std::wstring committed;
committed.resize(bytes / sizeof(wchar_t) + 1);
ImmGetCompositionStringW(hImc, GCS_RESULTSTR, (WCHAR*)committed.data(), bytes);
for (auto& it : committed) {
OnKeyChar((WPARAM)it, NULL);
}
}
ImmReleaseContext(Hwnd(), hImc);
return 0;
}
}
break;
}
case WM_IME_ENDCOMPOSITION: {
break;
}
case WM_ERASEBKGND: {
break;
}
case WM_DISPLAYCHANGE: {
auto width = LOWORD(lParam);
auto height = HIWORD(lParam);;
break;
}
case WM_DPICHANGED:
{
int dpi = HIWORD(wParam);
//新的缩放比
FLOAT systemScale = (float)dpi / USER_DEFAULT_SCREEN_DPI;
RECT* const prcNewWindow = (RECT*)lParam;
int newX = prcNewWindow->left;
int newY = prcNewWindow->top;
int newWidth = prcNewWindow->right - prcNewWindow->left;
int newHeight = prcNewWindow->bottom - prcNewWindow->top;
this->OnDpiChange(systemScale, Rect(newX, newY, newWidth, newHeight));
return 0;
}
case WM_PAINT:
{
PAINTSTRUCT pst;
HDC winHDC = ::BeginPaint(Hwnd(), &pst);
RECT& r = pst.rcPaint;
Rect rePaintRect{ r.left,r.top,r.right - r.left, r.bottom - r.top };
this->DoPaint(winHDC, rePaintRect);
::EndPaint(Hwnd(), &pst);
return 0;
}
case WM_NOTIFY: {
//NMHDR* pNMHDR = reinterpret_cast<NMHDR*>(lParam);
//if (pNMHDR->hwndFrom == _hWndTips && pNMHDR->code==) {
// NMTTDISPINFO* pDispInfo = reinterpret_cast<NMTTDISPINFO*>(lParam);
// // 在这里更改提示文字
// pDispInfo->szText = const_cast<TCHAR*>(L"新的提示文字");
// return true;
//}
break;
}
case WM_SHOWWINDOW: {
//窗口即将被隐藏
if (wParam == FALSE) {
OnMouseLeave();//触发鼠标从窗口中移出事件
Refresh();//前先刷新一下窗口中的画面(清除残留画面)
}
break;
}
case WM_WINDOWPOSCHANGED: {
WINDOWPOS* wPos = (WINDOWPOS*)(void*)lParam;
if ((wPos->flags & SWP_NOCOPYBITS) == SWP_NOCOPYBITS) {
//丢弃工作区的整个内容。 如果未指定此标志,则会在调整或重新定位窗口后保存并复制回工作区的有效内容。
this->Invalidate();
}
if ((wPos->flags & SWP_SHOWWINDOW) == SWP_SHOWWINDOW) {
this->Invalidate();
}
break;
}
case WM_MOVE: {
if (IsMinimized()) {
//窗口最小化的时候不做处理
break;
}
int xPos = (int)(short)LOWORD(lParam); // horizontal position
int yPos = (int)(short)HIWORD(lParam); // vertical position
m_rect.X = xPos;
m_rect.Y = yPos;
OnMove(m_rect.GetLocation());
break;
}
case WM_SIZE: {
if (IsMinimized()) {
//窗口最小化的时候不做处理
break;
}
UINT width = LOWORD(lParam);
UINT height = HIWORD(lParam);
m_rect.Width = width;
m_rect.Height = height;
//获取客户区的矩形
RECT rect;
::GetClientRect(Hwnd(), &rect);
m_rectClient.X = rect.left;
m_rectClient.Y = rect.top;
m_rectClient.Width = rect.right - rect.left;
m_rectClient.Height = rect.bottom - rect.top;
OnSize(m_rect.GetSize());
break;
}
case WM_CLOSE:
{
bool bClose = true;
OnClose(bClose);
if (bClose) {
//解决关闭窗口时 owner窗口闪烁的问题
if (m_ownerWnd) {
::EnableWindow(m_ownerWnd, TRUE);
}
::DestroyWindow(Hwnd());
}
else {
//关闭已取消
return TRUE;
}
break;
}
case WM_KEYDOWN:
{
#ifdef _DEBUG
if (wParam == VK_F11) {
m_publicData->Debug = !m_publicData->Debug;
if (m_publicData->Debug) {
m_publicData->DebugColor = m_publicData->DebugColors[m_publicData->ColorIndex];
m_publicData->ColorIndex++;
if (m_publicData->ColorIndex >= m_publicData->DebugColors.size()) {
m_publicData->ColorIndex = 0;
}
}
Invalidate();
}
#endif
OnKeyDown(wParam, lParam);
break;
}
case WM_KEYUP: {
OnKeyUp(wParam, lParam);
break;
}
case WM_DESTROY:
{
m_hWnd = NULL;
OnDestroy();
break;
}
case WM_SETCURSOR: {
break;
}
case WM_MOUSEHOVER: {
//鼠标悬停消息
m_bTracking = false;
auto xPos = GET_X_LPARAM(lParam);
auto yPos = GET_Y_LPARAM(lParam);
OnMouseHover({ xPos,yPos });
break;
}
case WM_MOUSEMOVE:
{
if (!m_bTracking) {
TRACKMOUSEEVENT tme;
tme.cbSize = sizeof(tme);
tme.dwFlags = TME_LEAVE | TME_HOVER;
tme.hwndTrack = Hwnd();
tme.dwHoverTime = 500;//500毫秒算鼠标悬停
if (TrackMouseEvent(&tme) == TRUE) {
m_bTracking = true;
}
}
auto xPos = GET_X_LPARAM(lParam);
auto yPos = GET_Y_LPARAM(lParam);
//OutputDebugStringA(UIString("%d %d\n").format(x, y).c_str());
OnMouseMove({ xPos,yPos });
m_mouseIn = true;
//给hwndTip发送消息告诉现在移动到什么位置了
//LPARAM lp = MAKELPARAM(args.Location.X, args.Location.Y);
//SendMessage(_hWndTips, TTM_TRACKPOSITION, 0, lParam);
break;
}
case WM_LBUTTONDOWN:
{
OnMouseDown(MouseButton::Left, { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) });
break;
}
case WM_RBUTTONDOWN:
{
OnMouseDown(MouseButton::Right, { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) });
break;
}
case WM_LBUTTONUP:
{
Point pt(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
OnMouseUp(MouseButton::Left, pt);
break;
}
case WM_RBUTTONUP:
{
Point pt(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
OnMouseUp(MouseButton::Right, pt);
break;
}
case WM_MOUSELEAVE:
{
m_bTracking = false;
m_mouseIn = false;
OnMouseLeave();
break;
}
case WM_MOUSEWHEEL:
{
auto fwKeys = GET_KEYSTATE_WPARAM(wParam);
auto zDelta = GET_WHEEL_DELTA_WPARAM(wParam);
auto xPos = GET_X_LPARAM(lParam);
auto yPos = GET_Y_LPARAM(lParam);
OnMouseWheel(zDelta, { xPos,yPos });
break;
}
default:
{
break;
}
}
return ::DefWindowProc(Hwnd(), uMsg, wParam, lParam);
}
void Window::DoPaint(HDC winHDC, const Rect& rePaintRect) {
#define COUNT_DOPAINT 0
#if COUNT_DOPAINT
auto start = std::chrono::steady_clock::now();
#endif // COUNT_DOPAINT
#if USED_DIRECT2D
DXRender graphics(winHDC, 0, 0, GetClientRect().Width, GetClientRect().Height);
PaintEventArgs args(graphics);
args.DC = winHDC;
args.HWND = Hwnd();
args.PublicData = this->m_publicData;
args.PublicData->PaintCount = 0;
args.InvalidRectangle = rePaintRect;
OnPaint(args);
#endif
#if COUNT_DOPAINT
auto end = std::chrono::steady_clock::now();
auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
auto now = std::chrono::system_clock::now();
auto ts = std::chrono::duration_cast<std::chrono::seconds>(now.time_since_epoch()).count();
UIString log = UIString("ts:(%lld) elapsed:(%lldms) count:(%d) rect:[%d,%d,%d,%d]\n").format(ts, static_cast<long long>(elapsed), args.PublicData->PaintCount, rePaintRect.X, rePaintRect.Y, rePaintRect.Width, rePaintRect.Height);
OutputDebugStringA(log.c_str());
#endif // COUNT_DOPAINT
}
void Window::OnPaint(PaintEventArgs& arg)
{
this->SendEvent(m_frame, arg);
}
bool Window::IsInWindow(Control& pControl, Control& it) {
const Rect& winClientRect = GetClientRect();
const Rect& rect = it.GetRect();//
if (rect.IsEmptyArea()) {
return false;
}
auto clientRect = it.GetClientRect();//
if (!winClientRect.IntersectsWith(clientRect)) {
return false;
}
if (!pControl.GetClientRect().IntersectsWith(clientRect)) {
return false;
}
return true;
}
Control* Window::HitTestControl(const Point& clientPoint, Point* outPoint) {
Control* outCtl = m_frame->GetLayout();
if (outCtl == NULL)return NULL;
outPoint->X = clientPoint.X;
outPoint->Y = clientPoint.Y;
Find_Loop:
ScrollBar* scrollBar = outCtl->GetScrollBar();
if (scrollBar && scrollBar->GetClientRect().Contains(clientPoint)) {
if (scrollBar->IsDraw()) {
auto barRect = scrollBar->GetClientRect();
(*outPoint).X = clientPoint.X - barRect.X;
(*outPoint).Y = clientPoint.Y - barRect.Y;
outCtl = scrollBar;
return outCtl;
}
}
const Controls* pTemp;
if (outCtl->GetViewControls().size() > 0) {
pTemp = &outCtl->GetViewControls();
}
else {
pTemp = (Controls*)(&(outCtl->GetControls()));
}
for (auto itor = pTemp->rbegin(); itor != pTemp->rend(); ++itor) {
Control& it = **itor;
if (!it.IsVisible()) {
continue;
}
if (!IsInWindow(*outCtl, it)) {
continue;
}
if (it.GetClientRect().Contains(clientPoint)) {
outCtl = &it;
auto ctlRect = it.GetClientRect();
(*outPoint).X = clientPoint.X - ctlRect.X;
(*outPoint).Y = clientPoint.Y - ctlRect.Y;
if (!outCtl->IsHitTestVisible()) {
//如果控件未开启命中测试则跳过该控件
continue;
}
goto Find_Loop;
}
}
//当命中控件未开启命中测试 偏移到父控件
while (true)
{
if (!outCtl->IsHitTestVisible() && outCtl->Parent) {
(*outPoint).X += outCtl->X();
(*outPoint).Y += outCtl->Y();
outCtl = outCtl->Parent;
}
else {
break;
}
}
return outCtl;
}
void Window::SendEvent(Control* ctrl, const EventArgs& args)
{
if (ctrl) {
//设置鼠标样式
if (args.EventType == Event::OnMouseMove || args.EventType == Event::OnMouseDown) {
auto cursor = ctrl->GetCursor(args.EventType == Event::OnMouseDown ? ControlState::Active : ControlState::None);
if (cursor) {
::SetCursor(cursor);
}
}
ctrl->SendEvent(args);
}
}
void Window::OnMouseHover(const Point& point) {
}
void Window::OnMouseMove(const Point& point)
{
//对窗口进行移动
if (m_moveWindow && m_mouseDown) {
POINT ptNow;
::GetCursorPos(&ptNow);
int dx = ptNow.x - m_dragPoint.x;
int dy = ptNow.y - m_dragPoint.y;
HWND hWnd = Hwnd();
RECT rect;
::GetWindowRect(hWnd, &rect);
this->SetRect(Rect(rect.left + dx, rect.top + dy, rect.right - rect.left, rect.bottom - rect.top));
m_dragPoint = ptNow; // 更新起点
return;
}
if (m_inputControl && m_mouseDown) { //按住移动的控件
auto ctlRect = m_inputControl->GetClientRect();
MouseEventArgs args(Event::OnMouseMove, { point.X - ctlRect.X ,point.Y - ctlRect.Y });
this->SendEvent(m_inputControl, args);
return;
}
Point relativePoint;
Control* newCtl = this->HitTestControl(point, &relativePoint);//找到当前控件的位置
MouseEventArgs args(Event::None);
args.Location = relativePoint;
//触发上一个
if (m_focusControl != newCtl) {
if (m_focusControl) {
MouseEventArgs args(Event::OnMouseLeave);
auto rect = m_focusControl->GetClientRect();
args.Location.X = point.X - rect.X;
args.Location.Y = point.Y - rect.Y;
this->SendEvent(m_focusControl, args);
}
//触发MouseEnter
args.EventType = Event::OnMouseEnter;
this->SendEvent(newCtl, args);
m_focusControl = newCtl;
}
//触发命中的MouseMove
if (m_focusControl) {
args.EventType = Event::OnMouseMove;
this->SendEvent(m_focusControl, args);
}
}
void Window::OnMouseLeave()
{
if (m_focusControl) {
MouseEventArgs args(Event::OnMouseLeave);
this->SendEvent(m_focusControl, args);
}
m_focusControl = NULL;
m_mouseDown = false;
}
void Window::OnMouseDoubleClick(MouseButton mbtn, const Point& point)
{
Point relativePoint;
Control* outCtl = this->HitTestControl(point, &relativePoint);
if (outCtl) {
MouseEventArgs args(Event::OnMouseDoubleClick);
args.Button = mbtn;
args.Location = relativePoint;
this->SendEvent(outCtl, args);
}
}
void Window::OnMouseDown(MouseButton mbtn, const Point& point)
{
::SetCapture(Hwnd());
m_mouseDown = true;//标记为按下
//寻早控件
Point relativePoint;
Control* outCtl = this->HitTestControl(point, &relativePoint);
//如果单机的不是上一个 那么上一个触发失去焦点事件
if (m_inputControl != outCtl) {
this->SetFocus(outCtl);//设置焦点
}
//给命中的控件触发鼠标按下事件
if (m_inputControl) {
MouseEventArgs args(Event::OnMouseDown);
args.Button = mbtn;
args.Location = relativePoint;
this->SendEvent(m_inputControl, args);
}
//获取按下按钮和按下的时差
auto time_now = ::GetTickCount64();//记录鼠标按下的当前时间
auto offset = time_now - m_lastDownTime;//本次与上次按下按钮的时间差
//300毫秒之内同一个按钮按下两次算双击消息
if (offset < 300 && m_lastBtn == mbtn && m_downPoint == point) {
OnMouseDoubleClick(mbtn, point);
}
m_lastDownTime = ::GetTickCount64();
m_lastBtn = mbtn;
m_downPoint = point;
}
void Window::OnMouseUp(MouseButton mbtn, const Point& point)
{
m_moveWindow = false;
::ReleaseCapture();
if (m_mouseDown == false) {
return;
}
m_mouseDown = false;
if (m_inputControl) {
Rect ctlRect = m_inputControl->GetClientRect();
MouseEventArgs args(Event::None);
args.Button = mbtn;
args.Location = { point.X - ctlRect.X,point.Y - ctlRect.Y };
//触发鼠标抬起事件
args.EventType = Event::OnMouseUp;
this->SendEvent(m_inputControl, args);
}
}
void Window::OnMouseWheel(int zDelta, const Point& point)
{
if (m_focusControl == NULL) return;
if (m_focusControl) {
MouseEventArgs args(Event::OnMouseWheel);
args.Location = point;
args.ZDelta = zDelta;
this->SendEvent(m_focusControl, args);
}
ScrollBar* scrollBar = NULL;
if (m_focusControl && m_focusControl->GetScrollBar() && m_focusControl->GetScrollBar()->Scrollable()) {
scrollBar = dynamic_cast<ScrollBar*>(m_focusControl->GetScrollBar());
}
Control* pControl = m_focusControl;
while (scrollBar == NULL && pControl)
{
if (pControl->GetScrollBar() && pControl->GetScrollBar()->Scrollable()) {
scrollBar = dynamic_cast<ScrollBar*>(pControl->GetScrollBar());
break;
}
pControl = pControl->Parent;
}
if (scrollBar) {
MouseEventArgs args(Event::OnMouseWheel);
args.Location = point;
args.ZDelta = zDelta;
this->SendEvent(scrollBar, args);
}
}
float Window::GetScale() {
return this->m_publicData->Scale;
}
void Window::OnSize(const Size& sz)
{
if (!IsFloatEqual(m_frame->GetScale(), m_publicData->Scale)) {
this->OnDpiChange(m_publicData->Scale, Rect());
}
const Rect& rect = this->GetClientRect();
m_frame->SetRect(rect);
}
void Window::OnClose(bool& bClose)
{
}
void Window::OnDestroy()
{
}
void Window::OnKeyChar(WPARAM wParam, LPARAM lParam)
{
if (m_inputControl) { //
KeyboardEventArgs args(Event::OnKeyChar, wParam, lParam);
this->SendEvent(m_inputControl, args);
return;
}
}
void Window::OnKeyDown(WPARAM wParam, LPARAM lParam) {
if (m_inputControl) { //
KeyboardEventArgs args(Event::OnKeyDown, wParam, lParam);
this->SendEvent(m_inputControl, args);
return;
}
}
void Window::OnKeyUp(WPARAM wParam, LPARAM lParam) {
if (m_inputControl) { //
KeyboardEventArgs args(Event::OnKeyUp, wParam, lParam);
this->SendEvent(m_inputControl, args);
return;
}
}
void Window::OnFocus(HWND hWnd)
{
}
void Window::OnMove(const Point& point) {
}
void Window::OnKillFocus(HWND hWnd)
{
if (m_inputControl) { //窗口失去焦点
KillFocusEventArgs args(NULL);
this->SendEvent(m_inputControl, args);
m_inputControl = NULL;
}
}
void Window::MoveWindow() {
::GetCursorPos(&m_dragPoint); // 获取屏幕坐标
m_moveWindow = true;
}
void Window::TitleMoveWindow() {
::ReleaseCapture();
::SendMessage(Hwnd(), WM_NCLBUTTONDOWN, HTCAPTION, NULL);//模拟鼠标按住标题栏移动窗口,会吃掉鼠标左键的弹起消息,手动触发也无惧于事
::SendMessage(Hwnd(), WM_LBUTTONUP, NULL, NULL);//松开 不调发送此消息会导致一些消息问题
}
void Window::OnDpiChange(float systemScale, const Rect& newRect)
{
//新的缩放比
float newScale = systemScale / m_publicData->Scale;
this->m_publicData->Scale = systemScale;
this->m_miniSize.Scale(newScale);
this->m_maxSize.Scale(newScale);
DpiChangeEventArgs arg(systemScale);
this->SendEvent(m_frame, arg);
if (!newRect.IsEmptyArea()) {
SetRect({ newRect.X, newRect.Y, newRect.Width, newRect.Height });
}
}
void Window::OnNotify(Control* sender, EventArgs& args) {
if (this->NotifyHandler) {
this->NotifyHandler(sender, args);
}
else {
ezui::DefaultNotify(sender, args);
}
}
};