Files
EzUI/sources/EzUI.cpp
2026-01-24 22:42:46 +08:00

606 lines
16 KiB
C++

#include "EzUI.h"
#include "Bitmap.h"
#include "Control.h"
#include "IFrame.h"
#include "Window.h"
#include "TabLayout.h"
#include <gdiplus.h>
#pragma comment(lib, "shlwapi.lib")
#pragma comment(lib,"imm32.lib")
namespace ezui {
HMODULE __EzUI__HINSTANCE = NULL;
Resource* __EzUI__Resource = NULL;
DWORD __EzUI__ThreadId = NULL;
HWND __EzUI_MessageWnd = NULL;
const std::list<ezui::MonitorInfo> __EzUI__MonitorInfos;
Color Color::Make(const UIString& colorStr, bool* _isGood) {
Color color;
bool _bCopy;
bool* isGood = _isGood ? _isGood : &_bCopy;
*isGood = false;
do {
if (colorStr == "black") {
color = Color::Black; *isGood = true; break;
}
else if (colorStr == "white") {
color = Color::White; *isGood = true; break;
}
else if (colorStr == "gray") {
color = Color::Gray; *isGood = true; break;
}
else if (colorStr == "lightgray") {
color = Color::LightGray; *isGood = true; break;
}
else if (colorStr == "darkgray") {
color = Color::DarkGray; *isGood = true; break;
}
else if (colorStr == "red") {
color = Color::Red; *isGood = true; break;
}
else if (colorStr == "darkred") {
color = Color::DarkRed; *isGood = true; break;
}
else if (colorStr == "lightcoral") {
color = Color::LightCoral; *isGood = true; break;
}
else if (colorStr == "tomato") {
color = Color::Tomato; *isGood = true; break;
}
else if (colorStr == "crimson") {
color = Color::Crimson; *isGood = true; break;
}
else if (colorStr == "green" || colorStr == "lime") {
color = Color::Green; *isGood = true; break;
}
else if (colorStr == "darkgreen") {
color = Color::DarkGreen; *isGood = true; break;
}
else if (colorStr == "lawngreen") {
color = Color::LawnGreen; *isGood = true; break;
}
else if (colorStr == "palegreen") {
color = Color::PaleGreen; *isGood = true; break;
}
else if (colorStr == "blue") {
color = Color::Blue; *isGood = true; break;
}
else if (colorStr == "royalblue") {
color = Color::RoyalBlue; *isGood = true; break;
}
else if (colorStr == "dodgerblue") {
color = Color::DodgerBlue; *isGood = true; break;
}
else if (colorStr == "deepskyblue") {
color = Color::DeepSkyBlue; *isGood = true; break;
}
else if (colorStr == "lightblue") {
color = Color::LightBlue; *isGood = true; break;
}
else if (colorStr == "yellow") {
color = Color::Yellow; *isGood = true; break;
}
else if (colorStr == "gold") {
color = Color::Gold; *isGood = true; break;
}
else if (colorStr == "lightyellow") {
color = Color::LightYellow; *isGood = true; break;
}
else if (colorStr == "khaki") {
color = Color::Khaki; *isGood = true; break;
}
else if (colorStr == "orange") {
color = Color::Orange; *isGood = true; break;
}
else if (colorStr == "darkorange") {
color = Color::DarkOrange; *isGood = true; break;
}
else if (colorStr == "coral") {
color = Color::Coral; *isGood = true; break;
}
else if (colorStr == "salmon") {
color = Color::Salmon; *isGood = true; break;
}
else if (colorStr == "purple") {
color = Color::Purple; *isGood = true; break;
}
else if (colorStr == "mediumpurple") {
color = Color::MediumPurple; *isGood = true; break;
}
else if (colorStr == "indigo") {
color = Color::Indigo; *isGood = true; break;
}
else if (colorStr == "violet") {
color = Color::Violet; *isGood = true; break;
}
else if (colorStr == "plum") {
color = Color::Plum; *isGood = true; break;
}
else if (colorStr == "cyan" || colorStr == "aqua") {
color = Color::Cyan; *isGood = true; break;
}
else if (colorStr == "teal") {
color = Color::Teal; *isGood = true; break;
}
else if (colorStr == "turquoise") {
color = Color::Turquoise; *isGood = true; break;
}
else if (colorStr == "brown") {
color = Color::Brown; *isGood = true; break;
}
else if (colorStr == "maroon") {
color = Color::Maroon; *isGood = true; break;
}
else if (colorStr == "tan") {
color = Color::Tan; *isGood = true; break;
}
else if (colorStr == "beige") {
color = Color::Beige; *isGood = true; break;
}
else if (colorStr == "navy") {
color = Color::Navy; *isGood = true; break;
}
else if (colorStr == "olive") {
color = Color::Olive; *isGood = true; break;
}
else if (colorStr == "silver") {
color = Color::Silver; *isGood = true; break;
}
else if (colorStr == "transparent") {
color = Color::Transparent; *isGood = true; break;
}
else if (colorStr.find("#") == 0 && colorStr.size() == 7) { //"#4e6ef2"
auto rStr = colorStr.substr(1, 2);
auto gStr = colorStr.substr(3, 2);
auto bStr = colorStr.substr(5, 2);
DWORD r, g, b;
sscanf_s(rStr.c_str(), "%x", &r);
sscanf_s(gStr.c_str(), "%x", &g);
sscanf_s(bStr.c_str(), "%x", &b);
color = Color((BYTE)r, (BYTE)g, (BYTE)b);
*isGood = true; break;
}
else if (colorStr.find("rgb") == 0) { //"rgb(255,255,255,50%)"
size_t pos1 = colorStr.find("(");
size_t pos2 = colorStr.rfind(")");
if (pos1 == std::string::npos || pos2 == std::string::npos) {
break;//非标准rgb格式
}
UIString rgbStr = colorStr.substr(pos1 + 1, pos2 - pos1 - 1);
auto rgbList = rgbStr.replace(' ', ',').split(",");
if (rgbList.size() < 3) {
break;//非标准rgb格式
}
BYTE r = std::stoi(rgbList.at(0));
BYTE g = std::stoi(rgbList.at(1));
BYTE b = std::stoi(rgbList.at(2));
BYTE a = 255;
if (rgbList.size() > 3) { //如果有设置透明度
std::string aStr = rgbList.at(3);
if (aStr.rfind("%") != std::string::npos) {
a = (BYTE)(std::atof(aStr.c_str()) / 100.0f * 255 + 0.5);//计算出透明度转为byte
}
else {
a = std::stoi(aStr.c_str());//0~255描述透明度
}
}
color = Color(r, g, b, a);
*isGood = true; break;
}
} while (false);
return color;
}
Image::Image(Bitmap* bitmap) :DXImage(bitmap->GetHBITMAP())
{
}
Image* Image::Make(const UIString& fileOrRes) {
//本地文件中获取
std::wstring wstr = fileOrRes.unicode();
DWORD dwAttr = GetFileAttributesW(wstr.c_str());
if (dwAttr && (dwAttr != -1) && (dwAttr & FILE_ATTRIBUTE_ARCHIVE)) {
Image* img = new Image(wstr);
#ifdef _DEBUG
if (img) {
img->m_path = fileOrRes;
}
#endif
return img;
}
//从资源中获取
if (ezui::__EzUI__Resource) {
std::string data;
if (!ezui::__EzUI__Resource->GetFile(fileOrRes, &data) || data.empty()) {
return NULL;
}
Image* img = new Image(data.c_str(), data.size());
#ifdef _DEBUG
if (img) {
img->m_path = fileOrRes;
}
#endif
return img;
}
return NULL;
}
bool IsFloatEqual(float num1, float num2)
{
return std::fabsf(num1 - num2) <= EZUI_FLOAT_EPSILON;
}
HICON LoadIcon(const UIString& fileName)
{
std::string fileData;
if (!GetResource(fileName, &fileData)) {
return NULL;
}
const char* pData = fileData.c_str();
size_t size = fileData.size();
HICON hIcon = NULL;
HGLOBAL hMem = ::GlobalAlloc(GMEM_MOVEABLE, size);
void* pMem = ::GlobalLock(hMem);
::memcpy(pMem, pData, size);
::GlobalUnlock(hMem);
IStream* pStream = NULL;
if (::CreateStreamOnHGlobal(hMem, TRUE, &pStream) != S_OK) {
return NULL;
}
Gdiplus::Bitmap* bmp = Gdiplus::Bitmap::FromStream(pStream);
if (bmp && bmp->GetHICON(&hIcon) != Gdiplus::Ok) {
hIcon = NULL;
}
delete bmp;
pStream->Release();
return hIcon;
}
void InstallFont(const UIString& fontFileName) {
auto ret = ::AddFontResourceW(fontFileName.unicode().c_str());
::SystemParametersInfoW(SPI_SETNONCLIENTMETRICS, 0, NULL, SPIF_SENDCHANGE);//刷新
}
void UnstallFont(const UIString& fontFileName) {
auto ret = ::RemoveFontResourceW(fontFileName.unicode().c_str());
::SystemParametersInfoW(SPI_SETNONCLIENTMETRICS, 0, NULL, SPIF_SENDCHANGE);//刷新
}
bool CopyToClipboard(int uFormat, void* pData, size_t size, HWND hWnd) {
//打开剪贴板
bool ret = ::OpenClipboard(hWnd);
if (!ret)return ret;
//清空剪贴板
::EmptyClipboard();
//为剪切板申请内存
HGLOBAL clip = ::GlobalAlloc(GMEM_DDESHARE, size);
::memcpy((void*)clip, pData, size);
//解锁
ret = ::GlobalUnlock(clip);
ret = ::SetClipboardData(uFormat, clip);
ret = ::CloseClipboard();
return ret;
}
bool GetClipboardData(int uFormat, std::function<void(void*, size_t)> Callback, HWND hWnd) {
//只接收当前类型
bool ret = ::IsClipboardFormatAvailable(uFormat);
if (!ret)return ret;
//打开剪贴版
ret = OpenClipboard(hWnd);
if (!ret)return ret;
//获取剪贴板数据
HANDLE hClipboard = ::GetClipboardData(uFormat);
size_t dataSize = ::GlobalSize(hClipboard);
void* pData = ::GlobalLock(hClipboard);
if (Callback) {
Callback(pData, dataSize);
}
//解锁
ret = ::GlobalUnlock(hClipboard);
ret = ::CloseClipboard();
return ret;
}
bool CopyToClipboard(const std::wstring& str, HWND hWnd) {
return CopyToClipboard(CF_UNICODETEXT, (void*)str.c_str(), (str.size() + 1) * 2, hWnd);
}
bool GetClipboardData(std::wstring* outStr, HWND hWnd) {
bool ret = GetClipboardData(CF_UNICODETEXT, [=](void* data, size_t _sz) {
wchar_t* wstr = (wchar_t*)data;
size_t sz = _sz / 2;
outStr->clear();
outStr->append(wstr, wstr[sz - 1] == 0 ? sz - 1 : sz);
}, hWnd);
return ret;
}
bool GetResource(const UIString& filename, std::string* outFileData)
{
outFileData->clear();
//本地文件中获取
std::wstring wstr = filename.unicode();
DWORD dwAttr = GetFileAttributesW(wstr.c_str());
if (dwAttr && (dwAttr != -1) && (dwAttr & FILE_ATTRIBUTE_ARCHIVE)) {
std::ifstream ifs(wstr, std::ios::binary);
ifs.seekg(0, std::ios::end);
auto size = ifs.tellg();
outFileData->resize(size);
ifs.seekg(0);
ifs.read((char*)outFileData->c_str(), size);
ifs.close();
return true;
}
//从资源中获取
if (ezui::__EzUI__Resource) {
if (ezui::__EzUI__Resource->GetFile(filename, outFileData)) {
return true;
}
}
return false;
}
#undef GetMonitorInfo
void GetMonitorInfo(MonitorInfo& mt, HMONITOR hMonitor) {
//获取显示器信息
MONITORINFOEX infoEx;
infoEx.cbSize = sizeof(infoEx);
::GetMonitorInfoW(hMonitor, &infoEx);
mt.Monitor = hMonitor;
//逻辑宽高
mt.Rect.X = infoEx.rcMonitor.left;
mt.Rect.Y = infoEx.rcMonitor.top;
mt.Rect.Width = infoEx.rcMonitor.right - infoEx.rcMonitor.left;
mt.Rect.Height = infoEx.rcMonitor.bottom - infoEx.rcMonitor.top;
if ((infoEx.dwFlags & MONITORINFOF_PRIMARY) == MONITORINFOF_PRIMARY) {//是否为主显示器
mt.Primary = true;
}
//获取工作区域 自动排除任务栏
mt.WorkRect.X = infoEx.rcWork.left;
mt.WorkRect.Y = infoEx.rcWork.top;
mt.WorkRect.Width = infoEx.rcWork.right - infoEx.rcWork.left;
mt.WorkRect.Height = infoEx.rcWork.bottom - infoEx.rcWork.top;
//获取物理宽高
DEVMODE dm;
dm.dmSize = sizeof(dm);
dm.dmDriverExtra = 0;
::EnumDisplaySettings(infoEx.szDevice, ENUM_REGISTRY_SETTINGS, &dm);
mt.Physical.Width = dm.dmPelsWidth;//物理宽
mt.Physical.Height = dm.dmPelsHeight;//物理高
//计算缩放
mt.Scale = ((float)mt.Physical.Height / (float)mt.Rect.Height);
//显示器fps
mt.FPS = (float)dm.dmDisplayFrequency;
}
size_t GetMonitor(std::list<MonitorInfo>* outMonitorInfo)
{
outMonitorInfo->clear();
//// 枚举显示器回调函数
::EnumDisplayMonitors(NULL, NULL, [](HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) ->BOOL {
// 获取当前所有显示器的信息
std::list<MonitorInfo>* monitors = (std::list<MonitorInfo>*)dwData;
MonitorInfo mt;
GetMonitorInfo(mt, hMonitor);
monitors->push_back(mt);
return TRUE;
}, LPARAM(outMonitorInfo));
return outMonitorInfo->size();
}
void GetMontior(MonitorInfo* monitorInfo, HWND hWnd)
{
POINT cursorPos;
HMONITOR hMonitor;
if (hWnd) {
hMonitor = ::MonitorFromWindow(hWnd, MONITOR_DEFAULTTONEAREST);
}
else {
// 确定包含鼠标位置的屏幕
GetCursorPos(&cursorPos);
hMonitor = ::MonitorFromPoint(cursorPos, MONITOR_DEFAULTTONEAREST);
}
// 获取屏幕信息
GetMonitorInfo(*monitorInfo, hMonitor);
}
void GetMontior(MonitorInfo* monitorInfo, const Rect& rect)
{
RECT r = rect.ToRECT();
HMONITOR hMonitor = ::MonitorFromRect(&r, MONITOR_DEFAULTTONEAREST);
// 获取屏幕信息
GetMonitorInfo(*monitorInfo, hMonitor);
}
HCURSOR LoadCursor(Cursor cursorType)
{
return ::LoadCursorW(NULL, (LPTSTR)cursorType);
}
HCURSOR LoadCursor(const UIString& fileName)
{
return ::LoadCursorFromFileW(fileName.unicode().c_str());
}
void FreeCursor(HCURSOR hCursor)
{
::DestroyCursor(hCursor);
}
void DefaultNotify(Control* sender, EventArgs& args) {
WindowData* winData = NULL;
if (!sender || !(winData = sender->GetPublicData())) {
return;
}
auto* win = winData->Window;
switch (args.EventType)
{
case Event::OnMouseDoubleClick: {
if (sender->Action == ControlAction::Title) {
if (::IsZoomed(win->Hwnd())) {
win->ShowNormal();
}
else {
win->ShowMaximized();
}
}
break;
}
case Event::OnMouseDown: {
if (sender->Action == ControlAction::MoveWindow) {
winData->MoveWindow();
break;
}
if (sender->Action == ControlAction::Title) {
winData->TitleMoveWindow();
break;
}
if (sender->Action == ControlAction::Mini) {
win->ShowMinimized();
break;
}
if (sender->Action == ControlAction::Max) {
if (!win->IsMaximized()) {
win->ShowMaximized();
}
else {
win->ShowNormal();
}
break;
}
if (sender->Action == ControlAction::Close) {
win->Close();
break;
}
UIString tabName = sender->GetAttribute("tablayout");
if (!tabName.empty()) {
auto ctls = sender->Parent->FindControl("tablayout", tabName);
IFrame* frame = sender->GetFrame();
TabLayout* tabLayout = dynamic_cast<TabLayout*>(frame ? frame->FindControl(tabName) : win->FindControl(tabName));
if (tabLayout && sender->Parent) {
int pos = 0;
for (auto& it : ctls)
{
if (it == sender) {
tabLayout->SlideToPage(pos);
tabLayout->Invalidate();
break;
}
++pos;
}
}
}
break;
}
default:
break;
}
}
void ControlStyle::Scale(float scale)
{
this->FontSize = this->FontSize * scale + 0.5;
this->Border.Scale(scale);
if (this->BackImage) {
this->BackImage->DrawPosition.Scale(scale);
this->BackImage->DrawSize.Scale(scale);
}
if (this->ForeImage) {
this->ForeImage->DrawPosition.Scale(scale);
this->ForeImage->DrawSize.Scale(scale);
}
}
Object::Object(Object* parentObject)
{
if (parentObject) {
parentObject->Attach(this);
}
}
Object::~Object() {
}
void Object::SetAttribute(const UIString& attrName, const UIString& attrValue) {
auto itor = m_attrs.find(attrName);
if (itor != m_attrs.end()) {
(*itor).second = attrValue;
}
else {
m_attrs.insert(std::pair<UIString, UIString>(attrName, attrValue));
}
}
UIString Object::GetAttribute(const UIString& attrName) {
auto itor = m_attrs.find(attrName);
if (itor != m_attrs.end()) {
return (*itor).second;
}
return "";
}
const std::map<UIString, UIString>& Object::GetAttributes() {
return m_attrs;
}
void Object::RemoveAttribute(const UIString& attrName)
{
auto itor = m_attrs.find(attrName);
if (itor != m_attrs.end()) {
m_attrs.erase(itor);
}
}
Object* Object::Attach(Object* obj)
{
m_childObjects.Add(obj);
return obj;
}
void Object::Detach(Object* obj)
{
m_childObjects.Remove(obj);
}
void Object::DeleteLater()
{
BeginInvoke([this]() {
delete this;
});
}
void PaintEventArgs::PushLayer(const Geometry& dxGeometry) {
this->Graphics.PushLayer(dxGeometry);
m_layers.push_back(false);
}
void PaintEventArgs::PushLayer(const Rect& rectBounds) {
this->Graphics.PushAxisAlignedClip(rectBounds);
m_layers.push_back(true);
}
void PaintEventArgs::PopLayer() {
if (m_layers.size() > 0) {
if (*m_layers.rbegin() == true) {
this->Graphics.PopAxisAlignedClip();
}
else {
this->Graphics.PopLayer();
}
m_layers.pop_back();
}
}
void PaintEventArgs::PushOffset(const Point& offset)
{
this->Graphics.SetTransform(offset.X, offset.Y);
this->m_offsets.push_back(offset);
}
void PaintEventArgs::PopOffset()
{
this->m_offsets.pop_back();
if (this->m_offsets.size() > 0) {
auto& offset = *this->m_offsets.rbegin();
this->Graphics.SetTransform(offset.X, offset.Y);
}
}
};