初版-带一个进度条
This commit is contained in:
65
sources/Animation.cpp
Normal file
65
sources/Animation.cpp
Normal file
@@ -0,0 +1,65 @@
|
||||
#include "Animation.h"
|
||||
namespace ezui {
|
||||
|
||||
Animation::Animation(Object* parentObject)
|
||||
: Object(parentObject)
|
||||
{
|
||||
m_timer = new Timer(this);
|
||||
m_timer->Tick = [this](Timer* t) {
|
||||
auto now = std::chrono::steady_clock::now();
|
||||
auto delta = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - m_lastTime).count();
|
||||
m_lastTime = now;
|
||||
|
||||
m_currValue += m_speedPerMs * delta;
|
||||
|
||||
bool finished = false;
|
||||
if ((m_speedPerMs > 0 && m_currValue >= m_endValue) || (m_speedPerMs < 0 && m_currValue <= m_endValue)) {
|
||||
m_currValue = m_endValue;
|
||||
finished = true;
|
||||
}
|
||||
|
||||
if (ValueChanged) {
|
||||
ValueChanged(m_currValue);
|
||||
}
|
||||
|
||||
if (finished) {
|
||||
t->Stop();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
Animation::~Animation()
|
||||
{
|
||||
m_timer->Stop();
|
||||
}
|
||||
|
||||
void Animation::SetStartValue(float value)
|
||||
{
|
||||
m_startValue = value;
|
||||
}
|
||||
|
||||
void Animation::SetEndValue(float value)
|
||||
{
|
||||
m_endValue = value;
|
||||
}
|
||||
|
||||
void Animation::Start(int durationMs, int fps)
|
||||
{
|
||||
m_timer->Stop();
|
||||
|
||||
m_currValue = m_startValue;
|
||||
float distance = m_endValue - m_startValue;
|
||||
m_speedPerMs = distance / durationMs;
|
||||
|
||||
m_lastTime = std::chrono::steady_clock::now(); //记录开始时间
|
||||
m_timer->Interval = 1000 / fps; //频率
|
||||
m_timer->Start();
|
||||
}
|
||||
bool Animation::IsStopped() {
|
||||
return m_timer->IsStopped();
|
||||
}
|
||||
void Animation::Stop()
|
||||
{
|
||||
m_timer->Stop();
|
||||
}
|
||||
}
|
||||
260
sources/Application.cpp
Normal file
260
sources/Application.cpp
Normal file
@@ -0,0 +1,260 @@
|
||||
#include "Application.h"
|
||||
#include "LayeredWindow.h"
|
||||
#include "UIManager.h"
|
||||
#include <CommCtrl.h>
|
||||
|
||||
#undef FindResource
|
||||
namespace ezui {
|
||||
bool g_comInitialized = false;//标记是否由我初始化
|
||||
std::list<HWND> g_hWnds;//存储所有使用本框架产生的窗口句柄
|
||||
|
||||
// 内部使用:枚举名称时的上下文
|
||||
struct ResourceContext {
|
||||
UIString rcIDName;
|
||||
HRSRC hResource = NULL;
|
||||
UIString rcType;
|
||||
};
|
||||
// 回调:枚举资源名称
|
||||
BOOL CALLBACK EnumNamesProc(HMODULE hModule, LPCWSTR lpszType, LPWSTR lpszName, LONG_PTR lParam) {
|
||||
ResourceContext* ctx = (ResourceContext*)(lParam);
|
||||
UIString resName = UIString(lpszName).toLower();
|
||||
if (!IS_INTRESOURCE(lpszName) && ctx->rcIDName.toLower() == resName) {
|
||||
ctx->hResource = FindResourceW(hModule, lpszName, lpszType);
|
||||
ctx->rcType = (wchar_t*)lpszType;
|
||||
return FALSE; // 找到就停止
|
||||
}
|
||||
return TRUE; // 继续
|
||||
}
|
||||
//通过资源名称查找windows嵌套资源
|
||||
HRSRC FindResource(const UIString& rcIDName)
|
||||
{
|
||||
HMODULE hModule = ezui::__EzUI__HINSTANCE;
|
||||
ResourceContext ctx;
|
||||
ctx.rcIDName = rcIDName;
|
||||
EnumResourceTypesW(hModule, [](HMODULE hModule, LPWSTR lpszType, LONG_PTR lParam)->BOOL {
|
||||
return EnumResourceNamesW(hModule, lpszType, EnumNamesProc, lParam);
|
||||
}, (LONG_PTR)&ctx);
|
||||
return ctx.hResource;
|
||||
}
|
||||
}
|
||||
|
||||
namespace ezui {
|
||||
LRESULT CALLBACK __EzUI__WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
|
||||
if (message == WM_CREATE) {
|
||||
g_hWnds.push_back(hWnd);
|
||||
}
|
||||
else if (message == WM_DESTROY) {
|
||||
auto itor = std::find(g_hWnds.begin(), g_hWnds.end(), hWnd);
|
||||
if (itor != g_hWnds.end()) {
|
||||
g_hWnds.erase(itor);
|
||||
}
|
||||
}
|
||||
WindowData* wndData = (WindowData*)UI_GET_USERDATA(hWnd);
|
||||
//执行消息过程
|
||||
if (wndData && wndData->WndProc) {
|
||||
return wndData->WndProc(hWnd, message, wParam, lParam);
|
||||
}
|
||||
return ::DefWindowProc(hWnd, message, wParam, lParam);
|
||||
}
|
||||
|
||||
void InitInvoker() {
|
||||
if (!ezui::__EzUI_MessageWnd) {
|
||||
::WNDCLASSEXW wcex = {};
|
||||
wcex.cbSize = sizeof(wcex);
|
||||
wcex.lpfnWndProc = [](HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) -> LRESULT {
|
||||
if (msg == WM_GUI_SYSTEM) {
|
||||
if (wParam == WM_GUI_BEGININVOKE || wParam == WM_GUI_INVOKE) {
|
||||
using Func = std::function<void()>;
|
||||
Func* callback = (Func*)lParam;
|
||||
(*callback)();
|
||||
if (wParam == WM_GUI_BEGININVOKE) {
|
||||
delete callback;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
return ::DefWindowProc(hwnd, msg, wParam, lParam);
|
||||
};
|
||||
wcex.hInstance = ezui::__EzUI__HINSTANCE;
|
||||
wcex.lpszClassName = EZUI_INVOKER_WINDOW_CLASS;
|
||||
RegisterClassExW(&wcex);
|
||||
ezui::__EzUI_MessageWnd = CreateWindowEx(
|
||||
WS_EX_LAYERED | WS_EX_NOACTIVATE | WS_EX_TOOLWINDOW,
|
||||
EZUI_INVOKER_WINDOW_CLASS, // 上面注册的类名
|
||||
L"", // 窗口名(无用)
|
||||
0, // 样式设为 0(无 WS_VISIBLE)
|
||||
0, 0, 0, 0, // 尺寸全为 0
|
||||
NULL, NULL, ezui::__EzUI__HINSTANCE, NULL
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
//销毁多线程通讯窗口
|
||||
void DestroyInvoker() {
|
||||
//销毁用于通讯的不可见窗口
|
||||
if (ezui::__EzUI_MessageWnd) {
|
||||
::DestroyWindow(ezui::__EzUI_MessageWnd);
|
||||
ezui::__EzUI_MessageWnd = NULL;
|
||||
BOOL ret = UnregisterClassW(EZUI_INVOKER_WINDOW_CLASS, ezui::__EzUI__HINSTANCE);
|
||||
}
|
||||
}
|
||||
|
||||
void Application::EnableHighDpi() {
|
||||
ezui::GetMonitor((std::list<MonitorInfo>*) & ezui::__EzUI__MonitorInfos);//获取一次显示器信息
|
||||
//DPI感知相关
|
||||
//不跟随系统放大无法接收WM_DISPLAYCHANGED消息
|
||||
//bool b = SetProcessDPIAware();
|
||||
//SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE);
|
||||
//会进入WM_DPICHANGED消息可进行自行控制缩放
|
||||
HMODULE hModNtdll = NULL;
|
||||
if (hModNtdll = ::LoadLibraryW(L"User32.dll")) {
|
||||
typedef void (WINAPI* Func_SetProcessDpiAwarenessContext)(void*);
|
||||
Func_SetProcessDpiAwarenessContext setProcessDpiAwarenessContext;
|
||||
setProcessDpiAwarenessContext = (Func_SetProcessDpiAwarenessContext)::GetProcAddress(hModNtdll, "SetProcessDpiAwarenessContext");
|
||||
if (setProcessDpiAwarenessContext)
|
||||
{
|
||||
//SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);//高版本sdk直接用
|
||||
setProcessDpiAwarenessContext((void*)-4);//函数指针方式是为了兼容win7
|
||||
}
|
||||
else {
|
||||
::SetProcessDPIAware();
|
||||
}
|
||||
::FreeLibrary(hModNtdll);
|
||||
}
|
||||
}
|
||||
|
||||
bool Application::SetResource(const UIString& localOrResName) {
|
||||
//只允许有一个资源文件
|
||||
if (ezui::__EzUI__Resource) {
|
||||
delete ezui::__EzUI__Resource;
|
||||
ezui::__EzUI__Resource = NULL;
|
||||
}
|
||||
//先从vs中的资源里面查找
|
||||
bool found = false;
|
||||
HRSRC hRsrc = ezui::FindResource(localOrResName);
|
||||
if (hRsrc) {
|
||||
ezui::__EzUI__Resource = new Resource(hRsrc);
|
||||
found = true;
|
||||
}
|
||||
else {
|
||||
//本地文件中获取
|
||||
std::wstring wstr = localOrResName.unicode();
|
||||
DWORD dwAttr = GetFileAttributesW(wstr.c_str());
|
||||
if (dwAttr && (dwAttr != -1) && (dwAttr & FILE_ATTRIBUTE_ARCHIVE)) {
|
||||
ezui::__EzUI__Resource = new Resource(localOrResName);
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
#ifdef _DEBUG
|
||||
if (!found) {
|
||||
::MessageBoxW(NULL, localOrResName.unicode().c_str(), L"Failed to open Resource", MB_OK | MB_ICONINFORMATION);
|
||||
}
|
||||
#endif
|
||||
return found;
|
||||
}
|
||||
Application::Application(HINSTANCE hInstance) {
|
||||
//存入全局实例
|
||||
ezui::__EzUI__HINSTANCE = hInstance == NULL ? ::GetModuleHandleW(NULL) : hInstance;
|
||||
ezui::__EzUI__ThreadId = ::GetCurrentThreadId();
|
||||
//设计窗口
|
||||
::WNDCLASSEXW wcex = {};
|
||||
wcex.cbSize = sizeof(wcex);
|
||||
wcex.style = CS_HREDRAW | CS_VREDRAW;//宽高改变窗口触发重绘
|
||||
wcex.lpfnWndProc = __EzUI__WndProc;//窗口过程
|
||||
wcex.cbClsExtra = 0;
|
||||
wcex.cbWndExtra = 0;
|
||||
wcex.hInstance = ezui::__EzUI__HINSTANCE;//
|
||||
wcex.hIcon = NULL;//(大图标)用于窗口标题栏、Alt+Tab 切换界面、任务栏预览等显示大图标的地方
|
||||
wcex.hIconSm = NULL;//(小图标)用于任务栏按钮、系统托盘(部分情况下)以及 Alt+Tab 小图标显示等
|
||||
wcex.hCursor = LoadCursorW(NULL, IDC_ARROW);//光标
|
||||
wcex.hbrBackground = NULL; // 窗口背景
|
||||
wcex.lpszMenuName = NULL;
|
||||
wcex.lpszClassName = EZUI_WINDOW_CLASS;//类名
|
||||
|
||||
if (!RegisterClassExW(&wcex)) //注册窗口
|
||||
{
|
||||
::MessageBoxW(NULL, L"This program requires Windows NT !",
|
||||
wcex.lpszClassName, MB_ICONERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
//注册一个窗口类并创建隐形窗口用于UI直接的多线程通讯
|
||||
InitInvoker();
|
||||
//为程序设置工作目录
|
||||
std::wstring startPath = Application::StartPath().unicode();
|
||||
::SetCurrentDirectoryW(startPath.c_str());
|
||||
if (!g_comInitialized) {
|
||||
auto ret = ::CoInitialize(NULL);//初始化com
|
||||
if (ret == S_OK) {
|
||||
g_comInitialized = true;//由我初始化的
|
||||
}
|
||||
}
|
||||
RenderInitialize();//初始化图形绘制库 D2D/GDI/GDI+
|
||||
|
||||
InitControls();//注册基础控件
|
||||
}
|
||||
//销毁所有窗口
|
||||
void DestroyAllWindows() {
|
||||
while (ezui::g_hWnds.size() > 0)
|
||||
{
|
||||
auto itor = ezui::g_hWnds.begin();
|
||||
if (itor != ezui::g_hWnds.end()) {
|
||||
HWND hwnd = *itor;
|
||||
g_hWnds.erase(itor);
|
||||
::DestroyWindow(hwnd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Application::~Application() {
|
||||
//销毁通讯窗口
|
||||
DestroyInvoker();
|
||||
//销毁所有窗口
|
||||
DestroyAllWindows();
|
||||
//取消窗口注册的类
|
||||
BOOL ret = UnregisterClassW(EZUI_WINDOW_CLASS, ezui::__EzUI__HINSTANCE);
|
||||
RenderUnInitialize();
|
||||
if (g_comInitialized) {
|
||||
::CoUninitialize();
|
||||
g_comInitialized = false;
|
||||
}
|
||||
if (ezui::__EzUI__Resource) {
|
||||
delete ezui::__EzUI__Resource;
|
||||
ezui::__EzUI__Resource = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
int Application::Exec()
|
||||
{
|
||||
MSG msg;
|
||||
while (::GetMessage(&msg, NULL, 0, 0)) {
|
||||
::TranslateMessage(&msg);
|
||||
::DispatchMessage(&msg);
|
||||
}
|
||||
return msg.wParam;
|
||||
}
|
||||
|
||||
void Application::Exit(int exitCode) {
|
||||
//销毁用于通讯的不可见窗口
|
||||
DestroyInvoker();
|
||||
//销毁所有窗口
|
||||
DestroyAllWindows();
|
||||
//退出消息循环
|
||||
::PostQuitMessage(exitCode);
|
||||
}
|
||||
|
||||
UIString Application::StartPath()
|
||||
{
|
||||
std::vector<wchar_t> wPath(32768);
|
||||
DWORD count = ::GetModuleFileNameW(__EzUI__HINSTANCE, wPath.data(), (DWORD)wPath.size());
|
||||
for (int i = count - 1; i > -1; i--)
|
||||
{
|
||||
if (wPath[i] == L'\\') {
|
||||
wPath[i] = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return UIString(wPath.data());
|
||||
}
|
||||
|
||||
};
|
||||
180
sources/Bitmap.cpp
Normal file
180
sources/Bitmap.cpp
Normal file
@@ -0,0 +1,180 @@
|
||||
#include "Bitmap.h"
|
||||
#include <gdiplus.h>
|
||||
namespace ezui {
|
||||
int GetEncoderClsid(const WCHAR* format, CLSID* pClsid)
|
||||
{
|
||||
UINT num = 0; // number of image encoders
|
||||
UINT size = 0; // size of the image encoder array in bytes
|
||||
|
||||
Gdiplus::ImageCodecInfo* pImageCodecInfo = NULL;
|
||||
|
||||
Gdiplus::GetImageEncodersSize(&num, &size);
|
||||
if (size == 0) {
|
||||
return -1; // Failure
|
||||
}
|
||||
|
||||
pImageCodecInfo = (Gdiplus::ImageCodecInfo*)(malloc(size));
|
||||
if (pImageCodecInfo == NULL) {
|
||||
return -1; // Failure
|
||||
}
|
||||
|
||||
Gdiplus::GetImageEncoders(num, size, pImageCodecInfo);
|
||||
|
||||
for (UINT j = 0; j < num; ++j)
|
||||
{
|
||||
if (wcscmp(pImageCodecInfo[j].MimeType, format) == 0)
|
||||
{
|
||||
*pClsid = pImageCodecInfo[j].Clsid;
|
||||
free(pImageCodecInfo);
|
||||
return j; // Success
|
||||
}
|
||||
}
|
||||
free(pImageCodecInfo);
|
||||
return -1; // Failure
|
||||
}
|
||||
|
||||
Bitmap::Bitmap(int width, int height) {
|
||||
Create(width, height);
|
||||
}
|
||||
Bitmap::Bitmap(HDC dc, const Rect& rect) {
|
||||
Create(rect.Width, rect.Height);
|
||||
::BitBlt(this->GetDC(), 0, 0, rect.Width, rect.Height, dc, rect.X, rect.Y, SRCCOPY);
|
||||
}
|
||||
void Bitmap::Create(int width, int height) {
|
||||
m_width = width;
|
||||
m_height = height;
|
||||
memset(&m_bmpInfo, 0, sizeof(m_bmpInfo));
|
||||
BITMAPINFOHEADER& bmih = m_bmpInfo.bmiHeader;
|
||||
bmih.biSize = sizeof(bmih);
|
||||
bmih.biBitCount = 32;
|
||||
bmih.biCompression = BI_RGB;
|
||||
bmih.biPlanes = 1;
|
||||
bmih.biWidth = width;
|
||||
bmih.biHeight = -height;
|
||||
bmih.biSizeImage = width * height * 4;
|
||||
m_bmp = ::CreateDIBSection(NULL, &m_bmpInfo, DIB_RGB_COLORS, (void**)&m_point, NULL, 0);
|
||||
::memset(m_point, 0, bmih.biSizeImage);
|
||||
this->GetDC();
|
||||
}
|
||||
int Bitmap::Width()const {
|
||||
return m_width;
|
||||
}
|
||||
int Bitmap::Height() const {
|
||||
return m_height;
|
||||
}
|
||||
void Bitmap::SetPixel(int x, int y, const Color& color) {
|
||||
DWORD* point = (DWORD*)this->m_point + (x + y * this->Width());//起始地址+坐标偏移
|
||||
|
||||
((BYTE*)point)[3] = color.GetA();//修改A通道数值
|
||||
((BYTE*)point)[2] = color.GetR();//修改R通道数值
|
||||
((BYTE*)point)[1] = color.GetG();//修改G通道数值
|
||||
((BYTE*)point)[0] = color.GetB();//修改B通道数值
|
||||
}
|
||||
|
||||
Color Bitmap::GetPixel(int x, int y) const {
|
||||
DWORD* point = (DWORD*)this->m_point + (x + y * this->Width());//起始地址+坐标偏移
|
||||
BYTE a, r, g, b;
|
||||
a = ((BYTE*)point)[3];
|
||||
r = ((BYTE*)point)[2];//修改R通道数值
|
||||
g = ((BYTE*)point)[1];//修改G通道数值
|
||||
b = ((BYTE*)point)[0];//修改B通道数值
|
||||
return Color(r, g, b, a);
|
||||
}
|
||||
byte* Bitmap::GetPixel()
|
||||
{
|
||||
return (byte*)this->m_point;
|
||||
}
|
||||
void Bitmap::Earse(const Rect& _rect) {
|
||||
Rect rect = _rect;
|
||||
if (rect.X < 0) {
|
||||
rect.X = 0;
|
||||
rect.Width += rect.X;
|
||||
}
|
||||
if (rect.Y < 0) {
|
||||
rect.Y = 0;
|
||||
rect.Height += rect.Y;
|
||||
}
|
||||
if (rect.GetBottom() > Height()) {
|
||||
rect.Height = this->Height() - rect.Y;
|
||||
}
|
||||
if (rect.GetRight() > Width()) {
|
||||
rect.Width = this->Width() - rect.X;
|
||||
}
|
||||
for (int y = rect.Y; y < rect.GetBottom(); ++y)
|
||||
{
|
||||
DWORD* point = (DWORD*)this->m_point + (rect.X + y * this->Width());//起始地址+坐标偏移
|
||||
::memset(point, 0, rect.Width * 4);//抹除
|
||||
}
|
||||
}
|
||||
HBITMAP Bitmap::GetHBITMAP()
|
||||
{
|
||||
return this->m_bmp;
|
||||
}
|
||||
HDC Bitmap::GetDC() {
|
||||
if (!m_hdc) {
|
||||
m_hdc = ::CreateCompatibleDC(NULL);
|
||||
::SelectObject(m_hdc, m_bmp);
|
||||
}
|
||||
return m_hdc;
|
||||
}
|
||||
bool Bitmap::Save(const UIString& fileName)
|
||||
{
|
||||
size_t pos = fileName.rfind(".");
|
||||
UIString ext;
|
||||
if (pos != size_t(-1)) {
|
||||
ext = fileName.substr(pos);
|
||||
ext = ext.toLower();
|
||||
}
|
||||
|
||||
bool ret = false;
|
||||
Gdiplus::Bitmap* pbmSrc = NULL;
|
||||
do
|
||||
{
|
||||
CLSID pngClsid;
|
||||
int code = -1;
|
||||
if (ext == ".png") {
|
||||
code = GetEncoderClsid(L"image/png", &pngClsid);
|
||||
}
|
||||
else if (ext == ".jpg" || ext == ".jpeg") {
|
||||
code = GetEncoderClsid(L"image/jpeg", &pngClsid);
|
||||
}
|
||||
else if (ext == ".tiff") {
|
||||
code = GetEncoderClsid(L"image/tiff", &pngClsid);
|
||||
}
|
||||
else {
|
||||
code = GetEncoderClsid(L"image/bmp", &pngClsid);
|
||||
}
|
||||
if (code == -1) {
|
||||
break;
|
||||
}
|
||||
pbmSrc = new Gdiplus::Bitmap(Width(), Height(), Width() * 4, PixelFormat32bppARGB, (BYTE*)GetPixel());
|
||||
if (pbmSrc && pbmSrc->Save(fileName.unicode().c_str(), &pngClsid) == Gdiplus::Status::Ok)
|
||||
{
|
||||
ret = true;
|
||||
break;
|
||||
}
|
||||
} while (false);
|
||||
if (pbmSrc) {
|
||||
delete pbmSrc;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
Bitmap* Bitmap::Clone() const {
|
||||
if (m_width <= 0 || m_height <= 0) {
|
||||
return NULL;
|
||||
}
|
||||
// 创建新 Bitmap 对象(保留格式)
|
||||
Bitmap* clone = new Bitmap(m_width, m_height);
|
||||
//拷贝像素
|
||||
memcpy(clone->m_point, m_point, m_width * m_height * 4);
|
||||
return clone;
|
||||
}
|
||||
|
||||
Bitmap::~Bitmap() {
|
||||
if (m_hdc) {
|
||||
::DeleteDC(m_hdc);
|
||||
::DeleteObject((HGDIOBJ)(HBITMAP)(m_bmp));
|
||||
}
|
||||
}
|
||||
};
|
||||
173
sources/BorderlessWindow.cpp
Normal file
173
sources/BorderlessWindow.cpp
Normal file
@@ -0,0 +1,173 @@
|
||||
#include "BorderlessWindow.h"
|
||||
namespace ezui {
|
||||
BorderlessWindow::BorderlessWindow(int width, int height, HWND owner, DWORD dwStyle, DWORD dwExStyle) : Window(width, height, owner, dwStyle | WS_MAXIMIZEBOX | WS_MINIMIZEBOX | WS_POPUP, dwExStyle)
|
||||
{
|
||||
//无边框
|
||||
auto style = ::GetWindowLong(Hwnd(), GWL_STYLE);
|
||||
style |= WS_THICKFRAME;
|
||||
::SetWindowLong(Hwnd(), GWL_STYLE, style);
|
||||
|
||||
m_shadowBox = new ShadowBox(width, height, Hwnd());
|
||||
UpdateShadowBox();
|
||||
}
|
||||
BorderlessWindow::~BorderlessWindow() {
|
||||
CloseShadowBox();
|
||||
}
|
||||
ShadowBox* BorderlessWindow::GetShadowBox()
|
||||
{
|
||||
return m_shadowBox;
|
||||
}
|
||||
void BorderlessWindow::SetShadow(int padding)
|
||||
{
|
||||
m_shadowWeight = padding;
|
||||
UpdateShadowBox();
|
||||
}
|
||||
|
||||
void BorderlessWindow::OnDpiChange(float systemScale, const Rect& newRect)
|
||||
{
|
||||
if (m_shadowBox) {//对窗口阴影进行新DPI适配
|
||||
if (this->m_shadowScale != systemScale) {
|
||||
this->m_shadowWeight *= systemScale / m_shadowScale;
|
||||
this->m_shadowScale = systemScale;
|
||||
UpdateShadowBox();
|
||||
}
|
||||
}
|
||||
//对边框进行新DPI适配
|
||||
float newScale = systemScale / GetPublicData()->Scale;
|
||||
__super::OnDpiChange(systemScale, newRect);
|
||||
}
|
||||
|
||||
void BorderlessWindow::SetResizable(bool resize) {
|
||||
this->m_bResize = resize;
|
||||
}
|
||||
|
||||
bool BorderlessWindow::IsResizable() {
|
||||
return this->m_bResize;
|
||||
}
|
||||
|
||||
HWND BorderlessWindow::GetShadowHwnd()
|
||||
{
|
||||
if (m_shadowBox) {
|
||||
return m_shadowBox->Hwnd();
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void BorderlessWindow::UpdateShadowBox() {
|
||||
if (m_shadowBox && ::IsWindow(m_shadowBox->Hwnd())) {
|
||||
auto* mainLayout = this->GetLayout();
|
||||
int shadowWeight = m_shadowWeight;
|
||||
m_shadowBox->Update(shadowWeight * this->GetScale(), (mainLayout ? mainLayout->GetBorderTopLeftRadius() : 0));
|
||||
}
|
||||
}
|
||||
void BorderlessWindow::CloseShadowBox()
|
||||
{
|
||||
if (m_shadowBox) {
|
||||
delete m_shadowBox;
|
||||
m_shadowBox = NULL;
|
||||
}
|
||||
}
|
||||
LRESULT BorderlessWindow::WndProc(UINT uMsg, WPARAM wParam, LPARAM lParam) {
|
||||
switch (uMsg)
|
||||
{
|
||||
case WM_NCCALCSIZE:
|
||||
{
|
||||
if (wParam == TRUE) {
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case WM_NCPAINT:
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
case WM_NCACTIVATE:
|
||||
{
|
||||
return (wParam == FALSE ? TRUE : FALSE);
|
||||
}
|
||||
case WM_WINDOWPOSCHANGED:
|
||||
{
|
||||
WINDOWPOS* pPos = (WINDOWPOS*)lParam;
|
||||
//在窗口显示/隐藏的时候更新阴影窗口
|
||||
if ((pPos->flags & SWP_SHOWWINDOW) || (pPos->flags & SWP_HIDEWINDOW)) {
|
||||
this->UpdateShadowBox();
|
||||
}
|
||||
// Z-order 发生变化
|
||||
if ((pPos->flags & SWP_NOZORDER) == 0) {
|
||||
this->UpdateShadowBox();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case WM_ACTIVATE: {
|
||||
auto state = LOWORD(wParam);
|
||||
if (state == WA_ACTIVE || state == WA_CLICKACTIVE) {
|
||||
// 窗口激活
|
||||
}
|
||||
else if (state == WA_INACTIVE) {
|
||||
// 窗口失活
|
||||
}
|
||||
this->UpdateShadowBox();
|
||||
break;
|
||||
}
|
||||
case WM_SHOWWINDOW: {
|
||||
//窗口的可见状态标志被设置(此时窗口还未真正生效)
|
||||
if (wParam == TRUE) {
|
||||
//SHOW WINDOWS
|
||||
}
|
||||
else {
|
||||
//HIDE WINDOWS
|
||||
}
|
||||
break;
|
||||
}
|
||||
case WM_NCHITTEST:
|
||||
{
|
||||
//非全屏状态下/非最大化状态下/当调整大小的标志为true 的情况下才允许调整大小;
|
||||
if (!IsFullScreen() && !IsMaximized() && this->IsResizable()) {
|
||||
RECT rc;
|
||||
::GetWindowRect(Hwnd(), &rc);
|
||||
POINT pt{ GET_X_LPARAM(lParam),GET_Y_LPARAM(lParam) };
|
||||
int x = 4;//
|
||||
if (pt.x < rc.left + x)
|
||||
{
|
||||
if (pt.y < rc.top + x)return HTTOPLEFT;//
|
||||
if (pt.y >= rc.bottom - x)return HTBOTTOMLEFT;//
|
||||
return HTLEFT;//
|
||||
}
|
||||
if (pt.x >= rc.right - x)//
|
||||
{
|
||||
if (pt.y < rc.top + x)return HTTOPRIGHT;//
|
||||
if (pt.y >= rc.bottom - x)return HTBOTTOMRIGHT;//
|
||||
return HTRIGHT;//
|
||||
}
|
||||
if (pt.y < rc.top + x)return HTTOP;//
|
||||
if (pt.y >= rc.bottom - x)return HTBOTTOM;//
|
||||
return HTCLIENT;//
|
||||
}
|
||||
break;
|
||||
}
|
||||
case WM_KILLFOCUS: {
|
||||
HWND wnd = (HWND)wParam;
|
||||
if (this->GetShadowBox() && wnd != this->GetShadowBox()->Hwnd()) {
|
||||
this->OnKillFocus(wnd);
|
||||
}
|
||||
else if (this->GetShadowBox() == NULL) {
|
||||
this->OnKillFocus(wnd);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return __super::WndProc(uMsg, wParam, lParam);
|
||||
}
|
||||
void BorderlessWindow::OnMove(const Point& location)
|
||||
{
|
||||
__super::OnMove(location);
|
||||
UpdateShadowBox();
|
||||
}
|
||||
void BorderlessWindow::OnSize(const Size& sz)
|
||||
{
|
||||
__super::OnSize(sz);//避免先显示阴影再显示窗口的问题
|
||||
UpdateShadowBox();
|
||||
}
|
||||
}
|
||||
15
sources/Button.cpp
Normal file
15
sources/Button.cpp
Normal file
@@ -0,0 +1,15 @@
|
||||
#include "Button.h"
|
||||
|
||||
namespace ezui {
|
||||
void Button::Init()
|
||||
{
|
||||
Style.Cursor = LoadCursor(Cursor::HAND);
|
||||
}
|
||||
Button::Button(Object* parentObject):Label(parentObject)
|
||||
{
|
||||
Init();
|
||||
}
|
||||
Button::~Button()
|
||||
{
|
||||
}
|
||||
};
|
||||
83
sources/CheckBox.cpp
Normal file
83
sources/CheckBox.cpp
Normal file
@@ -0,0 +1,83 @@
|
||||
#include "CheckBox.h"
|
||||
|
||||
namespace ezui {
|
||||
CheckBox::CheckBox(Object* parentObject) :Label(parentObject)
|
||||
{
|
||||
}
|
||||
CheckBox::~CheckBox()
|
||||
{
|
||||
}
|
||||
void CheckBox::SetCheck(bool checked)
|
||||
{
|
||||
m_checked = checked;
|
||||
if (checked) {
|
||||
this->State = this->State | ControlState::Checked;
|
||||
}
|
||||
else {
|
||||
this->State = this->State & (~ControlState::Checked);
|
||||
}
|
||||
}
|
||||
bool CheckBox::GetCheck()
|
||||
{
|
||||
return m_checked;
|
||||
}
|
||||
void CheckBox::OnDpiChange(const DpiChangeEventArgs& args) {
|
||||
if (!ezui::IsFloatEqual(args.Scale, this->GetScale())) {
|
||||
this->CheckedStyle.Scale(args.Scale / this->GetScale());
|
||||
}
|
||||
__super::OnDpiChange(args);
|
||||
}
|
||||
void CheckBox::SetAttribute(const UIString& key, const UIString& value)
|
||||
{
|
||||
__super::SetAttribute(key, value);
|
||||
if (key == "checked") {
|
||||
if (value == "true") {
|
||||
SetCheck(true);
|
||||
return;
|
||||
}
|
||||
if (value == "false") {
|
||||
SetCheck(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ControlStyle& CheckBox::GetStyle(const ControlState& _state) {
|
||||
if ((_state & ControlState::Checked) == ControlState::Checked) {
|
||||
return this->CheckedStyle;
|
||||
}
|
||||
return __super::GetStyle(_state);
|
||||
}
|
||||
|
||||
void CheckBox::OnMouseDown(const MouseEventArgs& arg) {
|
||||
__super::OnMouseDown(arg);
|
||||
SetCheck(!GetCheck());
|
||||
if (CheckedChanged) {
|
||||
CheckedChanged(this, GetCheck());
|
||||
}
|
||||
if (GetCheck()) {
|
||||
this->State = this->State | ControlState::Checked;
|
||||
this->Invalidate();
|
||||
}
|
||||
}
|
||||
void CheckBox::OnMouseEnter(const MouseEventArgs& args)
|
||||
{
|
||||
__super::OnMouseEnter(args);
|
||||
if (GetCheck()) {
|
||||
this->State = this->State | ControlState::Checked;
|
||||
this->Invalidate();
|
||||
}
|
||||
}
|
||||
void CheckBox::OnMouseUp(const MouseEventArgs& args)
|
||||
{
|
||||
__super::OnMouseUp(args);
|
||||
}
|
||||
void CheckBox::OnMouseLeave(const MouseEventArgs& args)
|
||||
{
|
||||
__super::OnMouseLeave(args);
|
||||
if (GetCheck()) {
|
||||
this->State = this->State | ControlState::Checked;
|
||||
this->Invalidate();
|
||||
}
|
||||
}
|
||||
};
|
||||
152
sources/ComboBox.cpp
Normal file
152
sources/ComboBox.cpp
Normal file
@@ -0,0 +1,152 @@
|
||||
#include "ComboBox.h"
|
||||
|
||||
namespace ezui {
|
||||
Control* ComboBox::Add(Control* childCtl)
|
||||
{
|
||||
return __super::Add(childCtl);
|
||||
}
|
||||
void ComboBox::Remove(Control* childCtl, bool freeCtrl)
|
||||
{
|
||||
__super::Remove(childCtl, freeCtrl);
|
||||
}
|
||||
void ComboBox::Init()
|
||||
{
|
||||
this->m_textBox.SetReadOnly(true);
|
||||
this->Add(&m_textBox);
|
||||
this->Add(&m_UpDown);
|
||||
|
||||
m_UpDown.EventHandler = [&](Control* sd, EventArgs& arg)->void {
|
||||
if (arg.EventType == Event::OnPaint) {
|
||||
//绘制
|
||||
auto& args = (PaintEventArgs&)arg;
|
||||
|
||||
auto fSzie = sd->GetFontSize() * 0.5f;
|
||||
int width = fSzie * 1.5f;
|
||||
|
||||
Rect rect(0, 0, width, fSzie);
|
||||
rect.Y = (sd->Height() - fSzie) / 2.0f;
|
||||
rect.X += (sd->Height() - width) / 2.0f;
|
||||
|
||||
//args.Graphics.SetColor(Color::Red);
|
||||
//args.Graphics.DrawRectangle(rect);
|
||||
|
||||
PointF p1(rect.GetLeft(), rect.Y);
|
||||
PointF p2(rect.GetRight(), rect.Y);
|
||||
PointF p3(rect.GetLeft() + width / 2.0f, rect.GetBottom());
|
||||
|
||||
args.Graphics.SetColor(this->GetForeColor());
|
||||
args.Graphics.DrawLine(p1, p3);
|
||||
args.Graphics.DrawLine(p2, p3);
|
||||
}
|
||||
else if (arg.EventType == Event::OnMouseDown/*&& args.Button == MouseButton::Left*/) {
|
||||
//单击
|
||||
if (m_menuWnd == NULL) {
|
||||
m_menuWnd = new MenuContent(this, &m_UpDown);
|
||||
m_menuWnd->SetShadow(10);
|
||||
m_list.Style.BackColor = Color::White;
|
||||
m_menuWnd->SetLayout(&m_list);
|
||||
}
|
||||
for (auto& it : m_list.GetControls()) {
|
||||
it->SetFixedHeight(Height());
|
||||
}
|
||||
int height = this->Height() * m_list.GetControls().size();
|
||||
if (height == 0) {
|
||||
height = Height();
|
||||
}
|
||||
if (!m_menuWnd->IsVisible()) {
|
||||
m_menuWnd->SetSize({ Width(), height });
|
||||
m_menuWnd->Show();
|
||||
}
|
||||
else {
|
||||
m_menuWnd->Hide();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
ComboBox::ComboBox(Object* parentObject) :HLayout(parentObject)
|
||||
{
|
||||
Init();
|
||||
}
|
||||
UIString ComboBox::GetText()
|
||||
{
|
||||
return this->m_textBox.GetText();
|
||||
}
|
||||
int ComboBox::GetCheck()
|
||||
{
|
||||
return this->m_index;
|
||||
}
|
||||
bool ComboBox::SetCheck(int pos)
|
||||
{
|
||||
auto item = m_list.GetControl(pos);
|
||||
if (item) {
|
||||
m_textBox.SetText(((Label*)item)->GetText());
|
||||
m_index = pos;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
ComboBox::~ComboBox()
|
||||
{
|
||||
m_list.Clear(true);
|
||||
if (m_menuWnd) {
|
||||
delete m_menuWnd;
|
||||
}
|
||||
}
|
||||
int ComboBox::AddItem(const UIString& text)
|
||||
{
|
||||
Label* lb = new Label;
|
||||
lb->SetDockStyle(DockStyle::Horizontal);
|
||||
lb->SetText(text);
|
||||
m_list.Add(lb);
|
||||
lb->HoverStyle.BackColor = Color::Gray;
|
||||
lb->HoverStyle.ForeColor = Color::White;
|
||||
|
||||
lb->EventHandler = [&](Control* sd, const EventArgs& args) ->void {
|
||||
if (args.EventType == Event::OnMouseDown) {
|
||||
m_index = sd->Parent->IndexOf(sd);
|
||||
m_textBox.SetText(((Label*)sd)->GetText());
|
||||
m_textBox.Invalidate();
|
||||
m_menuWnd->Hide();
|
||||
}
|
||||
};
|
||||
|
||||
return m_list.GetControls().size() - 1;
|
||||
}
|
||||
void ComboBox::RemoveItem(int index)
|
||||
{
|
||||
Control* lb = m_list.GetControl(index);
|
||||
m_list.Remove(lb, true);
|
||||
}
|
||||
void ComboBox::OnLayout() {
|
||||
this->m_UpDown.SetFixedSize(Size(Height(), Height()));
|
||||
__super::OnLayout();
|
||||
}
|
||||
|
||||
ComboBox::MenuContent::MenuContent(Control* ownerCtl, Control* hittestCtl) :PopupWindow(0, 0, ownerCtl), m_hittestCtl(hittestCtl)
|
||||
{
|
||||
}
|
||||
void ComboBox::MenuContent::OnKillFocus(HWND wnd)
|
||||
{
|
||||
if (::GetWindow(Hwnd(), GW_OWNER) == wnd) {
|
||||
POINT pt;
|
||||
::GetCursorPos(&pt);
|
||||
// 将鼠标屏幕坐标转换为客户端坐标
|
||||
::ScreenToClient(::GetWindow(Hwnd(), GW_OWNER), &pt);
|
||||
Rect _hittestRect = m_hittestCtl->GetClientRect();
|
||||
if (_hittestRect.Contains(pt.x, pt.y)) {
|
||||
return;
|
||||
}
|
||||
else {
|
||||
this->Hide();
|
||||
}
|
||||
}
|
||||
else {
|
||||
this->Hide();
|
||||
}
|
||||
}
|
||||
ComboBox::MenuContent::~MenuContent()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
1792
sources/Control.cpp
Normal file
1792
sources/Control.cpp
Normal file
File diff suppressed because it is too large
Load Diff
1272
sources/Direct2DRender.cpp
Normal file
1272
sources/Direct2DRender.cpp
Normal file
File diff suppressed because it is too large
Load Diff
605
sources/EzUI.cpp
Normal file
605
sources/EzUI.cpp
Normal file
@@ -0,0 +1,605 @@
|
||||
#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);
|
||||
}
|
||||
}
|
||||
};
|
||||
90
sources/HLayout.cpp
Normal file
90
sources/HLayout.cpp
Normal file
@@ -0,0 +1,90 @@
|
||||
#include "HLayout.h"
|
||||
namespace ezui {
|
||||
void HLayout::SetAttribute(const UIString& key, const UIString& value)
|
||||
{
|
||||
if (key == "valign" || key == "align") {
|
||||
if (value == "top") {
|
||||
ContentAlign = VAlign::Top;
|
||||
}
|
||||
else if (value == "bottom") {
|
||||
ContentAlign = VAlign::Bottom;
|
||||
}
|
||||
}
|
||||
__super::SetAttribute(key, value);
|
||||
}
|
||||
void HLayout::OnLayout()
|
||||
{
|
||||
int contentHeight = 0;
|
||||
int fixedWidth = 0;
|
||||
int fixedTotal = 0;
|
||||
int count = 0;//可见控件总数
|
||||
for (auto& it : GetControls()) {
|
||||
if ((it->IsAutoWidth() || it->IsAutoHeight()) && it->IsPendLayout()) {
|
||||
it->RefreshLayout();
|
||||
}
|
||||
if (it->IsVisible() == false || (it->IsAutoWidth() && it->GetFixedWidth() <= 0)) continue;
|
||||
++count;
|
||||
auto width = it->GetFixedWidth();
|
||||
if (width > 0) {
|
||||
fixedWidth += width;
|
||||
++fixedTotal;
|
||||
}
|
||||
fixedWidth += +it->Margin.GetHSpace();
|
||||
}
|
||||
int autoTotal = count - fixedTotal;
|
||||
if (autoTotal == 0) {
|
||||
//return;
|
||||
}
|
||||
double otherWidth = Width() * 1.0 - fixedWidth;
|
||||
double autoWidth = otherWidth / autoTotal;
|
||||
double maxRight = 0;
|
||||
//排序
|
||||
for (auto& it : GetControls()) {
|
||||
if (it->IsVisible() == false) continue;
|
||||
|
||||
maxRight += it->Margin.Left;
|
||||
int height = it->GetFixedHeight();
|
||||
int y = it->Y();
|
||||
|
||||
if (height == 0) {
|
||||
//当控件未指定绝对高度的时候
|
||||
height = this->Height() - it->Margin.GetVSpace();
|
||||
y = it->Margin.Top;
|
||||
}
|
||||
else {
|
||||
//当控件指定了绝对高度的时候
|
||||
if (ContentAlign == VAlign::Top) {
|
||||
y = it->Margin.Top;
|
||||
}
|
||||
else if (ContentAlign == VAlign::Mid) {
|
||||
y = int((this->Height() * 1.0 - height) / 2.0f + 0.5);
|
||||
}
|
||||
else if (ContentAlign == VAlign::Bottom) {
|
||||
y = this->Height() - it->Margin.Bottom - height;
|
||||
}
|
||||
}
|
||||
|
||||
if (it->GetFixedWidth() > 0 || it->IsAutoWidth()) {
|
||||
it->SetRect({ (int)maxRight ,y,it->GetFixedWidth() ,height });
|
||||
maxRight += it->Width();
|
||||
}
|
||||
else {
|
||||
it->SetRect({ (int)maxRight ,y,(int)autoWidth,height });
|
||||
maxRight += it->Width();
|
||||
}
|
||||
maxRight += it->Margin.Right;
|
||||
//统计上下文宽高
|
||||
int maxBottom = it->GetRect().GetBottom() + it->Margin.Bottom;
|
||||
if (maxBottom > contentHeight) {
|
||||
contentHeight = maxBottom;
|
||||
}
|
||||
this->SetContentSize({ (int)(maxRight + 0.5) ,contentHeight });
|
||||
}
|
||||
}
|
||||
HLayout::HLayout(Object* parentObject) :Control(parentObject)
|
||||
{
|
||||
}
|
||||
HLayout::~HLayout()
|
||||
{
|
||||
}
|
||||
};
|
||||
92
sources/HListView.cpp
Normal file
92
sources/HListView.cpp
Normal file
@@ -0,0 +1,92 @@
|
||||
#include "HListView.h"
|
||||
|
||||
namespace ezui {
|
||||
void HListView::Init()
|
||||
{
|
||||
this->GetScrollBar()->SetWidth(Width());//滚动条宽度
|
||||
this->GetScrollBar()->Parent = this;
|
||||
this->GetScrollBar()->OffsetCallback = [this](int offsetValue)->void {
|
||||
if (this->GetScrollBar()->ScrollPos() >= 1) {
|
||||
NextPage();
|
||||
}
|
||||
this->Offset(offsetValue);
|
||||
};
|
||||
}
|
||||
HListView::HListView(Object* parentObject) :PagedListView(parentObject)
|
||||
{
|
||||
Init();
|
||||
}
|
||||
HListView::~HListView()
|
||||
{
|
||||
}
|
||||
void HListView::OnLayout() {
|
||||
this->Offset(0);
|
||||
if (IsAutoWidth()) {
|
||||
this->GetScrollBar()->SetVisible(false);
|
||||
}
|
||||
else if (this->GetScrollBar()->IsVisible() == true) {
|
||||
this->GetScrollBar()->SetVisible(true);
|
||||
}
|
||||
this->GetScrollBar()->RefreshScroll();
|
||||
}
|
||||
|
||||
ScrollBar* HListView::GetScrollBar()
|
||||
{
|
||||
return &m_hScrollBar;
|
||||
}
|
||||
void HListView::Offset(int offset) {
|
||||
int _contentHeight = 0;
|
||||
int _contentWidth = offset;
|
||||
for (auto& it : GetControls()) {
|
||||
if (it->IsVisible() == false) {
|
||||
it->SetX(0);
|
||||
continue;
|
||||
}
|
||||
//处理y坐标和margin
|
||||
{
|
||||
int height = it->GetFixedHeight();
|
||||
if (height == 0) {
|
||||
height = this->Height() - it->Margin.GetVSpace();
|
||||
}
|
||||
int y = it->Y();
|
||||
if (y == 0) {
|
||||
y = it->Margin.Top;
|
||||
}
|
||||
if (y == 0 && height < this->Height()) {
|
||||
y = int((this->Height() * 1.0 - height) / 2 + 0.5);
|
||||
}
|
||||
_contentWidth += it->Margin.Left;
|
||||
it->SetRect(Rect(_contentWidth, y, it->Width(), height));
|
||||
_contentWidth += it->Width();
|
||||
_contentWidth += it->Margin.Right;
|
||||
}
|
||||
//计算最大高度
|
||||
int _height = it->Y() + it->Height() + it->Margin.Bottom;
|
||||
if (_height > _contentHeight) {
|
||||
_contentHeight = _height;
|
||||
}
|
||||
}
|
||||
this->SetContentSize({ _contentWidth - offset,_contentHeight });
|
||||
}
|
||||
|
||||
void HListView::OnChildPaint(PaintEventArgs& args) {
|
||||
ViewControls.clear();
|
||||
//绘制子控件
|
||||
auto rect = Rect(0, 0, Width(), Height());
|
||||
for (auto& it : GetControls()) {
|
||||
if (rect.IntersectsWith(it->GetRect())) {
|
||||
ViewControls.push_back(it);
|
||||
}
|
||||
if (it->X() >= Width()) {
|
||||
break;
|
||||
////当控件超出容器底部将不再派发绘制事件 但是仍然要进行布局
|
||||
//if (it->IsAutoWidth() && it->GetLayoutState() == LayoutState::Pend) {
|
||||
// it->RefreshLayout();
|
||||
//}
|
||||
}
|
||||
else {
|
||||
it->SendEvent(args);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
70
sources/HScrollBar.cpp
Normal file
70
sources/HScrollBar.cpp
Normal file
@@ -0,0 +1,70 @@
|
||||
#include "HScrollBar.h"
|
||||
|
||||
namespace ezui {
|
||||
HScrollBar::HScrollBar(Object* parentObj):ScrollBar(parentObj)
|
||||
{
|
||||
}
|
||||
HScrollBar::~HScrollBar() {}
|
||||
Rect HScrollBar::GetSliderRect() {
|
||||
Rect sliderRect;
|
||||
sliderRect.X = (int)m_sliderPos;
|
||||
sliderRect.Y = 0;
|
||||
sliderRect.Width = m_sliderLength;
|
||||
sliderRect.Height = Height();
|
||||
if (this->Scrollable() && sliderRect.Width <= 0) {
|
||||
sliderRect.Width = 1;
|
||||
}
|
||||
return sliderRect;
|
||||
}
|
||||
void HScrollBar::ScrollTo(Control* ctl)
|
||||
{
|
||||
if (ctl && ctl->Parent && ctl->Parent == this->Parent) {
|
||||
if (ctl->Parent->IsPendLayout()) {
|
||||
ctl->Parent->RefreshLayout();
|
||||
}
|
||||
//控件的矩形位置
|
||||
const Rect& ctlRect = ctl->GetRect();
|
||||
if (ctlRect.X >= 0 && ctlRect.GetRight() <= Width()) {
|
||||
return;
|
||||
}
|
||||
//出现在顶部
|
||||
int offset = this->m_offset - ctlRect.X;
|
||||
if (ctlRect.Y > 0) {
|
||||
//出现在底部
|
||||
offset += this->m_viewLength - ctlRect.Width;
|
||||
}
|
||||
__super::ScrollTo(offset, Event::None);
|
||||
}
|
||||
}
|
||||
void HScrollBar::GetInfo(int* viewLength, int* contentLength, int* scrollBarLength)
|
||||
{
|
||||
*viewLength = this->Parent->Width();
|
||||
*contentLength = this->Parent->GetContentSize().Width;
|
||||
*scrollBarLength = Width();
|
||||
}
|
||||
void HScrollBar::ParentSize(const Size& size) {
|
||||
this->SetRect({ 0,size.Height - this->Height(),size.Width,Height() });
|
||||
}
|
||||
void HScrollBar::OnMouseDown(const MouseEventArgs& arg) {
|
||||
__super::OnMouseDown(arg);
|
||||
Rect sliderRect = GetSliderRect();
|
||||
if (sliderRect.IsEmptyArea()) { return; }
|
||||
auto point = arg.Location;
|
||||
if (arg.Button == MouseButton::Left && sliderRect.Contains({ point.X,point.Y })) {
|
||||
m_mouseDown = true;
|
||||
this->m_lastPoint = point.X;
|
||||
}
|
||||
}
|
||||
void HScrollBar::OnMouseMove(const MouseEventArgs& arg)
|
||||
{
|
||||
__super::OnMouseMove(arg);
|
||||
auto point = arg.Location;
|
||||
if (m_mouseDown) {
|
||||
int offsetX = point.X - this->m_lastPoint;
|
||||
m_sliderPos += offsetX;
|
||||
m_lastPoint = point.X;
|
||||
int offset = m_sliderPos * this->m_rollRate + 0.5;
|
||||
__super::ScrollTo(-offset, Event::OnMouseDrag);
|
||||
}
|
||||
}
|
||||
};
|
||||
56
sources/IFrame.cpp
Normal file
56
sources/IFrame.cpp
Normal file
@@ -0,0 +1,56 @@
|
||||
#include "IFrame.h"
|
||||
namespace ezui {
|
||||
IFrame::IFrame(Object* parentObject) :Control(parentObject)
|
||||
{
|
||||
}
|
||||
IFrame::~IFrame()
|
||||
{
|
||||
}
|
||||
UIManager* IFrame::GetUIManager()
|
||||
{
|
||||
return &m_umg;
|
||||
}
|
||||
void IFrame::SetAttribute(const UIString& attrName, const UIString& attrValue) {
|
||||
if (attrName == "src") {
|
||||
LoadXml(attrValue);
|
||||
}
|
||||
__super::SetAttribute(attrName, attrValue);
|
||||
};
|
||||
void IFrame::LoadXml(const UIString& fileName) {
|
||||
m_umg.LoadXml(fileName);
|
||||
m_umg.SetupUI(this);
|
||||
}
|
||||
void IFrame::LoadXml(const char* fileData, size_t fileSize) {
|
||||
m_umg.LoadXml(fileData, fileSize);
|
||||
m_umg.SetupUI(this);
|
||||
}
|
||||
Control* IFrame::Add(Control* childCtrl)
|
||||
{
|
||||
//IFrame下只允许有一个控件并且会随着IFrame拉伸
|
||||
this->Clear();
|
||||
if (childCtrl) {
|
||||
auto* ctrl = __super::Add(childCtrl);
|
||||
childCtrl->SetDockStyle(DockStyle::Fill);
|
||||
return ctrl;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
void IFrame::Remove(Control* childCtl, bool freeCtrl)
|
||||
{
|
||||
__super::Remove(childCtl, freeCtrl);
|
||||
}
|
||||
void IFrame::SetLayout(Control* ctrl) {
|
||||
this->Add(ctrl);
|
||||
}
|
||||
Control* IFrame::GetLayout() {
|
||||
return this->GetControl(0);
|
||||
}
|
||||
void IFrame::OnNotify(Control* sender, EventArgs& args) {
|
||||
if (this->NotifyHandler) {
|
||||
this->NotifyHandler(sender, args);
|
||||
}
|
||||
else {
|
||||
ezui::DefaultNotify(sender, args);
|
||||
}
|
||||
}
|
||||
}
|
||||
161
sources/Label.cpp
Normal file
161
sources/Label.cpp
Normal file
@@ -0,0 +1,161 @@
|
||||
#include "Label.h"
|
||||
namespace ezui {
|
||||
Label::Label(Object* parentObject) :Control(parentObject)
|
||||
{
|
||||
}
|
||||
Label::~Label() {}
|
||||
void Label::OnDpiChange(const DpiChangeEventArgs& args) {
|
||||
if (args.Scale != this->GetScale()) {
|
||||
this->TextMargin.Scale(args.Scale / this->GetScale());
|
||||
}
|
||||
__super::OnDpiChange(args);
|
||||
}
|
||||
|
||||
void Label::OnForePaint(PaintEventArgs& args)
|
||||
{
|
||||
__super::OnForePaint(args);
|
||||
if (!m_wstr.empty()) {
|
||||
int maxWidth = Width() - this->TextMargin.GetHSpace();
|
||||
int maxHeight = Height() - this->TextMargin.GetVSpace();
|
||||
std::wstring drawText(m_wstr);
|
||||
std::wstring fontFamily = GetFontFamily();
|
||||
auto fontSize = GetFontSize();
|
||||
if (fontSize == 0)return;
|
||||
Font font(fontFamily, fontSize);
|
||||
args.Graphics.SetFont(font);
|
||||
args.Graphics.SetColor(GetForeColor());
|
||||
const std::wstring& wEllipsisText = m_ellipsisText;
|
||||
if (!wEllipsisText.empty()) { //水平文本溢出的显示方案
|
||||
Size ellipsisTextSize;
|
||||
{
|
||||
TextLayout textLayout(wEllipsisText, font);
|
||||
ellipsisTextSize = textLayout.GetFontBox();
|
||||
}
|
||||
TextLayout textLayout(m_wstr, font);
|
||||
|
||||
if (textLayout.GetFontBox().Width > maxWidth) {//当文字显示超出的时候 宽度
|
||||
int pos = 0;
|
||||
BOOL isTrailingHit;
|
||||
int fontHeight;
|
||||
textLayout.HitTestPoint({ maxWidth,0 }, &pos, &isTrailingHit, &fontHeight);//对文字进行命中测试
|
||||
drawText.erase(pos);
|
||||
while (drawText.size() > 0)
|
||||
{
|
||||
//从最后往前删除文字 直到可以显示正常为止
|
||||
drawText.erase(drawText.size() - 1, 1);
|
||||
TextLayout textLayout(drawText, font);
|
||||
if (textLayout.GetFontBox().Width + ellipsisTextSize.Width < maxWidth) {
|
||||
drawText.append(wEllipsisText);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
std::wstring viewStr = !drawText.empty() ? drawText : wEllipsisText;
|
||||
TextLayout textLayout(viewStr, font, SizeF(maxWidth, maxHeight), (IsAutoWidth() && IsAutoHeight()) ? TextAlign::TopLeft : this->TextAlign);
|
||||
if (this->m_underlineCount != 0) {//下划线
|
||||
textLayout.SetUnderline(m_underlinePos, m_underlineCount);
|
||||
}
|
||||
args.Graphics.DrawTextLayout(textLayout, { (float)this->TextMargin.Left,(float)this->TextMargin.Top });
|
||||
}
|
||||
}
|
||||
|
||||
void Label::SetAttribute(const UIString& key, const UIString& value) {
|
||||
do
|
||||
{
|
||||
if (key == "valign" || key == "align") {
|
||||
this->TextAlign = ezui::TextAlign((int)this->TextAlign & ~(int)VAlign::Top);
|
||||
this->TextAlign = ezui::TextAlign((int)this->TextAlign & ~(int)VAlign::Mid);
|
||||
this->TextAlign = ezui::TextAlign((int)this->TextAlign & ~(int)VAlign::Bottom);
|
||||
VAlign v = VAlign::Mid;
|
||||
if (value == "top") {
|
||||
v = VAlign::Top;
|
||||
}
|
||||
else if (value == "bottom") {
|
||||
v = VAlign::Bottom;
|
||||
}
|
||||
this->TextAlign = ezui::TextAlign((int)this->TextAlign | (int)v);
|
||||
break;
|
||||
}
|
||||
if (key == "halign" || key == "align") {
|
||||
this->TextAlign = ezui::TextAlign((int)this->TextAlign & ~(int)HAlign::Left);
|
||||
this->TextAlign = ezui::TextAlign((int)this->TextAlign & ~(int)HAlign::Center);
|
||||
this->TextAlign = ezui::TextAlign((int)this->TextAlign & ~(int)HAlign::Right);
|
||||
HAlign h = HAlign::Center;
|
||||
if (value == "left") {
|
||||
h = HAlign::Left;
|
||||
}
|
||||
else if (value == "right") {
|
||||
h = HAlign::Right;
|
||||
}
|
||||
this->TextAlign = ezui::TextAlign((int)this->TextAlign | (int)h);
|
||||
break;
|
||||
}
|
||||
if (key == "text") {
|
||||
this->SetText(value);
|
||||
break;
|
||||
}
|
||||
if (key == "underline") {
|
||||
size_t pos = value.find(",");
|
||||
this->m_underlinePos = std::atoi(value.substr(0, pos + 1).c_str());
|
||||
this->m_underlineCount = std::atoi(value.substr(pos + 1, pos).c_str());
|
||||
break;
|
||||
}
|
||||
if (key == "ellipsis") {
|
||||
this->SetElidedText(value);
|
||||
break;
|
||||
}
|
||||
} while (false);
|
||||
__super::SetAttribute(key, value);
|
||||
}
|
||||
void Label::RefreshLayout()
|
||||
{
|
||||
//比较特殊需要屏蔽
|
||||
this->OnLayout();
|
||||
}
|
||||
void Label::OnLayout() {
|
||||
__super::OnLayout();
|
||||
if (IsAutoWidth() || IsAutoHeight()) {
|
||||
|
||||
auto fontSize = GetFontSize();
|
||||
if (fontSize == 0)return;
|
||||
Font font(GetFontFamily(), fontSize);
|
||||
|
||||
int maxWidth = IsAutoWidth() ? EZUI_FLOAT_MAX : Width() - this->TextMargin.GetHSpace();
|
||||
int maxHeight = IsAutoHeight() ? EZUI_FLOAT_MAX : Height() - this->TextMargin.GetVSpace();
|
||||
|
||||
TextLayout text(this->m_wstr, font, SizeF(maxWidth, maxHeight));
|
||||
Size box = text.GetFontBox();
|
||||
this->SetContentSize(box);
|
||||
if (IsAutoWidth()) {
|
||||
this->SetFixedWidth(box.Width + this->TextMargin.GetHSpace());
|
||||
if (Parent) {
|
||||
Parent->RefreshLayout();
|
||||
}
|
||||
}
|
||||
if (IsAutoHeight()) {
|
||||
this->SetFixedHeight(box.Height + this->TextMargin.GetVSpace());
|
||||
if (Parent) {
|
||||
Parent->RefreshLayout();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
void Label::SetText(const UIString& text) {
|
||||
m_wstr = text.unicode();
|
||||
this->TryPendLayout();
|
||||
}
|
||||
void Label::SetUnderline(int pos, int count)
|
||||
{
|
||||
this->m_underlinePos = pos;
|
||||
this->m_underlineCount = count;
|
||||
}
|
||||
UIString Label::GetText()const
|
||||
{
|
||||
return UIString(m_wstr);
|
||||
}
|
||||
void Label::SetElidedText(const UIString& text)
|
||||
{
|
||||
this->m_ellipsisText = text.unicode();
|
||||
}
|
||||
};
|
||||
147
sources/LayeredWindow.cpp
Normal file
147
sources/LayeredWindow.cpp
Normal file
@@ -0,0 +1,147 @@
|
||||
#include "LayeredWindow.h"
|
||||
|
||||
namespace ezui {
|
||||
|
||||
std::vector<LayeredWindow*> g_layeredWnds;
|
||||
Timer g_layeredWndTimer;
|
||||
|
||||
//WS_EX_LAYERED | WS_EX_NOACTIVATE | WS_EX_TRANSPARENT
|
||||
LayeredWindow::LayeredWindow(int width, int height, HWND owner, DWORD dwStyle, DWORD dwExStyle) :BorderlessWindow(width, height, owner, dwStyle, dwExStyle | WS_EX_LAYERED)
|
||||
{
|
||||
//初始化全局绘制计时器
|
||||
if (g_layeredWndTimer.Tick == NULL) {
|
||||
g_layeredWndTimer.Interval = 5;
|
||||
g_layeredWndTimer.Tick = [](Timer* t) {
|
||||
t->Stop();
|
||||
Invoke([]() {
|
||||
for (auto& it : g_layeredWnds) {
|
||||
it->Paint();
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
//添加到全局绘制队列
|
||||
g_layeredWnds.push_back(this);
|
||||
//获取公共数据
|
||||
auto* publicData = this->GetPublicData();
|
||||
publicData->InvalidateRect = [this](const Rect& rect) ->void {
|
||||
//标记窗口无效区域
|
||||
this->InvalidateRect(rect);
|
||||
};
|
||||
publicData->Refresh = [this]()->void {
|
||||
//立即更新窗口中的无效区域
|
||||
this->Paint();
|
||||
};
|
||||
//获取客户区大小 创建一个位图给窗口绘制
|
||||
Size sz = GetClientRect().GetSize();
|
||||
m_winBitmap = new Bitmap(sz.Width, sz.Height);
|
||||
}
|
||||
LayeredWindow::~LayeredWindow() {
|
||||
if (m_winBitmap) {
|
||||
delete m_winBitmap;
|
||||
}
|
||||
//从全局中移除
|
||||
auto itor = std::find(g_layeredWnds.begin(), g_layeredWnds.end(), this);
|
||||
if (itor != g_layeredWnds.end()) {
|
||||
g_layeredWnds.erase(itor);
|
||||
}
|
||||
}
|
||||
|
||||
void LayeredWindow::InvalidateRect(const Rect& _rect) {
|
||||
//将此区域添加到无效区域
|
||||
m_invalidateRect.push_back(_rect);
|
||||
//timer延迟绘制
|
||||
g_layeredWndTimer.Start();
|
||||
}
|
||||
|
||||
void LayeredWindow::BeginPaint(Rect* out_rect)
|
||||
{
|
||||
const Rect& clientRect = GetClientRect();
|
||||
int Width = clientRect.Width;
|
||||
int Height = clientRect.Height;
|
||||
//将所有无效区域并集
|
||||
for (auto& it : m_invalidateRect) {
|
||||
Rect rect = it;
|
||||
//这段代码是保证重绘区域一定是在窗口内
|
||||
if (rect.X < 0) {
|
||||
rect.Width += rect.X;
|
||||
rect.X = 0;
|
||||
if (rect.Width < 0) rect.Width = 0;
|
||||
}
|
||||
if (rect.Y < 0) {
|
||||
rect.Height += rect.Y;
|
||||
rect.Y = 0;
|
||||
if (rect.Height < 0) rect.Height = 0;
|
||||
}
|
||||
if (rect.GetRight() > Width) {
|
||||
rect.Width = Width - rect.X;
|
||||
if (rect.Width < 0) rect.Width = 0;
|
||||
}
|
||||
if (rect.GetBottom() > Height) {
|
||||
rect.Height = Height - rect.Y;
|
||||
if (rect.Height < 0) rect.Height = 0;
|
||||
}
|
||||
//并集
|
||||
Rect::Union(*out_rect, *out_rect, rect);
|
||||
}
|
||||
}
|
||||
void LayeredWindow::EndPaint()
|
||||
{
|
||||
m_invalidateRect.clear();
|
||||
}
|
||||
void LayeredWindow::Paint()
|
||||
{
|
||||
if (IsVisible()) {
|
||||
Rect invalidateRect;
|
||||
BeginPaint(&invalidateRect);
|
||||
if ((m_winBitmap && !invalidateRect.IsEmptyArea())) {
|
||||
m_winBitmap->Earse(invalidateRect);//清除背景
|
||||
HDC winHDC = m_winBitmap->GetDC();
|
||||
#if 1
|
||||
//不使用双缓冲
|
||||
DoPaint(winHDC, invalidateRect);
|
||||
#else
|
||||
//使用双缓冲
|
||||
Bitmap doubleBuff(m_winBitmap->Width(), m_winBitmap->Height());
|
||||
DoPaint(doubleBuff.GetHDC(), invalidateRect);
|
||||
//使用BitBlt函数进行复制到winHDC //如果窗体不规则 不适用于BitBlt进行复制
|
||||
::BitBlt(winHDC, invalidateRect.X, invalidateRect.Y,
|
||||
invalidateRect.Width, invalidateRect.Height,
|
||||
doubleBuff.GetHDC(), invalidateRect.X, invalidateRect.Y,
|
||||
SRCCOPY);
|
||||
#endif
|
||||
UpdateLayeredWindow(winHDC);//updatelaredwindow 更新窗口
|
||||
EndPaint();
|
||||
}
|
||||
}
|
||||
}
|
||||
LRESULT LayeredWindow::WndProc(UINT uMsg, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
switch (uMsg)
|
||||
{
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return __super::WndProc(uMsg, wParam, lParam);
|
||||
}
|
||||
void LayeredWindow::OnSize(const Size& sz) {
|
||||
__super::OnSize(sz);
|
||||
if (m_winBitmap) {
|
||||
delete m_winBitmap;
|
||||
}
|
||||
m_winBitmap = new Bitmap(sz.Width, sz.Height);
|
||||
Invalidate();
|
||||
}
|
||||
|
||||
void LayeredWindow::UpdateLayeredWindow(HDC hdc) {
|
||||
const Rect& _rectClient = GetClientRect();
|
||||
POINT point{ _rectClient.X,_rectClient.Y };
|
||||
SIZE size{ _rectClient.Width, _rectClient.Height };
|
||||
BLENDFUNCTION blendFunc{ 0 };
|
||||
blendFunc.SourceConstantAlpha = 255 * this->Opacity;
|
||||
blendFunc.BlendOp = AC_SRC_OVER;
|
||||
blendFunc.AlphaFormat = AC_SRC_ALPHA;
|
||||
blendFunc.BlendFlags = 0;
|
||||
::UpdateLayeredWindow(Hwnd(), NULL, NULL, &size, hdc, &point, 0, &blendFunc, ULW_ALPHA);//透明
|
||||
}
|
||||
}
|
||||
54
sources/Menu.cpp
Normal file
54
sources/Menu.cpp
Normal file
@@ -0,0 +1,54 @@
|
||||
#include "Menu.h"
|
||||
|
||||
namespace ezui {
|
||||
UINT_PTR g_nextId = WM_APP + 1;
|
||||
inline UINT_PTR GenerateMenuId() {
|
||||
++g_nextId;
|
||||
return g_nextId;
|
||||
}
|
||||
|
||||
Menu::Menu(Object* parentObj) : Object(parentObj)
|
||||
{
|
||||
m_hMenu = ::CreatePopupMenu(); // 创建空菜单
|
||||
}
|
||||
|
||||
UINT_PTR Menu::Append(const UIString& text)
|
||||
{
|
||||
UINT_PTR id = GenerateMenuId();
|
||||
if (m_hMenu) {
|
||||
::AppendMenuW(m_hMenu, MF_STRING, id, text.unicode().c_str());
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
void Menu::Remove(UINT_PTR itemID)
|
||||
{
|
||||
if (!m_hMenu) return;
|
||||
int count = ::GetMenuItemCount(m_hMenu);
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
MENUITEMINFO mii = {};
|
||||
mii.cbSize = sizeof(mii);
|
||||
mii.fMask = MIIM_ID;
|
||||
|
||||
if (::GetMenuItemInfo(m_hMenu, i, TRUE, &mii)) {
|
||||
if (mii.wID == itemID) {
|
||||
::RemoveMenu(m_hMenu, i, MF_BYPOSITION);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Menu::~Menu()
|
||||
{
|
||||
if (m_hMenu) {
|
||||
::DestroyMenu(m_hMenu);
|
||||
}
|
||||
}
|
||||
|
||||
HMENU Menu::HMenu()
|
||||
{
|
||||
return m_hMenu;
|
||||
}
|
||||
|
||||
};
|
||||
163
sources/NotifyIcon.cpp
Normal file
163
sources/NotifyIcon.cpp
Normal file
@@ -0,0 +1,163 @@
|
||||
#include "NotifyIcon.h"
|
||||
#define TRAY_ICON_MSG WM_APP+1 // 自定义托盘消息
|
||||
|
||||
namespace ezui {
|
||||
bool g_bClassRegistered = false;
|
||||
NotifyIcon::NotifyIcon(Object* parentObj) :Object(parentObj)
|
||||
{
|
||||
if (!g_bClassRegistered) {
|
||||
::WNDCLASSEXW wcex = {};
|
||||
wcex.cbSize = sizeof(wcex);
|
||||
wcex.lpfnWndProc = [](HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)->LRESULT {
|
||||
WindowData* wndData = (WindowData*)UI_GET_USERDATA(hwnd);
|
||||
if (wndData && wndData->WndProc) {
|
||||
return wndData->WndProc(hwnd, message, wParam, lParam);
|
||||
}
|
||||
return ::DefWindowProc(hwnd, message, wParam, lParam);
|
||||
};
|
||||
wcex.hInstance = ezui::__EzUI__HINSTANCE;
|
||||
wcex.lpszClassName = L"EzUI_NotifyIcon";
|
||||
RegisterClassExW(&wcex);
|
||||
g_bClassRegistered = true;
|
||||
}
|
||||
m_hWnd = CreateWindowEx(WS_EX_LAYERED | WS_EX_NOACTIVATE | WS_EX_TOOLWINDOW, L"EzUI_NotifyIcon", L"", 0, 0, 0, 0, 0, NULL, NULL, ezui::__EzUI__HINSTANCE, NULL);
|
||||
m_publicData.WndProc = [this](HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)->LRESULT {
|
||||
return this->WndProc(uMsg, wParam, lParam);
|
||||
};
|
||||
UI_SET_USERDATA(m_hWnd, (LONG_PTR)&m_publicData);
|
||||
m_nid.cbSize = sizeof(m_nid);//结构体长度
|
||||
m_nid.hWnd = m_hWnd;//窗口句柄
|
||||
m_nid.uCallbackMessage = TRAY_ICON_MSG;//消息处理,这里很重要,处理鼠标点击
|
||||
m_nid.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
|
||||
Shell_NotifyIconW(NIM_ADD, &m_nid);
|
||||
}
|
||||
|
||||
LRESULT NotifyIcon::WndProc(UINT uMsg, WPARAM wParam, LPARAM lParam) {
|
||||
if (uMsg == TRAY_ICON_MSG) {
|
||||
POINT point;
|
||||
GetCursorPos(&point);
|
||||
switch (LOWORD(lParam))
|
||||
{
|
||||
case WM_MOUSEMOVE: {
|
||||
if (EventHandler) {
|
||||
MouseEventArgs args(Event::OnMouseMove, { point.x, point.y }, MouseButton::None);
|
||||
EventHandler(args);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case WM_LBUTTONDOWN: {
|
||||
if (EventHandler) {
|
||||
MouseEventArgs args(Event::OnMouseDown, { point.x, point.y }, MouseButton::Left);
|
||||
EventHandler(args);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case WM_LBUTTONUP: {
|
||||
if (EventHandler) {
|
||||
MouseEventArgs args(Event::OnMouseUp, { point.x, point.y }, MouseButton::Left);
|
||||
EventHandler(args);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case WM_LBUTTONDBLCLK: {
|
||||
if (EventHandler) {
|
||||
MouseEventArgs args(Event::OnMouseDoubleClick, { point.x, point.y }, MouseButton::Left);
|
||||
EventHandler(args);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case WM_RBUTTONDOWN: {
|
||||
if (this->EventHandler) {
|
||||
MouseEventArgs args(Event::OnMouseDown, { point.x,point.y }, MouseButton::Right);
|
||||
this->EventHandler(args);
|
||||
}
|
||||
if (m_menu) {
|
||||
//如果设置了托盘菜单则弹出菜单
|
||||
SetForegroundWindow(m_hWnd);
|
||||
TrackPopupMenu(m_menu->HMenu(), TPM_RIGHTBUTTON, point.x, point.y, 0, m_hWnd, NULL);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case WM_RBUTTONUP: {
|
||||
if (EventHandler) {
|
||||
MouseEventArgs args(Event::OnMouseUp, { point.x, point.y }, MouseButton::Right);
|
||||
EventHandler(args);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case WM_RBUTTONDBLCLK: {
|
||||
if (EventHandler) {
|
||||
MouseEventArgs args(Event::OnMouseDoubleClick, { point.x, point.y }, MouseButton::Right);
|
||||
EventHandler(args);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case WM_MBUTTONDOWN: {
|
||||
if (EventHandler) {
|
||||
MouseEventArgs args(Event::OnMouseDown, { point.x, point.y }, MouseButton::Middle);
|
||||
EventHandler(args);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case WM_MBUTTONUP: {
|
||||
if (EventHandler) {
|
||||
MouseEventArgs args(Event::OnMouseUp, { point.x, point.y }, MouseButton::Middle);
|
||||
EventHandler(args);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case WM_MBUTTONDBLCLK: {
|
||||
if (EventHandler) {
|
||||
MouseEventArgs args(Event::OnMouseDoubleClick, { point.x, point.y }, MouseButton::Middle);
|
||||
EventHandler(args);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (uMsg == WM_COMMAND) {
|
||||
//菜单被点击
|
||||
auto id = LOWORD(wParam);
|
||||
if (m_menu && m_menu->MouseClick) {
|
||||
m_menu->MouseClick(id);
|
||||
}
|
||||
}
|
||||
return ::DefWindowProc(m_hWnd, uMsg, wParam, lParam);
|
||||
}
|
||||
|
||||
void NotifyIcon::SetIcon(HICON icon)
|
||||
{
|
||||
m_nid.hIcon = icon;
|
||||
m_nid.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
|
||||
Shell_NotifyIconW(NIM_MODIFY, &m_nid);
|
||||
}
|
||||
|
||||
void NotifyIcon::SetMenu(Menu* menu) {
|
||||
this->m_menu = menu;
|
||||
}
|
||||
|
||||
void NotifyIcon::SetTips(const UIString& text)
|
||||
{
|
||||
wcscpy_s(m_nid.szTip, text.unicode().c_str());
|
||||
Shell_NotifyIconW(NIM_MODIFY, &m_nid);
|
||||
}
|
||||
|
||||
void NotifyIcon::ShowBalloonTip(const UIString& title, const UIString& msg, int timeOut) {
|
||||
m_nid.uTimeout = timeOut;
|
||||
m_nid.uFlags = NIF_INFO;
|
||||
m_nid.dwInfoFlags = NIIF_INFO;
|
||||
wcscpy_s(m_nid.szInfoTitle, title.unicode().c_str());
|
||||
wcscpy_s(m_nid.szInfo, msg.unicode().c_str());
|
||||
Shell_NotifyIconW(NIM_MODIFY, &m_nid);
|
||||
}
|
||||
|
||||
NotifyIcon::~NotifyIcon()
|
||||
{
|
||||
if (::IsWindow(m_hWnd)) {
|
||||
Shell_NotifyIconW(NIM_DELETE, &m_nid);
|
||||
::DestroyWindow(m_hWnd);
|
||||
}
|
||||
}
|
||||
};
|
||||
60
sources/PagedListView.cpp
Normal file
60
sources/PagedListView.cpp
Normal file
@@ -0,0 +1,60 @@
|
||||
#include "PagedListView.h"
|
||||
namespace ezui {
|
||||
|
||||
void PagedListView::SetPageInfo(const Controls& items, int pageSize)
|
||||
{
|
||||
this->m_pageIndex = 0;
|
||||
this->m_pageSize = pageSize;
|
||||
this->m_pageTotal = items.size() / pageSize + (items.size() % pageSize == 0 ? 0 : 1);
|
||||
this->m_items = items;
|
||||
this->NextPage();
|
||||
}
|
||||
|
||||
void PagedListView::GetPage(int pageIndex, Controls* outCtls)
|
||||
{
|
||||
if (outCtls) {
|
||||
outCtls->clear();
|
||||
int beginIndex = (pageIndex - 1) * m_pageSize;
|
||||
int count = 0;
|
||||
for (int i = beginIndex; count < m_pageSize && i < m_items.size(); ++i)
|
||||
{
|
||||
outCtls->push_back(m_items[i]);
|
||||
++count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PagedListView::Clear() {
|
||||
__super::Clear();
|
||||
}
|
||||
|
||||
void PagedListView::Clear(bool freeChilds) {
|
||||
__super::Clear(freeChilds);
|
||||
this->m_items.clear();
|
||||
this->m_pageIndex = 0;
|
||||
this->m_pageTotal = 0;
|
||||
}
|
||||
|
||||
void PagedListView::NextPage()
|
||||
{
|
||||
if (m_items.size() <= 0)return;
|
||||
if ((m_pageIndex + 1) > m_pageTotal)return;
|
||||
if (NextPaging && NextPaging(this, (m_pageIndex + 1)) == false) {
|
||||
return;
|
||||
}
|
||||
++m_pageIndex;
|
||||
Controls ctls;
|
||||
this->GetPage(m_pageIndex, &ctls);
|
||||
for (auto& it : ctls) {
|
||||
this->Add(it);
|
||||
}
|
||||
this->Invalidate();
|
||||
}
|
||||
|
||||
PagedListView::PagedListView(Object* patentObject):Control(patentObject)
|
||||
{
|
||||
}
|
||||
PagedListView::~PagedListView()
|
||||
{
|
||||
}
|
||||
};
|
||||
38
sources/PictureBox.cpp
Normal file
38
sources/PictureBox.cpp
Normal file
@@ -0,0 +1,38 @@
|
||||
#include "PictureBox.h"
|
||||
namespace ezui {
|
||||
PictureBox::PictureBox(Object* parentObject) :Control(parentObject)
|
||||
{
|
||||
Init();
|
||||
}
|
||||
void PictureBox::Init() {
|
||||
m_timer = new Timer(this);
|
||||
m_timer->Tick = [this](Timer* timer) {
|
||||
timer->Stop();
|
||||
HWND hWnd = this->Hwnd();
|
||||
BeginInvoke([this, hWnd, timer]() {
|
||||
if (!::IsWindow(hWnd))return;
|
||||
if (this->Image) {
|
||||
m_timer->Interval = this->Image->NextFrame();
|
||||
this->Invalidate();
|
||||
};
|
||||
});
|
||||
};
|
||||
}
|
||||
PictureBox::~PictureBox() {
|
||||
}
|
||||
void PictureBox::OnForePaint(PaintEventArgs& arg) {
|
||||
if (Image) {
|
||||
arg.Graphics.DrawImage(Image, RectF(0, 0, (float)Width(), (float)Height()));
|
||||
if (Image->FrameCount() > 1) {
|
||||
m_timer->Start();
|
||||
}
|
||||
}
|
||||
__super::OnForePaint(arg);
|
||||
}
|
||||
void PictureBox::SetAttribute(const UIString& key, const UIString& value) {
|
||||
__super::SetAttribute(key, value);
|
||||
if (key == "src") {
|
||||
Image = this->Attach(Image::Make(value));
|
||||
}
|
||||
}
|
||||
};
|
||||
72
sources/PopupWindow.cpp
Normal file
72
sources/PopupWindow.cpp
Normal file
@@ -0,0 +1,72 @@
|
||||
#include "PopupWindow.h"
|
||||
namespace ezui {
|
||||
PopupWindow::PopupWindow(int width, int height, HWND owner) :LayeredWindow(width, height, owner)
|
||||
{
|
||||
this->SetResizable(false);
|
||||
}
|
||||
PopupWindow::PopupWindow(int width, int height, Control* ownerCtl) :LayeredWindow(width, height, ownerCtl->Hwnd())
|
||||
{
|
||||
this->m_ownerCtl = ownerCtl;
|
||||
this->SetResizable(false);
|
||||
}
|
||||
void PopupWindow::Show()
|
||||
{
|
||||
int x, y, width, height;
|
||||
const Rect& rect = this->GetClientRect();
|
||||
Rect ctlRect;
|
||||
|
||||
POINT location;
|
||||
if (m_ownerCtl) {
|
||||
ctlRect = m_ownerCtl->GetClientRect();
|
||||
location.x = ctlRect.GetLeft();
|
||||
location.y = ctlRect.GetBottom();
|
||||
::ClientToScreen(m_ownerCtl->Hwnd(), &location);
|
||||
}
|
||||
else {
|
||||
::GetCursorPos(&location);
|
||||
}
|
||||
|
||||
MonitorInfo monitorInfo;
|
||||
GetMontior(&monitorInfo, ::GetWindow(Hwnd(), GW_OWNER));
|
||||
x = location.x;
|
||||
y = location.y;
|
||||
width = rect.Width;
|
||||
height = rect.Height;
|
||||
if (m_ownerCtl) {
|
||||
width = ctlRect.Width;
|
||||
}
|
||||
if ((location.y + height) > monitorInfo.Rect.GetBottom()) {
|
||||
y -= height;
|
||||
if (m_ownerCtl) {
|
||||
y -= ctlRect.Height;
|
||||
}
|
||||
}
|
||||
|
||||
//添加一点点偏移 不要紧贴着控件
|
||||
if (y >= location.y) {
|
||||
y += 1;
|
||||
}
|
||||
else {
|
||||
y -= 1;
|
||||
}
|
||||
|
||||
this->SetRect({ x, y, width, height });
|
||||
__super::Show();
|
||||
::SetForegroundWindow(Hwnd());
|
||||
this->SetTopMost(true);
|
||||
}
|
||||
|
||||
int PopupWindow::ShowModal(bool disableOnwer)
|
||||
{
|
||||
return __super::ShowModal(disableOnwer);
|
||||
}
|
||||
|
||||
PopupWindow::~PopupWindow()
|
||||
{
|
||||
}
|
||||
|
||||
void PopupWindow::OnKillFocus(HWND hWnd)
|
||||
{
|
||||
::DestroyWindow(Hwnd());
|
||||
}
|
||||
};
|
||||
118
sources/ProgressBar.cpp
Normal file
118
sources/ProgressBar.cpp
Normal file
@@ -0,0 +1,118 @@
|
||||
#include "ProgressBar.h"
|
||||
|
||||
namespace ezui {
|
||||
|
||||
ProgressBar::ProgressBar(Object* parentObject) : Control(parentObject) {
|
||||
// 设置默认大小
|
||||
SetFixedSize(Size(200, 25));
|
||||
}
|
||||
|
||||
ProgressBar::~ProgressBar() {
|
||||
}
|
||||
|
||||
void ProgressBar::SetProgress(float progress) {
|
||||
// 限制进度值在 0.0f - 1.0f 范围内
|
||||
float newProgress = (((0.0f) > ((((1.0f) < (progress)) ? (1.0f) : (progress)))) ? (0.0f) : ((((1.0f) < (progress)) ? (1.0f) : (progress))));
|
||||
if (m_progress != newProgress) {
|
||||
m_progress = newProgress;
|
||||
Invalidate(); // 触发重绘
|
||||
}
|
||||
}
|
||||
|
||||
float ProgressBar::GetProgress() const {
|
||||
return m_progress;
|
||||
}
|
||||
|
||||
void ProgressBar::SetProgressColor(const Color& color) {
|
||||
if (m_progressColor.GetValue() != color.GetValue()) {
|
||||
m_progressColor = color;
|
||||
Invalidate(); // 触发重绘
|
||||
}
|
||||
}
|
||||
|
||||
Color ProgressBar::GetProgressColor() const {
|
||||
return m_progressColor;
|
||||
}
|
||||
|
||||
void ProgressBar::SetBackgroundColor(const Color& color) {
|
||||
if (m_backgroundColor.GetValue() != color.GetValue()) {
|
||||
m_backgroundColor = color;
|
||||
Invalidate(); // 触发重绘
|
||||
}
|
||||
}
|
||||
|
||||
Color ProgressBar::GetBackgroundColor() const {
|
||||
return m_backgroundColor;
|
||||
}
|
||||
|
||||
void ProgressBar::SetAttribute(const UIString& attrName, const UIString& attrValue) {
|
||||
if (attrName == "progress") {
|
||||
// 设置进度值,支持百分比和小数
|
||||
float progress = 0.0f;
|
||||
if (attrValue.find("%") != UIString::npos) {
|
||||
// 百分比格式,如 "50%"
|
||||
progress = std::atof(attrValue.substr(0, attrValue.length() - 1).c_str()) / 100.0f;
|
||||
} else {
|
||||
// 小数格式,如 "0.5"
|
||||
progress = std::atof(attrValue.c_str());
|
||||
}
|
||||
SetProgress(progress);
|
||||
} else if (attrName == "progress-color") {
|
||||
// 设置进度条颜色
|
||||
bool isGood = false;
|
||||
Color color = Color::Make(attrValue, &isGood);
|
||||
if (isGood) {
|
||||
SetProgressColor(color);
|
||||
}
|
||||
} else {
|
||||
// 调用基类方法处理其他属性
|
||||
Control::SetAttribute(attrName, attrValue);
|
||||
}
|
||||
}
|
||||
|
||||
bool ProgressBar::ApplyStyleProperty(const UIString& key, const UIString& value) {
|
||||
if (key == "progress") {
|
||||
// CSS 样式中的进度设置
|
||||
SetAttribute("progress", value);
|
||||
return true;
|
||||
} else if (key == "progress-color") {
|
||||
// CSS 样式中的进度条颜色设置
|
||||
SetAttribute("progress-color", value);
|
||||
return true;
|
||||
} else if (key == "background-color") {
|
||||
// 背景颜色
|
||||
bool isGood = false;
|
||||
Color color = Color::Make(value, &isGood);
|
||||
if (isGood) {
|
||||
SetBackgroundColor(color);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// 调用基类方法处理其他样式
|
||||
return Control::ApplyStyleProperty(key, value);
|
||||
}
|
||||
|
||||
void ProgressBar::OnPaint(PaintEventArgs& args) {
|
||||
// 获取控件矩形
|
||||
RectF rect(0, 0, (float)Width(), (float)Height());
|
||||
|
||||
// 绘制背景
|
||||
args.Graphics.SetColor(m_backgroundColor);
|
||||
args.Graphics.FillRectangle(rect);
|
||||
|
||||
// 绘制进度
|
||||
if (m_progress > 0.0f) {
|
||||
args.Graphics.SetColor(m_progressColor);
|
||||
float progressWidth = rect.Width * m_progress;
|
||||
RectF progressRect(0, 0, progressWidth, rect.Height);
|
||||
args.Graphics.FillRectangle(progressRect);
|
||||
}
|
||||
|
||||
// 绘制边框(如果有设置)
|
||||
Border border = GetStyle(State).Border;
|
||||
if (border.Style != StrokeStyle::None && (border.Left > 0 || border.Top > 0 || border.Right > 0 || border.Bottom > 0)) {
|
||||
OnBorderPaint(args, border);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
23
sources/RadioButton.cpp
Normal file
23
sources/RadioButton.cpp
Normal file
@@ -0,0 +1,23 @@
|
||||
#include "RadioButton.h"
|
||||
namespace ezui {
|
||||
RadioButton::RadioButton(Object* parentObject):CheckBox(parentObject)
|
||||
{
|
||||
}
|
||||
void RadioButton::OnMouseDown(const MouseEventArgs& arg)
|
||||
{
|
||||
__super::OnMouseDown(arg);
|
||||
SetCheck(true);
|
||||
if (GetCheck() == true) {
|
||||
for (auto& it : Parent->GetControls()) {
|
||||
RadioButton* rbtn = dynamic_cast<RadioButton*>(it);
|
||||
if (rbtn && rbtn != this && rbtn->GetCheck() == true) {
|
||||
rbtn->SetCheck(false);
|
||||
rbtn->Invalidate();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
RadioButton::~RadioButton()
|
||||
{
|
||||
}
|
||||
};
|
||||
249
sources/Resource.cpp
Normal file
249
sources/Resource.cpp
Normal file
@@ -0,0 +1,249 @@
|
||||
#include "Resource.h"
|
||||
#include "EzUI.h"
|
||||
namespace ezui {
|
||||
//判断文件是否存在
|
||||
bool DirectoryExists(const UIString& directoryNme) {
|
||||
DWORD dwAttr = GetFileAttributesW(directoryNme.unicode().c_str());
|
||||
if (dwAttr == DWORD(-1)) {
|
||||
return false;
|
||||
}
|
||||
if (dwAttr & FILE_ATTRIBUTE_DIRECTORY)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
//寻找指定目录以及目录下的所有文件
|
||||
void FindFilesRecursively(const std::wstring& path, std::list<std::wstring>* result) {
|
||||
WIN32_FIND_DATAW findData;
|
||||
HANDLE findHandle = FindFirstFileW((path + L"/*").c_str(), &findData);
|
||||
if (findHandle == INVALID_HANDLE_VALUE) {
|
||||
return;
|
||||
}
|
||||
do
|
||||
{
|
||||
if (findData.cFileName[0] == L'.') {
|
||||
continue;
|
||||
}
|
||||
if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
|
||||
// 如果是目录,递归查找该目录
|
||||
std::wstring newPath = path + L"/" + findData.cFileName;
|
||||
FindFilesRecursively(newPath, result);
|
||||
}
|
||||
else {
|
||||
std::wstring newPath = path + L"/" + findData.cFileName;
|
||||
result->push_back(newPath);
|
||||
}
|
||||
} while (FindNextFileW(findHandle, &findData));
|
||||
FindClose(findHandle);
|
||||
}
|
||||
}
|
||||
|
||||
namespace ezui {
|
||||
Resource::ReadStream::ReadStream(HRSRC hRsrc) {
|
||||
this->m_ptr = (char*)::LoadResource(ezui::__EzUI__HINSTANCE, hRsrc);
|
||||
this->m_count = ::SizeofResource(ezui::__EzUI__HINSTANCE, hRsrc);
|
||||
}
|
||||
Resource::ReadStream::ReadStream(const UIString& fileName) {
|
||||
this->m_ifs = new std::ifstream(fileName.unicode(), std::ios::binary);
|
||||
this->m_ifs->seekg(0, std::ios::end);
|
||||
this->m_count = m_ifs->tellg();
|
||||
this->m_ifs->seekg(0);
|
||||
}
|
||||
void Resource::ReadStream::seekg(std::streampos pos) {
|
||||
if (m_ifs) {
|
||||
m_ifs->seekg(pos);
|
||||
}
|
||||
else {
|
||||
this->m_pos = pos;
|
||||
}
|
||||
}
|
||||
void Resource::ReadStream::read(char* buf, std::streamsize count) {
|
||||
if (m_ifs) {
|
||||
m_ifs->read(buf, count);
|
||||
}
|
||||
else {
|
||||
::memcpy(buf, m_ptr + m_pos, count);
|
||||
this->m_pos += count;
|
||||
}
|
||||
}
|
||||
std::streampos Resource::ReadStream::tellg() {
|
||||
if (m_ifs) {
|
||||
return m_ifs->tellg();
|
||||
}
|
||||
else {
|
||||
return m_pos;
|
||||
}
|
||||
}
|
||||
const std::streamsize Resource::ReadStream::size() {
|
||||
return this->m_count;
|
||||
}
|
||||
Resource::ReadStream::~ReadStream() {
|
||||
if (m_ifs) {
|
||||
delete m_ifs;
|
||||
}
|
||||
if (m_ptr) {
|
||||
::FreeResource((HGLOBAL)m_ptr);
|
||||
}
|
||||
}
|
||||
|
||||
bool Resource::Package(const UIString& dir, const UIString& outFile, const std::function<void(const UIString&, int, int)>& packCallback) {
|
||||
if (dir.empty() || !DirectoryExists(dir)) {
|
||||
std::wstring err = UIString("Error: Invalid directory: \"%s\"\n").format(dir.c_str()).unicode();
|
||||
OutputDebugStringW(err.c_str());
|
||||
ASSERT(!"Invalid directory !");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::list<Entry> items;
|
||||
std::list<std::wstring> files;
|
||||
FindFilesRecursively(dir.unicode(), &files);
|
||||
std::streamsize headOffset = sizeof(std::streamsize);
|
||||
std::ofstream ofs(outFile.unicode(), std::ios::binary);
|
||||
ofs.seekp(headOffset);
|
||||
for (auto& file : files) {
|
||||
//读取文件大小
|
||||
std::ifstream ifs(file, std::ios::binary);
|
||||
ifs.seekg(0, std::ios::end);
|
||||
auto size = ifs.tellg();
|
||||
//读取文件
|
||||
UIString data;
|
||||
data.resize(size);
|
||||
ifs.seekg(0);
|
||||
ifs.read((char*)data.c_str(), size);
|
||||
//记录文件名称,偏移,大小
|
||||
Entry item;
|
||||
item.Name = file;
|
||||
item.Size = size;
|
||||
item.Offset = headOffset;
|
||||
ofs.write(data.c_str(), data.size());
|
||||
headOffset += data.size();
|
||||
items.push_back(item);
|
||||
}
|
||||
//将偏移文件头偏移信息写入到文件初始位置
|
||||
ofs.seekp(0);
|
||||
ofs.write((char*)(&headOffset), sizeof(headOffset));
|
||||
//设置到文件条目位置
|
||||
ofs.seekp(headOffset);
|
||||
//处理路径
|
||||
UIString root = dir + "/";
|
||||
root = root.replace("\\", "/");
|
||||
root = root.replace("//", "/");
|
||||
if (root[root.size() - 1] == '/') {
|
||||
root.erase(root.size() - 1, 1);
|
||||
}
|
||||
size_t pos = root.rfind("/");
|
||||
if (pos != std::string::npos) {
|
||||
root = root.substr(0, pos + 1);
|
||||
}
|
||||
int index = 0;
|
||||
for (auto& item : items) {
|
||||
//写入文件偏移位置
|
||||
ofs.write((char*)(&item.Offset), sizeof(headOffset));
|
||||
//写入文件大小
|
||||
ofs.write((char*)(&item.Size), sizeof(headOffset));
|
||||
//文件路径名称
|
||||
UIString name = item.Name;
|
||||
name = name.replace("\\", "/");
|
||||
name = name.replace("//", "/");
|
||||
name = name.replace(root, "", false);
|
||||
ofs.write(name.c_str(), name.size() + 1);
|
||||
if (packCallback) {
|
||||
packCallback(name, index, items.size());
|
||||
}
|
||||
index++;
|
||||
}
|
||||
//首位呼应
|
||||
ofs.write((char*)(&headOffset), sizeof(headOffset));
|
||||
ofs.flush();
|
||||
ofs.close();
|
||||
return ofs.good();
|
||||
}
|
||||
|
||||
void Resource::UnPackage() {
|
||||
m_isGood = false;
|
||||
std::list<Entry>& items = (std::list<Entry>&)this->Items;
|
||||
auto& ifs = *(this->m_rStream);
|
||||
if (ifs.size() < sizeof(std::streamsize) * 2) {
|
||||
//不是标准的资源文件 不执行解析
|
||||
ASSERT(!"error resource");
|
||||
return;
|
||||
}
|
||||
//读取记录文件位置的偏移
|
||||
ifs.seekg(0);
|
||||
std::streamsize headOffset;
|
||||
ifs.read((char*)&headOffset, sizeof(headOffset));
|
||||
//读取末尾
|
||||
ifs.seekg(ifs.size() - sizeof(headOffset));
|
||||
std::streamsize endValue;
|
||||
ifs.read((char*)&endValue, sizeof(headOffset));
|
||||
if (headOffset != endValue) {
|
||||
//不是标准的资源文件 不执行解析
|
||||
ASSERT(!"error resource");
|
||||
return;
|
||||
}
|
||||
m_isGood = true;
|
||||
//开始读取文件剩余条目
|
||||
ifs.seekg(headOffset);
|
||||
std::streampos endPos = ifs.size() - sizeof(headOffset);
|
||||
while (ifs.tellg() < endPos)
|
||||
{
|
||||
//读取到文件偏移位置
|
||||
std::streamsize fileOffset;
|
||||
ifs.read((char*)&fileOffset, sizeof(headOffset));
|
||||
//读取文件大小
|
||||
std::streamsize fileSize;
|
||||
ifs.read((char*)&fileSize, sizeof(headOffset));
|
||||
//读取文件名称
|
||||
std::vector<char> buf(32768);
|
||||
for (int i = 0; i < buf.size(); i++)
|
||||
{
|
||||
char ch;
|
||||
ifs.read((char*)&ch, 1);
|
||||
buf[i] = ch;
|
||||
if (ch == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Entry item;
|
||||
item.Offset = fileOffset;
|
||||
item.Size = fileSize;
|
||||
item.Name = buf.data();
|
||||
items.push_back(item);
|
||||
}
|
||||
}
|
||||
|
||||
Resource::Resource(const UIString& resFile) {
|
||||
this->m_rStream = new ReadStream(resFile);
|
||||
this->UnPackage();
|
||||
}
|
||||
Resource::Resource(HRSRC hRsrc) {
|
||||
this->m_rStream = new ReadStream(hRsrc);
|
||||
this->UnPackage();
|
||||
}
|
||||
Resource:: ~Resource() {
|
||||
if (m_rStream) {
|
||||
delete m_rStream;
|
||||
}
|
||||
}
|
||||
bool Resource::GetFile(const UIString& fileName, std::string* out) {
|
||||
for (const auto& it : this->Items) {
|
||||
if (it.Name == fileName) {
|
||||
out->resize(it.Size);
|
||||
m_rStream->seekg(it.Offset);
|
||||
m_rStream->read((char*)out->c_str(), it.Size);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool Resource::IsGood() {
|
||||
return m_isGood;
|
||||
}
|
||||
void Resource::GetFile(const Entry& item, std::string* out) {
|
||||
out->resize(item.Size);
|
||||
m_rStream->seekg(item.Offset);
|
||||
m_rStream->read((char*)out->c_str(), item.Size);
|
||||
}
|
||||
};
|
||||
|
||||
154
sources/ScrollBar.cpp
Normal file
154
sources/ScrollBar.cpp
Normal file
@@ -0,0 +1,154 @@
|
||||
#include "ScrollBar.h"
|
||||
//滚动条相关
|
||||
namespace ezui {
|
||||
void ScrollBar::OnMouseUp(const MouseEventArgs& arg)
|
||||
{
|
||||
//::ReleaseCapture();
|
||||
__super::OnMouseUp(arg);
|
||||
m_mouseDown = false;
|
||||
}
|
||||
void ScrollBar::OnMouseLeave(const MouseEventArgs& arg)
|
||||
{
|
||||
__super::OnMouseLeave(arg);
|
||||
m_mouseDown = false;
|
||||
}
|
||||
ScrollBar::ScrollBar(Object* parentObject) :Control(parentObject)
|
||||
{
|
||||
Init();
|
||||
}
|
||||
void ScrollBar::Init() {
|
||||
Style.ForeColor = Color(205, 205, 205);//默认滑块颜色
|
||||
SetSize({ 10,10 });
|
||||
}
|
||||
ScrollBar::~ScrollBar() {}
|
||||
bool ScrollBar::IsDraw()
|
||||
{
|
||||
if (!Scrollable()) {
|
||||
return false;
|
||||
}
|
||||
return this->IsVisible();
|
||||
}
|
||||
void ScrollBar::Reset()
|
||||
{
|
||||
this->m_sliderPos = 0;
|
||||
this->m_offset = 0;
|
||||
}
|
||||
bool ScrollBar::Scrollable()
|
||||
{
|
||||
if (this->m_overflowLength > 0) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
void ScrollBar::OnBackgroundPaint(PaintEventArgs& e) {
|
||||
if (!Scrollable()) {
|
||||
return;
|
||||
}
|
||||
__super::OnBackgroundPaint(e);
|
||||
}
|
||||
void ScrollBar::OnForePaint(PaintEventArgs& args)
|
||||
{
|
||||
if (!Scrollable()) {
|
||||
return;
|
||||
}
|
||||
__super::OnForePaint(args);
|
||||
//绘制滑块
|
||||
RectF sliderRect = GetSliderRect();
|
||||
Color color = GetForeColor();
|
||||
if (color.GetValue() != 0) {
|
||||
args.Graphics.SetColor(color);
|
||||
args.Graphics.FillRectangle(sliderRect, GetBorderTopLeftRadius());
|
||||
}
|
||||
}
|
||||
|
||||
void ScrollBar::OnMouseDown(const MouseEventArgs& arg)
|
||||
{
|
||||
//::SetCapture(this->PublicData->HANDLE);
|
||||
__super::OnMouseDown(arg);
|
||||
}
|
||||
|
||||
void ScrollBar::ScrollTo(float scrollRate) {
|
||||
if (Parent == NULL) return;
|
||||
if (Parent->IsPendLayout()) {
|
||||
Parent->RefreshLayout();
|
||||
}
|
||||
int offset = scrollRate * this->m_overflowLength;
|
||||
ScrollTo(-offset, Event::None);
|
||||
}
|
||||
|
||||
float ScrollBar::ScrollPos()
|
||||
{
|
||||
if (this->m_overflowLength <= 0) return 1.0f;
|
||||
return std::abs(this->m_offset) * 1.0f / this->m_overflowLength;
|
||||
}
|
||||
|
||||
void ScrollBar::ScrollTo(int offset, const Event& type) {
|
||||
if (Parent == NULL) return;
|
||||
if (Parent->IsPendLayout()) {
|
||||
Parent->RefreshLayout();
|
||||
}
|
||||
//if (!Scrollable()) {
|
||||
// return;
|
||||
//}
|
||||
int viewLength;
|
||||
int contentLength;
|
||||
int scrollBarLength;
|
||||
this->GetInfo(&viewLength, &contentLength, &scrollBarLength);
|
||||
if (offset > 0) {
|
||||
//滚动条在顶部
|
||||
this->m_offset = 0;
|
||||
this->m_sliderPos = 0;
|
||||
}
|
||||
else if (std::abs(offset) >= this->m_overflowLength) {
|
||||
//滚动条在底部
|
||||
this->m_offset = -this->m_overflowLength;
|
||||
this->m_sliderPos = scrollBarLength - this->m_sliderLength;
|
||||
}
|
||||
else {
|
||||
//正常滚动
|
||||
this->m_offset = offset;
|
||||
this->m_sliderPos = -offset / this->m_rollRate;
|
||||
}
|
||||
//调用容器的滚动函数进行偏移
|
||||
if (OffsetCallback) {
|
||||
OffsetCallback(this->m_offset);
|
||||
SyncInfo();
|
||||
}
|
||||
Parent->Invalidate();
|
||||
//Parent->Refresh();//可以用Refresh,这样滚动的时候的时候显得丝滑
|
||||
if (Scroll) {
|
||||
Scroll(this, (double)this->m_offset / (-this->m_overflowLength), type);
|
||||
}
|
||||
}
|
||||
void ScrollBar::SyncInfo()
|
||||
{
|
||||
if (Parent == NULL)return;
|
||||
if (Parent->IsPendLayout()) {
|
||||
Parent->RefreshLayout();
|
||||
}
|
||||
int scrollBarLength;
|
||||
this->GetInfo(&this->m_viewLength, &this->m_contentLength, &scrollBarLength);
|
||||
this->m_overflowLength = this->m_contentLength - this->m_viewLength;//超出容器的内容长度
|
||||
if (m_overflowLength > 0) {
|
||||
this->m_sliderLength = (double)this->m_viewLength / this->m_contentLength * scrollBarLength + 0.5;//滑块长度
|
||||
double rollTotal = scrollBarLength - this->m_sliderLength;//当前滑块可用滑道的总距离
|
||||
this->m_rollRate = (double)(m_contentLength - this->m_viewLength) / rollTotal;//滑块每次滚动一次的对应上下文内容的比率
|
||||
}
|
||||
else {
|
||||
this->m_sliderLength = scrollBarLength;
|
||||
this->m_sliderPos = 0;
|
||||
this->m_offset = 0;
|
||||
this->m_rollRate = 0;
|
||||
this->m_overflowLength = 0;
|
||||
}
|
||||
}
|
||||
void ScrollBar::RefreshScroll() {
|
||||
SyncInfo();
|
||||
ScrollTo(this->m_offset, Event::None);
|
||||
};
|
||||
void ScrollBar::OnMouseWheel(const MouseEventArgs& arg) {
|
||||
__super::OnMouseWheel(arg);
|
||||
this->m_offset += arg.ZDelta * 0.5;
|
||||
ScrollTo(this->m_offset, Event::OnMouseWheel);
|
||||
}
|
||||
};
|
||||
176
sources/ShadowBox.cpp
Normal file
176
sources/ShadowBox.cpp
Normal file
@@ -0,0 +1,176 @@
|
||||
#include "ShadowBox.h"
|
||||
|
||||
namespace ezui {
|
||||
|
||||
ShadowBox::ShadowBox(int width, int height, HWND mainHwnd)
|
||||
{
|
||||
m_mainHWnd = mainHwnd;
|
||||
DWORD dwFlags = WS_EX_LAYERED | WS_EX_NOACTIVATE | WS_EX_TOOLWINDOW | WS_EX_TRANSPARENT;
|
||||
m_hWnd = CreateWindowExW(dwFlags, EZUI_WINDOW_CLASS, L"EzUI_ShadowWindow", WS_POPUP, 0, 0, width, height, NULL, NULL, ezui::__EzUI__HINSTANCE, NULL);
|
||||
ASSERT(m_hWnd);
|
||||
this->m_publicData = new WindowData;
|
||||
//绑定消息过程
|
||||
m_publicData->WndProc = [this](HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) ->LRESULT {
|
||||
return this->WndProc(uMsg, wParam, lParam);
|
||||
};
|
||||
UI_SET_USERDATA(Hwnd(), this->m_publicData);
|
||||
}
|
||||
|
||||
LRESULT ShadowBox::WndProc(UINT uMsg, WPARAM wParam, LPARAM lParam) {
|
||||
switch (uMsg) {
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return ::DefWindowProcW(Hwnd(), uMsg, wParam, lParam);
|
||||
}
|
||||
bool ShadowBox::SetShadow(int m_Width, int m_Height, int iSize, float _radius) {
|
||||
float radius = _radius;
|
||||
if (radius == 0) {//判断窗口是不是有圆角
|
||||
//如果没有圆角就让窗口阴影加点弧度显得更和谐一点
|
||||
radius = iSize / 2.0f;//半径
|
||||
}
|
||||
int width = m_Width < m_Height ? m_Width : m_Height;
|
||||
int max_size = width / 2.0f - radius;
|
||||
if (max_size <= 0) {
|
||||
radius = 0;
|
||||
max_size = width / 2.0f;
|
||||
}
|
||||
iSize = (int)iSize < max_size ? iSize : max_size;
|
||||
double piAngle = 3.1415926;
|
||||
int iSizeB = 4 * iSize;
|
||||
double fN = piAngle / iSize / 5.0f;//设置四条边外模糊度 值越小越黑
|
||||
double lN = 1.0 / iSize;
|
||||
int iAplpha = 0;
|
||||
int Left = iSize + radius,
|
||||
Top = iSize + radius,
|
||||
Right = m_Width - iSize - radius,
|
||||
Bottom = m_Height - iSize - radius;
|
||||
//处理四边的阴影
|
||||
int x = 0, y = 0;
|
||||
for (size_t i = 0; i < iSize; ++i)
|
||||
{
|
||||
iAplpha = int(255 - cos(fN * (i)) * 255);
|
||||
for (x = Left; x <= Right; ++x)
|
||||
{
|
||||
SetAplpha(x, i, iAplpha, _radius);
|
||||
SetAplpha(x, m_Height - i - 1, iAplpha, _radius);
|
||||
}
|
||||
for (y = Top; y <= Bottom; ++y)
|
||||
{
|
||||
SetAplpha(i, y, iAplpha, _radius);
|
||||
SetAplpha(m_Width - i - 1, y, iAplpha, _radius);
|
||||
}
|
||||
}
|
||||
//处理R角的阴影
|
||||
double fL = 0;
|
||||
int iSizeR = iSize + radius;
|
||||
for (int y = 0; y < iSizeR; ++y)
|
||||
{
|
||||
for (int x = y; x < iSizeR; ++x)
|
||||
{
|
||||
fL = sqrt((iSizeR - x) * (iSizeR - x) + (iSizeR - y) * (iSizeR - y));
|
||||
if (fL <= radius) {
|
||||
break;
|
||||
}
|
||||
if (fL > radius && fL < iSizeR) {
|
||||
iAplpha = int(255 - cos(fN * (iSizeR - fL)) * 255);
|
||||
}
|
||||
else {
|
||||
iAplpha = 0;
|
||||
}
|
||||
SetAplpha(x, y, iAplpha, _radius);
|
||||
SetAplpha(y, x, iAplpha, _radius);
|
||||
|
||||
SetAplpha(m_Width - x - 1, y, iAplpha, _radius);
|
||||
SetAplpha(m_Width - y - 1, x, iAplpha, _radius);
|
||||
|
||||
SetAplpha(x, m_Height - y - 1, iAplpha, _radius);
|
||||
SetAplpha(y, m_Height - x - 1, iAplpha, _radius);
|
||||
|
||||
SetAplpha(m_Width - x - 1, m_Height - y - 1, iAplpha, _radius);
|
||||
SetAplpha(m_Width - y - 1, m_Height - x - 1, iAplpha, _radius);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void ShadowBox::SetAplpha(int x, int y, BYTE a, float radius) {
|
||||
DWORD* point = (DWORD*)m_bufBitmap->GetPixel() + (x + y * m_bufBitmap->Width());//起始地址+坐标偏移
|
||||
((BYTE*)point)[3] = a;//修改A通道数值
|
||||
////使用A通道数值进行预乘
|
||||
//float opacity = (1.0f / 255.0f) * a;
|
||||
//((BYTE*)point)[2] = 50 * opacity;//修改R通道数值
|
||||
//((BYTE*)point)[1] = 50 * opacity;//修改G通道数值
|
||||
//((BYTE*)point)[0] = 50 * opacity;//修改B通道数值
|
||||
}
|
||||
void ShadowBox::Update(int shadowMargin, int radius) {
|
||||
if (!::IsWindowVisible(m_mainHWnd) || ::IsIconic(m_mainHWnd)) {
|
||||
::ShowWindow(m_hWnd, SW_HIDE);
|
||||
return;
|
||||
}
|
||||
|
||||
HWND ownerHWnd = ::GetWindow(m_mainHWnd, GW_OWNER);
|
||||
//解决拥有窗口情况下阴影显示问题
|
||||
SetWindowLongPtr(Hwnd(), GWLP_HWNDPARENT, (LONG_PTR)(ownerHWnd ? ownerHWnd : NULL));
|
||||
|
||||
::ShowWindow(m_hWnd, SW_SHOW);
|
||||
::SetWindowPos(m_hWnd, m_mainHWnd, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
|
||||
|
||||
Rect mainWinRect;//主窗口矩形
|
||||
{
|
||||
RECT winRect;
|
||||
::GetWindowRect(m_mainHWnd, &winRect);
|
||||
mainWinRect.X = winRect.left;
|
||||
mainWinRect.Y = winRect.top;
|
||||
mainWinRect.Width = winRect.right - winRect.left;
|
||||
mainWinRect.Height = winRect.bottom - winRect.top;
|
||||
}
|
||||
Size mainWinSize = mainWinRect.GetSize();//主窗口大小
|
||||
|
||||
//阴影窗口所在的位置
|
||||
Rect shadowRect(mainWinRect.X - shadowMargin, mainWinRect.Y - shadowMargin, mainWinRect.Width + shadowMargin * 2, mainWinRect.Height + shadowMargin * 2);
|
||||
::MoveWindow(m_hWnd, shadowRect.X, shadowRect.Y, shadowRect.Width, shadowRect.Height, FALSE);
|
||||
//只有在大小发生改变或者圆角改变的时候才回去重新生成新的窗口阴影贴上去
|
||||
if (mainWinSize.Equals(m_lastSize) && m_radius == radius && m_lastShadowMargin == shadowMargin) {
|
||||
return;
|
||||
}
|
||||
m_lastShadowMargin = shadowMargin;//记录最后一次窗口阴影大小
|
||||
m_radius = radius;//记录最后一次窗口圆角值
|
||||
m_lastSize = mainWinSize;//记录最后一次阴影大小
|
||||
if (m_bufBitmap) {
|
||||
delete m_bufBitmap;
|
||||
m_bufBitmap = NULL;
|
||||
}
|
||||
//创建用于渲染阴影的位图
|
||||
m_bufBitmap = new Bitmap(shadowRect.Width, shadowRect.Height);
|
||||
SetShadow(shadowRect.Width, shadowRect.Height, shadowMargin, radius);//渲染阴影
|
||||
|
||||
POINT point{ 0,0 };
|
||||
SIZE size{ shadowRect.Width, shadowRect.Height };
|
||||
BLENDFUNCTION blend{ 0 };
|
||||
blend.BlendOp = AC_SRC_OVER;
|
||||
blend.BlendFlags = 0;
|
||||
blend.AlphaFormat = AC_SRC_ALPHA;
|
||||
blend.SourceConstantAlpha = 255;
|
||||
::UpdateLayeredWindow(m_hWnd, NULL, NULL, &size, m_bufBitmap->GetDC(), &point, 0, &blend, ULW_ALPHA);//。。。。。。更新分层窗口
|
||||
}
|
||||
HWND ShadowBox::Hwnd()
|
||||
{
|
||||
return m_hWnd;
|
||||
}
|
||||
ShadowBox::~ShadowBox()
|
||||
{
|
||||
if (::IsWindow(Hwnd())) {
|
||||
HWND hwnd = m_hWnd;//复制一份
|
||||
m_hWnd = NULL;//置零
|
||||
::DestroyWindow(hwnd);//销毁
|
||||
}
|
||||
if (m_bufBitmap) {
|
||||
delete m_bufBitmap;
|
||||
}
|
||||
if (m_publicData) {
|
||||
delete m_publicData;
|
||||
}
|
||||
}
|
||||
};
|
||||
19
sources/Spacer.cpp
Normal file
19
sources/Spacer.cpp
Normal file
@@ -0,0 +1,19 @@
|
||||
#include "Spacer.h"
|
||||
#include "Control.h"
|
||||
namespace ezui {
|
||||
|
||||
Spacer::Spacer() {
|
||||
this->SetHitTestVisible(false);
|
||||
};
|
||||
Spacer::~Spacer() {};
|
||||
|
||||
VSpacer::~VSpacer() {};
|
||||
VSpacer::VSpacer(int fixedHeight) {
|
||||
SetFixedHeight(fixedHeight);
|
||||
}
|
||||
|
||||
HSpacer::~HSpacer() {};
|
||||
HSpacer::HSpacer(int fixedWidth) {
|
||||
SetFixedWidth(fixedWidth);
|
||||
}
|
||||
};
|
||||
175
sources/TabLayout.cpp
Normal file
175
sources/TabLayout.cpp
Normal file
@@ -0,0 +1,175 @@
|
||||
#include "TabLayout.h"
|
||||
|
||||
namespace ezui {
|
||||
TabLayout::TabLayout(Object* parentObject) :Control(parentObject)
|
||||
{
|
||||
Init();
|
||||
}
|
||||
void TabLayout::Init()
|
||||
{
|
||||
m_timer = new Timer(this);
|
||||
m_timer->Tick = [this](Timer* sender) {
|
||||
|
||||
HWND hWnd = this->Hwnd();
|
||||
BeginInvoke([this, hWnd, sender]() {
|
||||
if (!::IsWindow(hWnd))return;
|
||||
|
||||
m_stepAcc += m_stepPerFrame;
|
||||
int stepMove = m_stepAcc;
|
||||
m_stepAcc -= stepMove;
|
||||
m_nowOffset += stepMove;
|
||||
|
||||
// 检查是否到达终点
|
||||
if ((m_offset > 0 && m_nowOffset >= m_offset) ||
|
||||
(m_offset < 0 && m_nowOffset <= m_offset)) {
|
||||
m_nowOffset = m_offset;
|
||||
sender->Stop();
|
||||
this->Sort(); // 对齐页面
|
||||
this->Invalidate();
|
||||
return;
|
||||
}
|
||||
for (size_t i = 0; i < GetControls().size(); ++i) {
|
||||
Control* ctl = GetControls()[i];
|
||||
if (m_dlideDirection == SlideDirection::Horizontal) {
|
||||
ctl->SetRect(Rect(m_initial[i] - m_nowOffset, 0, Width(), Height()));
|
||||
}
|
||||
else {
|
||||
ctl->SetRect(Rect(0, m_initial[i] - m_nowOffset, Width(), Height()));
|
||||
}
|
||||
}
|
||||
this->Invalidate();
|
||||
});
|
||||
|
||||
};
|
||||
}
|
||||
TabLayout::~TabLayout()
|
||||
{
|
||||
}
|
||||
void TabLayout::Remove(Control* ctl, bool freeCtl)
|
||||
{
|
||||
size_t index = this->IndexOf(ctl);
|
||||
__super::Remove(ctl, freeCtl);
|
||||
int newIndex = this->GetControls().size() - 1;
|
||||
newIndex = newIndex < 0 ? 0 : newIndex;
|
||||
this->SetPageIndex(newIndex);
|
||||
TryPendLayout();
|
||||
}
|
||||
void TabLayout::Sort()
|
||||
{
|
||||
int pos = 0;
|
||||
for (auto itor = GetControls().begin(); itor != GetControls().end(); ++itor)
|
||||
{
|
||||
if (m_dlideDirection == SlideDirection::Horizontal) {
|
||||
int x = Width() * (pos - m_pageIndex);//子控件索引-当前所在页索引
|
||||
(*itor)->SetRect(Rect(x, 0, Width(), Height()));
|
||||
}
|
||||
else {
|
||||
int y = Height() * (pos - m_pageIndex);//子控件索引-当前所在页索引
|
||||
(*itor)->SetRect(Rect(0, y, Width(), Height()));
|
||||
}
|
||||
(*itor)->SetVisible(true);
|
||||
++pos;
|
||||
}
|
||||
}
|
||||
void TabLayout::SetAttribute(const UIString& key, const UIString& value) {
|
||||
__super::SetAttribute(key, value);
|
||||
if (key == "tabindex") {
|
||||
this->SetPageIndex(std::atoi(value.c_str()));
|
||||
}
|
||||
}
|
||||
void TabLayout::OnLayout()
|
||||
{
|
||||
Sort();
|
||||
this->EndLayout();
|
||||
}
|
||||
|
||||
Control* TabLayout::Add(Control* childCtl)
|
||||
{
|
||||
auto ret = __super::Add(childCtl);
|
||||
this->TryPendLayout();
|
||||
return ret;
|
||||
}
|
||||
|
||||
void TabLayout::SetPageIndex(int index)
|
||||
{
|
||||
this->m_pageIndex = index;
|
||||
this->TryPendLayout();
|
||||
}
|
||||
|
||||
|
||||
void TabLayout::SlideToPage(int index, SlideDirection dlideDirection, int durationMs, int fps)
|
||||
{
|
||||
//滑动方向
|
||||
m_dlideDirection = dlideDirection;
|
||||
|
||||
Sort();//先直接归位
|
||||
int offsetTotal = (index - m_pageIndex) * (m_dlideDirection == SlideDirection::Horizontal ? Width() : Height());
|
||||
if (offsetTotal == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 记录初始坐标
|
||||
int controlCount = GetControls().size();
|
||||
|
||||
m_initial.clear();
|
||||
m_initial.resize(controlCount);
|
||||
for (int i = 0; i < controlCount; ++i) {
|
||||
if (m_dlideDirection == SlideDirection::Horizontal) {
|
||||
m_initial[i] = GetControls()[i]->X();
|
||||
}
|
||||
else {
|
||||
m_initial[i] = GetControls()[i]->Y();
|
||||
}
|
||||
}
|
||||
|
||||
m_nowOffset = 0;
|
||||
m_offset = offsetTotal;
|
||||
m_pageIndex = index;
|
||||
|
||||
int FRAME_INTERVAL_MS = 1000 / fps;
|
||||
int totalFrames = durationMs / FRAME_INTERVAL_MS;
|
||||
if (totalFrames <= 0) {
|
||||
totalFrames = 1;
|
||||
}
|
||||
|
||||
m_stepPerFrame = offsetTotal * 1.0f / totalFrames;
|
||||
m_timer->Interval = FRAME_INTERVAL_MS;
|
||||
m_timer->Start();
|
||||
}
|
||||
|
||||
|
||||
|
||||
void TabLayout::SetPage(Control* ctl)
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
ASSERT(ctl->Parent == this);
|
||||
#endif // _DEBUG
|
||||
this->m_pageIndex = this->IndexOf(ctl);
|
||||
this->TryPendLayout();
|
||||
}
|
||||
|
||||
Control* TabLayout::GetPage() {
|
||||
int pos = 0;
|
||||
for (auto& it : GetControls()) {
|
||||
if (pos == this->m_pageIndex) {
|
||||
return it;
|
||||
}
|
||||
++pos;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
int TabLayout::GetPageIndex()
|
||||
{
|
||||
return m_pageIndex;
|
||||
}
|
||||
|
||||
void TabLayout::OnChildPaint(PaintEventArgs& args)
|
||||
{
|
||||
ViewControls.clear();
|
||||
Control* currPage = this->GetPage();
|
||||
if (currPage) {//仅绘制当前页
|
||||
ViewControls.push_back(currPage);
|
||||
currPage->SendEvent(args);
|
||||
}
|
||||
}
|
||||
};
|
||||
132
sources/Task.cpp
Normal file
132
sources/Task.cpp
Normal file
@@ -0,0 +1,132 @@
|
||||
#include "Task.h"
|
||||
namespace ezui {
|
||||
|
||||
Mutex::Mutex() {
|
||||
InitializeCriticalSection(&m_mtx); // 初始化互斥锁
|
||||
m_bLocked = false;
|
||||
}
|
||||
Mutex::~Mutex() {
|
||||
DeleteCriticalSection(&m_mtx); // 销毁互斥锁
|
||||
}
|
||||
void Mutex::Lock() {
|
||||
EnterCriticalSection(&m_mtx);
|
||||
m_bLocked = true;
|
||||
}
|
||||
void Mutex::UnLock() {
|
||||
LeaveCriticalSection(&m_mtx);
|
||||
m_bLocked = false;
|
||||
}
|
||||
|
||||
ConditionVariable::ConditionVariable() {
|
||||
m_codv = CreateEvent(NULL, FALSE, FALSE, NULL); // 创建事件对象
|
||||
}
|
||||
ConditionVariable::~ConditionVariable() {
|
||||
if (m_codv != NULL) {
|
||||
CloseHandle(m_codv); // 关闭句柄,释放资源
|
||||
}
|
||||
}
|
||||
void ConditionVariable::Notify() {
|
||||
SetEvent(m_codv); // 设置事件,唤醒等待的线程
|
||||
}
|
||||
void ConditionVariable::Wait(const std::function<bool()>& condition_cb) {
|
||||
m_mtx.Lock();// 先加锁
|
||||
// 如果没有条件回调的情况下
|
||||
if (!condition_cb) {
|
||||
m_mtx.UnLock(); // 必须先解锁再等待!
|
||||
WaitForSingleObject(m_codv, INFINITE);
|
||||
return; //事件已触发 无需再检查 等待信号之前已经解锁 所以无需解锁 直接return
|
||||
}
|
||||
// 如果有条件回调 循环检查
|
||||
while (!condition_cb()) {
|
||||
m_mtx.UnLock(); // 解锁后再等待,避免死锁
|
||||
WaitForSingleObject(m_codv, INFINITE);
|
||||
m_mtx.Lock(); // 重新加锁检查条件
|
||||
}
|
||||
m_mtx.UnLock(); // 最终解锁
|
||||
}
|
||||
void ConditionVariable::Lock() {
|
||||
m_mtx.Lock();
|
||||
}
|
||||
void ConditionVariable::Unlock() {
|
||||
m_mtx.UnLock();
|
||||
}
|
||||
}
|
||||
|
||||
namespace ezui {
|
||||
void Task::Wait() {
|
||||
if (!m_bJoin)
|
||||
{
|
||||
m_bJoin = true;
|
||||
m_thread->join(); // 等待线程完成
|
||||
}
|
||||
}
|
||||
bool Task::IsStopped() {
|
||||
return m_finished;
|
||||
}
|
||||
void Task::DoWork(std::function<void()>* func) {
|
||||
(*func)(); // 执行任务
|
||||
delete func;
|
||||
m_finished = true;
|
||||
}
|
||||
|
||||
Task::~Task() {
|
||||
Wait(); // 等待任务完成
|
||||
delete m_thread; // 删除线程对象
|
||||
}
|
||||
|
||||
TaskFactory::TaskFactory(int maxTaskCount) {
|
||||
for (size_t i = 0; i < maxTaskCount; ++i)
|
||||
{
|
||||
m_tasks.push_back(new Task([this]() {
|
||||
while (true)
|
||||
{
|
||||
std::function<void()> task;
|
||||
{
|
||||
std::unique_lock<std::mutex> autoLock(this->m_mtx);
|
||||
this->m_codv.wait(autoLock, [this]()->bool {
|
||||
return this->m_bStop || !this->m_funcs.empty();
|
||||
});
|
||||
if (m_funcs.empty()) {
|
||||
this->m_codv2.notify_all();
|
||||
}
|
||||
if (this->m_bStop && m_funcs.empty()) {
|
||||
break;
|
||||
}
|
||||
task = std::move(*m_funcs.begin());
|
||||
m_funcs.pop_front();
|
||||
}
|
||||
task();
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
void TaskFactory::WaitAll() {
|
||||
m_codv.notify_all();
|
||||
std::unique_lock<std::mutex> lock2(this->m_mtx2);
|
||||
this->m_codv2.wait(lock2, [this]()->bool {
|
||||
return m_funcs.empty();
|
||||
});
|
||||
}
|
||||
TaskFactory::~TaskFactory() {
|
||||
{
|
||||
std::unique_lock<std::mutex> autoLock(m_mtx);
|
||||
m_bStop = true;
|
||||
}
|
||||
WaitAll();
|
||||
while (!m_tasks.empty())
|
||||
{
|
||||
for (auto itor = m_tasks.begin(); itor != m_tasks.end(); )
|
||||
{
|
||||
if ((*itor)->IsStopped()) {
|
||||
(*itor)->Wait();
|
||||
delete (*itor);
|
||||
itor = m_tasks.erase(itor);
|
||||
}
|
||||
else {
|
||||
++itor;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
729
sources/TextBox.cpp
Normal file
729
sources/TextBox.cpp
Normal file
@@ -0,0 +1,729 @@
|
||||
#include "TextBox.h"
|
||||
#undef min
|
||||
#undef max
|
||||
namespace ezui {
|
||||
TextBox::TextBox(Object* parentObject) :Control(parentObject)
|
||||
{
|
||||
Init();
|
||||
}
|
||||
TextBox::~TextBox() {
|
||||
m_timer->Stop();
|
||||
if (m_textLayout) { delete m_textLayout; }
|
||||
if (m_font) { delete m_font; }
|
||||
}
|
||||
void TextBox::Init()
|
||||
{
|
||||
this->GetScrollBar()->SetWidth(5);
|
||||
this->GetScrollBar()->Parent = this;
|
||||
this->GetScrollBar()->OffsetCallback = [=](int offset) {
|
||||
this->Offset(offset);
|
||||
};
|
||||
|
||||
Style.Cursor = LoadCursor(Cursor::IBEAM);
|
||||
|
||||
m_timer = new Timer(this);
|
||||
m_timer->Interval = 500;
|
||||
m_timer->Tick = [this](Timer* t) {
|
||||
|
||||
HWND hWnd = this->Hwnd();//在对象析构前获取句柄
|
||||
BeginInvoke([this, hWnd]() {//捕获hWnd
|
||||
if (!::IsWindow(hWnd))return;//如果窗口已经销毁 则不往下执行
|
||||
|
||||
if (!this->IsEnabled() || this->IsReadOnly()) {
|
||||
m_bCareShow = false;
|
||||
return;
|
||||
}
|
||||
if (!m_careRect.IsEmptyArea() && m_focus) {
|
||||
m_bCareShow = !m_bCareShow;
|
||||
this->Invalidate();
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
void TextBox::OnRemove() {
|
||||
__super::OnRemove();
|
||||
m_timer->Stop();
|
||||
}
|
||||
void TextBox::SetAutoWidth(bool flag)
|
||||
{
|
||||
//需要屏蔽
|
||||
}
|
||||
void TextBox::SetAutoHeight(bool flag)
|
||||
{
|
||||
//需要屏蔽
|
||||
}
|
||||
void TextBox::OnKeyChar(const KeyboardEventArgs& arg)
|
||||
{
|
||||
__super::OnKeyChar(arg);
|
||||
WPARAM wParam = arg.wParam;
|
||||
LPARAM lParam = arg.lParam;
|
||||
|
||||
if (wParam < 32)return;//控制字符
|
||||
if (IsReadOnly()) return;//只读
|
||||
WCHAR buf[2]{ (WCHAR)wParam ,0 };//
|
||||
InsertUnicode(std::wstring(buf));//插入新的字符
|
||||
Analysis();//分析字符串
|
||||
Invalidate();//刷新
|
||||
}
|
||||
|
||||
#undef max
|
||||
#undef min
|
||||
void TextBox::BuildSelectedRect() {
|
||||
m_selectRects.clear();
|
||||
if (m_textLayout) {
|
||||
//获取起点和终点
|
||||
Point point1, point2;
|
||||
if ((m_A_TextPos + m_A_isTrailingHit) < (m_B_TextPos + m_B_isTrailingHit)) {
|
||||
point1 = m_textLayout->HitTestTextPosition(m_A_TextPos, m_A_isTrailingHit);
|
||||
point2 = m_textLayout->HitTestTextPosition(m_B_TextPos, m_B_isTrailingHit);
|
||||
}
|
||||
else {
|
||||
point2 = m_textLayout->HitTestTextPosition(m_A_TextPos, m_A_isTrailingHit);
|
||||
point1 = m_textLayout->HitTestTextPosition(m_B_TextPos, m_B_isTrailingHit);
|
||||
}
|
||||
// 中心偏移用于 hit test 保证在行内
|
||||
float offsetY = m_textLayout->GetFontHeight() / 2.0f;
|
||||
point1.Y += offsetY;
|
||||
point2.Y += offsetY;
|
||||
|
||||
// 获取每行矩形
|
||||
auto lineRects = m_textLayout->GetLineRects();
|
||||
|
||||
for (auto& lr : lineRects) {
|
||||
// 首行
|
||||
if (point1.Y >= lr.Y && point1.Y <= lr.Y + lr.Height) {
|
||||
if (point2.Y <= lr.Y + lr.Height) {
|
||||
// 同一行
|
||||
float width = std::max(0.0f, float(point2.X - point1.X));
|
||||
m_selectRects.push_back(Rect(point1.X, lr.Y, width, lr.Height));
|
||||
}
|
||||
else {
|
||||
// 跨行首行 从 point1 到行末
|
||||
float width = std::max(0.0f, lr.X + lr.Width - point1.X);
|
||||
m_selectRects.push_back(Rect(point1.X, lr.Y, width, lr.Height));
|
||||
}
|
||||
}
|
||||
// 末行
|
||||
else if (point2.Y >= lr.Y && point2.Y <= lr.Y + lr.Height) {
|
||||
float width = std::max(0.0f, point2.X - lr.X);
|
||||
m_selectRects.push_back(Rect(lr.X, lr.Y, width, lr.Height));
|
||||
}
|
||||
// 中间整行
|
||||
else if (point1.Y < lr.Y && point2.Y > lr.Y + lr.Height) {
|
||||
m_selectRects.push_back(lr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool TextBox::SelectedAll() {
|
||||
if (m_textLayout && !m_text.empty()) {
|
||||
m_pointA = Point{ 0,0 };
|
||||
m_A_isTrailingHit = FALSE;
|
||||
m_A_TextPos = 0;
|
||||
|
||||
m_pointB = Point{ m_fontBox.Width ,0 };
|
||||
m_B_isTrailingHit = TRUE;
|
||||
m_B_TextPos = m_text.size() - 1;
|
||||
|
||||
BuildSelectedRect();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool TextBox::GetSelectedRange(int* outPos, int* outCount) {
|
||||
if (m_selectRects.size() > 0) {
|
||||
int pos, count;
|
||||
if ((m_A_TextPos + m_A_isTrailingHit) < (m_B_TextPos + m_B_isTrailingHit)) {
|
||||
int pos1 = m_A_TextPos;
|
||||
if (m_A_isTrailingHit == 1) {
|
||||
pos1 += 1;
|
||||
}
|
||||
int pos2 = m_B_TextPos;
|
||||
if (m_B_isTrailingHit == 0) {
|
||||
pos2 -= 1;
|
||||
}
|
||||
pos = pos1;
|
||||
count = std::abs(pos2 - pos1) + 1;
|
||||
}
|
||||
else {
|
||||
int pos1 = m_A_TextPos;
|
||||
if (m_A_isTrailingHit == 0) {
|
||||
pos1 -= 1;
|
||||
}
|
||||
int pos2 = m_B_TextPos;
|
||||
if (m_B_isTrailingHit == 1) {
|
||||
pos2 += 1;
|
||||
}
|
||||
pos = pos2;
|
||||
count = std::abs(pos2 - pos1) + 1;
|
||||
}
|
||||
*outPos = pos;
|
||||
*outCount = count;
|
||||
if (*outCount > 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
void TextBox::InsertUnicode(const std::wstring& str) {
|
||||
DeleteRange();//先删除是否有选中的区域
|
||||
if (m_textPos < 0)m_textPos = 0;
|
||||
if (m_textPos > (int)m_text.size()) {
|
||||
m_textPos = m_text.size();
|
||||
}
|
||||
m_text.insert(m_textPos, str);
|
||||
m_textPos += str.size();
|
||||
if (TextChanged) {
|
||||
TextChanged(UIString(m_text));
|
||||
}
|
||||
}
|
||||
bool TextBox::DeleteRange() {
|
||||
int pos, count;
|
||||
if (GetSelectedRange(&pos, &count)) {//删除选中的
|
||||
//isTrailingHit = FALSE;
|
||||
m_textPos = pos;
|
||||
m_text.erase(pos, count);
|
||||
if (TextChanged) {
|
||||
TextChanged(UIString(m_text));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool TextBox::Copy() {
|
||||
int pos, count;
|
||||
if (!GetSelectedRange(&pos, &count))return false;
|
||||
std::wstring wBuf(m_text.substr(pos, count));
|
||||
return ezui::CopyToClipboard(wBuf, Hwnd());
|
||||
}
|
||||
bool TextBox::Paste() {
|
||||
std::wstring wBuf;
|
||||
bool bRet = ezui::GetClipboardData(&wBuf, Hwnd());
|
||||
UIString u8Str(wBuf);
|
||||
if (!m_multiLine) {
|
||||
//行编辑框不允许有换行符
|
||||
ui_text::Replace(&u8Str, "\r", "");
|
||||
ui_text::Replace(&u8Str, "\n", "");
|
||||
}
|
||||
InsertUnicode(u8Str.unicode());//插入新的字符
|
||||
return bRet;
|
||||
}
|
||||
void TextBox::OnBackspace() {
|
||||
if (m_text.size() <= 0)return;
|
||||
|
||||
if (!DeleteRange()) {//先看看有没有有选中的需要删除
|
||||
//否则删除单个字符
|
||||
m_textPos--;
|
||||
if (m_textPos > -1) {
|
||||
m_text.erase(m_textPos, 1);
|
||||
if (TextChanged) {
|
||||
TextChanged(UIString(m_text));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
void TextBox::OnKeyDown(const KeyboardEventArgs& arg)
|
||||
{
|
||||
__super::OnKeyDown(arg);
|
||||
WPARAM wParam = arg.wParam;
|
||||
LPARAM lParam = arg.lParam;
|
||||
|
||||
//判断是否按下shift+enter
|
||||
if (IsMultiLine() && (GetKeyState(VK_SHIFT) & 0x8000) && wParam == VK_RETURN) {
|
||||
InsertUnicode(L"\n");//插入换行符
|
||||
Analysis();//分析字符串
|
||||
Invalidate();//刷新
|
||||
return;
|
||||
}
|
||||
//判断是否按下Ctrl键
|
||||
if (GetKeyState(VK_CONTROL) & 0x8000) {
|
||||
do
|
||||
{
|
||||
if (wParam == 'A') {//ctrl+a全选
|
||||
SelectedAll();//全选
|
||||
Invalidate();//刷新
|
||||
break;
|
||||
}
|
||||
if (wParam == 'C') {//ctrl+c 复制
|
||||
Copy();
|
||||
break;
|
||||
}
|
||||
if (wParam == 'X') {//ctrl+x 剪切
|
||||
if (IsReadOnly()) {
|
||||
break;
|
||||
}
|
||||
if (Copy()) {
|
||||
DeleteRange();//因为是剪切 所以要删除选中的这段
|
||||
Analysis();
|
||||
Invalidate();//刷新
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (wParam == 'V') {//ctrl+v 粘贴
|
||||
if (IsReadOnly()) {
|
||||
break;
|
||||
}
|
||||
Paste();
|
||||
Analysis();//分析字符串
|
||||
Invalidate();//刷新
|
||||
break;
|
||||
}
|
||||
if (wParam == 'Z') {//ctrl+z撤销
|
||||
if (IsReadOnly()) {
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
} while (false);
|
||||
return;
|
||||
}
|
||||
if (wParam == VK_BACK) { //退格键(删除字符)
|
||||
if (IsReadOnly()) {
|
||||
return;
|
||||
}
|
||||
OnBackspace();//退格键的操作在里面
|
||||
Analysis();//重新分析
|
||||
Invalidate();//刷新
|
||||
return;
|
||||
}
|
||||
if (wParam == VK_LEFT) {
|
||||
m_textPos--;
|
||||
m_bCareShow = true;
|
||||
m_selectRects.clear();
|
||||
BuildCare();
|
||||
Invalidate();
|
||||
return;
|
||||
}
|
||||
if (wParam == VK_RIGHT) {
|
||||
++m_textPos;
|
||||
m_bCareShow = true;
|
||||
m_selectRects.clear();
|
||||
BuildCare();
|
||||
Invalidate();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void TextBox::Analysis()
|
||||
{
|
||||
if (m_text.size() > (size_t)(this->m_maxLen)) {
|
||||
m_text.erase((size_t)(this->m_maxLen));
|
||||
}
|
||||
m_scrollX = 0;
|
||||
m_scrollY = 0;
|
||||
m_pointA = Point();
|
||||
m_A_isTrailingHit = 0;
|
||||
m_A_TextPos = 0;
|
||||
m_pointB = Point();
|
||||
m_B_isTrailingHit = 0;
|
||||
m_B_TextPos = 0;
|
||||
m_careRect = Rect();
|
||||
m_selectRects.clear();
|
||||
if (m_font == NULL) return;
|
||||
if (m_textLayout) delete m_textLayout;
|
||||
|
||||
std::wstring* drawText = &this->m_text;
|
||||
if (!m_passwordChar.empty()) {
|
||||
drawText = new std::wstring;
|
||||
int count = m_passwordChar.size() * m_text.size();
|
||||
for (size_t i = 0; i < m_text.size(); ++i)
|
||||
{
|
||||
*drawText += m_passwordChar;
|
||||
}
|
||||
}
|
||||
else {
|
||||
*drawText = m_text;
|
||||
}
|
||||
|
||||
if (!m_multiLine) {//单行编辑框
|
||||
m_font->Get()->SetWordWrapping(DWRITE_WORD_WRAPPING_NO_WRAP);
|
||||
bool bAlignLeft = (int(this->TextAlign) & int(HAlign::Left));
|
||||
float width = bAlignLeft ? EZUI_FLOAT_MAX : Width();
|
||||
m_textLayout = new TextLayout(*drawText, *m_font, SizeF{ width,(float)Height() }, this->TextAlign);
|
||||
m_fontBox = m_textLayout->GetFontBox();
|
||||
if (m_fontBox.Width < this->Width()) {
|
||||
m_scrollX = 0;
|
||||
}
|
||||
if (!bAlignLeft && m_fontBox.Width > this->Width()) {
|
||||
ezui::TextAlign tmp = this->TextAlign;
|
||||
tmp = ezui::TextAlign((int)tmp & ~(int)HAlign::Center);
|
||||
tmp = ezui::TextAlign((int)tmp & ~(int)HAlign::Right);
|
||||
tmp = ezui::TextAlign((int)tmp | (int)HAlign::Left);
|
||||
m_textLayout->SetTextAlign(tmp);
|
||||
}
|
||||
if (m_fontBox.Width > this->Width() && m_scrollX + m_fontBox.Width < this->Width()) {
|
||||
m_scrollX = this->Width() - m_fontBox.Width;
|
||||
}
|
||||
}
|
||||
else {//多行编辑框
|
||||
m_font->Get()->SetWordWrapping(DWRITE_WORD_WRAPPING_WRAP);
|
||||
m_textLayout = new TextLayout(*drawText, *m_font, SizeF{ (float)Width(),EZUI_FLOAT_MAX }, TextAlign::TopLeft);
|
||||
m_fontBox = m_textLayout->GetFontBox();
|
||||
}
|
||||
if (drawText != &this->m_text) {
|
||||
delete drawText;
|
||||
}
|
||||
this->SetContentSize({ m_fontBox.Width , m_fontBox.Height });
|
||||
if (m_multiLine) {
|
||||
this->GetScrollBar()->RefreshScroll();
|
||||
}
|
||||
BuildCare();
|
||||
}
|
||||
|
||||
void TextBox::BuildCare() {
|
||||
if (!m_textLayout) return;
|
||||
|
||||
if (m_textPos < 0) {
|
||||
m_textPos = 0;
|
||||
}
|
||||
if (m_textPos > (int)m_text.size()) {
|
||||
m_textPos = m_text.size();
|
||||
}
|
||||
|
||||
Point pt = m_textLayout->HitTestTextPosition(m_textPos, FALSE);
|
||||
m_careRect.X = pt.X;
|
||||
m_careRect.Y = pt.Y;
|
||||
m_careRect.Height = m_textLayout->GetFontHeight();
|
||||
m_careRect.Width = 1 * this->GetScale();
|
||||
|
||||
if (!m_multiLine) {
|
||||
//使光标一直在输入框内
|
||||
int drawX = m_careRect.X + m_scrollX;
|
||||
if (drawX < 0) {//光标在最左侧
|
||||
m_scrollX -= drawX;
|
||||
}
|
||||
if (drawX > Width()) {//光标在最右侧
|
||||
int ofssetX = (Width() - drawX);
|
||||
m_scrollX += ofssetX;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
void TextBox::OnMouseDown(const MouseEventArgs& arg) {
|
||||
__super::OnMouseDown(arg);
|
||||
m_lastX = 0;
|
||||
m_lastY = 0;
|
||||
auto mbtn = arg.Button;
|
||||
auto point = arg.Location;
|
||||
|
||||
if (mbtn == MouseButton::Left) {
|
||||
m_down = true;
|
||||
m_point_Start = ConvertPoint(point);
|
||||
if (m_textLayout) {
|
||||
int fontHeight;
|
||||
m_selectRects.clear();
|
||||
m_pointA = m_textLayout->HitTestPoint(m_point_Start, &m_A_TextPos, &m_A_isTrailingHit, &fontHeight);
|
||||
m_careRect.X = m_pointA.X;
|
||||
m_careRect.Y = m_pointA.Y;
|
||||
m_careRect.Width = 1;
|
||||
m_careRect.Height = fontHeight;
|
||||
|
||||
m_textPos = m_A_TextPos;
|
||||
if (m_A_isTrailingHit) {
|
||||
++m_textPos;
|
||||
}
|
||||
}
|
||||
Invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
void TextBox::OnMouseWheel(const MouseEventArgs& arg)
|
||||
{
|
||||
__super::OnMouseWheel(arg);
|
||||
if (!m_multiLine) {//单行
|
||||
int textWidth = m_fontBox.Width;
|
||||
if (arg.ZDelta > 0 && textWidth > Width()) {
|
||||
m_scrollX += std::abs(arg.ZDelta) * 0.5;
|
||||
if (m_scrollX > 0) {
|
||||
m_scrollX = 0;
|
||||
}
|
||||
Invalidate();
|
||||
}
|
||||
else if (arg.ZDelta<0 && textWidth>Width()) {
|
||||
m_scrollX -= std::abs(arg.ZDelta) * 0.5;
|
||||
if (-m_scrollX + Width() > textWidth) {
|
||||
m_scrollX = -(textWidth - Width());
|
||||
}
|
||||
Invalidate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ScrollBar* TextBox::GetScrollBar()
|
||||
{
|
||||
return &m_vScrollbar;
|
||||
}
|
||||
void TextBox::Offset(int _sliderY) {
|
||||
this->m_scrollY = _sliderY;
|
||||
Invalidate();
|
||||
}
|
||||
|
||||
void TextBox::OnLayout()
|
||||
{
|
||||
__super::OnLayout();
|
||||
m_scrollX = 0;
|
||||
m_scrollY = 0;
|
||||
m_selectRects.clear();
|
||||
if (!m_multiLine && Height() != m_lastHeight) {
|
||||
m_lastHeight = Height();
|
||||
Analysis();
|
||||
}
|
||||
if (m_multiLine && Width() != m_lastWidth) {
|
||||
m_lastWidth = Width();
|
||||
Analysis();
|
||||
}
|
||||
this->SetContentSize({ m_fontBox.Width ,m_multiLine ? m_fontBox.Height : Height() });
|
||||
this->GetScrollBar()->RefreshScroll();
|
||||
this->EndLayout();
|
||||
}
|
||||
|
||||
Point TextBox::ConvertPoint(const Point& pt) {
|
||||
int _x = -m_scrollX;
|
||||
int _y = -m_scrollY;
|
||||
return Point{ pt.X + _x,pt.Y + _y };
|
||||
}
|
||||
|
||||
void TextBox::OnMouseMove(const MouseEventArgs& arg)
|
||||
{
|
||||
__super::OnMouseMove(arg);
|
||||
auto point = arg.Location;
|
||||
if (m_down) {
|
||||
m_point_End = ConvertPoint(point);
|
||||
if (m_textLayout) {
|
||||
int fontHeight;
|
||||
m_selectRects.clear();//
|
||||
m_pointB = m_textLayout->HitTestPoint(m_point_End, &m_B_TextPos, &m_B_isTrailingHit, &fontHeight);
|
||||
|
||||
BuildSelectedRect();
|
||||
|
||||
if (!m_multiLine) {//单行
|
||||
//当鼠标往左侧移动
|
||||
int textWidth = m_fontBox.Width;
|
||||
if (m_lastX > point.X) {
|
||||
m_lastX = point.X;
|
||||
if (textWidth > Width() && m_scrollX < 0 && point.X < 0) {
|
||||
m_scrollX += 3;
|
||||
Invalidate();
|
||||
return;
|
||||
}
|
||||
}
|
||||
//当鼠标往右侧移动
|
||||
if (m_lastX < point.X) {
|
||||
m_lastX = point.X;
|
||||
if (textWidth > Width() && point.X > Width()) {
|
||||
m_scrollX -= 3;
|
||||
if (-m_scrollX + Width() > textWidth) {
|
||||
m_scrollX = -(textWidth - Width());
|
||||
}
|
||||
Invalidate();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
Invalidate();
|
||||
}
|
||||
}
|
||||
}
|
||||
void TextBox::OnMouseUp(const MouseEventArgs& arg)
|
||||
{
|
||||
__super::OnMouseUp(arg);
|
||||
m_down = false;
|
||||
m_lastX = 0;
|
||||
m_lastY = 0;
|
||||
Invalidate();
|
||||
}
|
||||
void TextBox::OnFocus(const FocusEventArgs& arg)
|
||||
{
|
||||
__super::OnFocus(arg);
|
||||
m_focus = true;
|
||||
m_bCareShow = true;
|
||||
m_timer->Start();
|
||||
Invalidate();
|
||||
}
|
||||
void TextBox::OnKillFocus(const KillFocusEventArgs& arg)
|
||||
{
|
||||
__super::OnKillFocus(arg);
|
||||
m_down = false;
|
||||
m_focus = false;
|
||||
m_bCareShow = false;
|
||||
m_timer->Stop();
|
||||
this->Invalidate();
|
||||
}
|
||||
const UIString TextBox::GetText()
|
||||
{
|
||||
return UIString(this->m_text);
|
||||
}
|
||||
void TextBox::SetText(const UIString& text)
|
||||
{
|
||||
this->m_text = text.unicode();
|
||||
Analysis();
|
||||
}
|
||||
bool TextBox::IsMultiLine()
|
||||
{
|
||||
return m_multiLine;
|
||||
}
|
||||
void TextBox::SetMultiLine(bool multiLine)
|
||||
{
|
||||
if (this->m_multiLine != multiLine) {
|
||||
this->m_multiLine = multiLine;
|
||||
Analysis();
|
||||
}
|
||||
}
|
||||
void TextBox::SetReadOnly(bool bReadOnly)
|
||||
{
|
||||
this->m_readOnly = bReadOnly;
|
||||
}
|
||||
bool TextBox::IsReadOnly()
|
||||
{
|
||||
return this->m_readOnly;
|
||||
}
|
||||
void TextBox::SetMaxLength(int maxLen)
|
||||
{
|
||||
this->m_maxLen = maxLen;
|
||||
}
|
||||
void TextBox::SetPlaceholderText(const UIString& text)
|
||||
{
|
||||
this->m_placeholder = text.unicode();
|
||||
}
|
||||
void TextBox::SetPasswordChar(const UIString& passwordChar)
|
||||
{
|
||||
this->m_passwordChar = passwordChar.unicode();
|
||||
}
|
||||
Rect TextBox::GetCareRect()
|
||||
{
|
||||
Rect rect(m_careRect);
|
||||
rect.X += m_scrollX;//偏移
|
||||
rect.Y += m_scrollY;
|
||||
return rect;
|
||||
}
|
||||
void TextBox::Insert(const UIString& str)
|
||||
{
|
||||
InsertUnicode(str.unicode());
|
||||
Analysis();//分析字符串
|
||||
}
|
||||
void TextBox::SetAttribute(const UIString& key, const UIString& value) {
|
||||
__super::SetAttribute(key, value);
|
||||
do
|
||||
{
|
||||
if (key == "valign") {
|
||||
this->TextAlign = ezui::TextAlign((int)this->TextAlign & ~(int)VAlign::Top);
|
||||
this->TextAlign = ezui::TextAlign((int)this->TextAlign & ~(int)VAlign::Mid);
|
||||
this->TextAlign = ezui::TextAlign((int)this->TextAlign & ~(int)VAlign::Bottom);
|
||||
VAlign v = VAlign::Mid;
|
||||
if (value == "top") {
|
||||
v = VAlign::Top;
|
||||
}
|
||||
else if (value == "bottom") {
|
||||
v = VAlign::Bottom;
|
||||
}
|
||||
this->TextAlign = ezui::TextAlign((int)this->TextAlign | (int)v);
|
||||
break;
|
||||
}
|
||||
if (key == "halign") {
|
||||
this->TextAlign = ezui::TextAlign((int)this->TextAlign & ~(int)HAlign::Left);
|
||||
this->TextAlign = ezui::TextAlign((int)this->TextAlign & ~(int)HAlign::Center);
|
||||
this->TextAlign = ezui::TextAlign((int)this->TextAlign & ~(int)HAlign::Right);
|
||||
HAlign h = HAlign::Center;
|
||||
if (value == "left") {
|
||||
h = HAlign::Left;
|
||||
}
|
||||
else if (value == "right") {
|
||||
h = HAlign::Right;
|
||||
}
|
||||
this->TextAlign = ezui::TextAlign((int)this->TextAlign | (int)h);
|
||||
break;
|
||||
}
|
||||
if (key == "passwordchar") {
|
||||
this->SetPasswordChar(value);
|
||||
break;
|
||||
}
|
||||
if (key == "placeholder") {
|
||||
this->SetPlaceholderText(value);
|
||||
break;
|
||||
}
|
||||
if (key == "text" || key == "value") {
|
||||
this->SetText(value);
|
||||
break;
|
||||
}
|
||||
if (key == "readonly") {
|
||||
if (value == "true") {
|
||||
this->SetReadOnly(true);
|
||||
break;
|
||||
}
|
||||
if (value == "false") {
|
||||
this->SetReadOnly(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (key == "multiline") {
|
||||
if (value == "true") {
|
||||
this->m_multiLine = true;
|
||||
break;
|
||||
}
|
||||
if (value == "false") {
|
||||
this->m_multiLine = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} while (false);
|
||||
}
|
||||
|
||||
void TextBox::OnForePaint(PaintEventArgs& e) {
|
||||
std::wstring fontFamily = GetFontFamily();
|
||||
auto fontSize = GetFontSize();
|
||||
if (fontSize == 0)return;
|
||||
if (m_font == NULL || ((m_font != NULL) && (m_font->GetFontFamily() != fontFamily || m_font->GetFontSize() != fontSize))) {
|
||||
if (m_font != NULL) {
|
||||
delete m_font;
|
||||
}
|
||||
m_font = new Font(fontFamily, fontSize);
|
||||
Analysis();
|
||||
}
|
||||
Color fontColor = GetForeColor();
|
||||
e.Graphics.SetFont(fontFamily, fontSize);
|
||||
if (m_text.empty()) {
|
||||
bool bAlignCenter = (int(this->TextAlign) & int(HAlign::Center));
|
||||
if ((!bAlignCenter) || (bAlignCenter && !m_focus)) {//避免光标和placeholder重叠
|
||||
Color placeholderColor = fontColor;
|
||||
placeholderColor.SetA(fontColor.GetA() * 0.6);
|
||||
e.Graphics.SetColor(placeholderColor);
|
||||
e.Graphics.DrawString(m_placeholder, RectF(0, 0, (float)Width(), (float)Height()), m_multiLine ? TextAlign::TopLeft : this->TextAlign);
|
||||
}
|
||||
}
|
||||
|
||||
if (m_selectRects.size() > 0) {
|
||||
Color selectedColor = fontColor;
|
||||
selectedColor.SetA(fontColor.GetA() * 0.35);
|
||||
e.Graphics.SetColor(selectedColor);
|
||||
for (auto& it : m_selectRects) {
|
||||
if (!it.IsEmptyArea()) {
|
||||
RectF rect(it);
|
||||
rect.X += m_scrollX;//偏移
|
||||
rect.Y += m_scrollY;
|
||||
e.Graphics.FillRectangle(rect);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (m_textLayout) {
|
||||
e.Graphics.SetColor(fontColor);
|
||||
e.Graphics.DrawTextLayout(*m_textLayout, PointF{ (float)m_scrollX, (float)m_scrollY });
|
||||
}
|
||||
|
||||
if (!m_careRect.IsEmptyArea() && m_focus) {
|
||||
if (m_bCareShow) {
|
||||
RectF rect(m_careRect.X, m_careRect.Y, m_careRect.Width, m_careRect.Height);
|
||||
rect.X += m_scrollX;//偏移
|
||||
rect.Y += m_scrollY;
|
||||
if (ezui::IsFloatEqual(rect.X, this->Width())) {
|
||||
//如果光标刚好在边框末尾
|
||||
rect.X = this->Width() - rect.Width;
|
||||
}
|
||||
rect.Width = rect.Width * this->GetScale();
|
||||
e.Graphics.SetColor(fontColor);
|
||||
e.Graphics.FillRectangle(rect);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
100
sources/TileListView.cpp
Normal file
100
sources/TileListView.cpp
Normal file
@@ -0,0 +1,100 @@
|
||||
#include "TileListView.h"
|
||||
|
||||
namespace ezui {
|
||||
|
||||
void TileListView::Init()
|
||||
{
|
||||
this->GetScrollBar()->SetHeight(Height());
|
||||
this->GetScrollBar()->Parent = this;
|
||||
this->GetScrollBar()->OffsetCallback = [this](int offsetValue)->void {
|
||||
if (this->GetScrollBar()->ScrollPos() >= 1) {
|
||||
NextPage();
|
||||
}
|
||||
this->Offset(offsetValue);
|
||||
};
|
||||
}
|
||||
|
||||
TileListView::TileListView(Object* parentObject) :PagedListView(parentObject)
|
||||
{
|
||||
Init();
|
||||
}
|
||||
TileListView::~TileListView()
|
||||
{
|
||||
}
|
||||
ScrollBar* TileListView::GetScrollBar()
|
||||
{
|
||||
return &m_vScrollBar;
|
||||
}
|
||||
void TileListView::OnChildPaint(PaintEventArgs& args)
|
||||
{
|
||||
ViewControls.clear();
|
||||
//绘制子控件
|
||||
auto rect = Rect(0, 0, Width(), Height());
|
||||
for (auto& it : GetControls()) {
|
||||
if (rect.IntersectsWith(it->GetRect())) {
|
||||
ViewControls.push_back(it);
|
||||
}
|
||||
if (it->Y() >= Height()) {
|
||||
break;
|
||||
////当控件超出容器底部将不再派发绘制事件 但是仍然要进行布局
|
||||
//if ((it->IsAutoWidth() || it->IsAutoHeight()) && it->GetLayoutState() == LayoutState::Pend) {
|
||||
// it->RefreshLayout();
|
||||
//}
|
||||
}
|
||||
else {
|
||||
it->SendEvent(args);
|
||||
}
|
||||
}
|
||||
}
|
||||
void TileListView::OnLayout()
|
||||
{
|
||||
this->Offset(0);
|
||||
if (m_autoHeight) {
|
||||
this->SetFixedHeight(this->GetContentSize().Height);
|
||||
this->GetScrollBar()->SetVisible(false);
|
||||
}
|
||||
else if (this->GetScrollBar()->IsVisible() == true) {
|
||||
this->GetScrollBar()->SetVisible(true);
|
||||
}
|
||||
this->GetScrollBar()->RefreshScroll();
|
||||
}
|
||||
void TileListView::Offset(int offset) {
|
||||
int _contentWidth = 0;
|
||||
int _contentHeight = 0;
|
||||
int maxWidth = this->Width();
|
||||
int maxHeight = 0;//每行最高的那个
|
||||
int x = 0;
|
||||
int y = offset;
|
||||
for (auto& _it : GetControls()) {
|
||||
if (_it->IsVisible() == false) {
|
||||
_it->SetLocation(Point{ 0,0 });
|
||||
continue;
|
||||
}
|
||||
Control& it = *_it;
|
||||
int itemWidth = it.Width() + it.Margin.GetHSpace();
|
||||
if (x + itemWidth > maxWidth) {
|
||||
//换行
|
||||
x = 0;
|
||||
y += maxHeight;
|
||||
maxHeight = 0;
|
||||
}
|
||||
|
||||
x += it.Margin.Left;//左边距
|
||||
int newX = x;//设置X坐标
|
||||
int newY = y + it.Margin.Top;//设置Y坐标+上边距
|
||||
it.SetRect(Rect(newX, newY, it.Width(), it.Height()));
|
||||
int itemHeight = it.Height() + it.Margin.GetVSpace();//当前控件垂直占用的空间
|
||||
if (maxHeight < itemHeight) {
|
||||
maxHeight = itemHeight;
|
||||
}
|
||||
x += (it.Width() + it.Margin.Right);//右边距
|
||||
_contentHeight = y + maxHeight;
|
||||
//计算最大宽度
|
||||
int _width = it.X() + it.Width();
|
||||
if (_width > _contentWidth) {
|
||||
_contentWidth = _width;
|
||||
}
|
||||
}
|
||||
this->SetContentSize({ _contentWidth,_contentHeight - offset });
|
||||
}
|
||||
};
|
||||
77
sources/Timer.cpp
Normal file
77
sources/Timer.cpp
Normal file
@@ -0,0 +1,77 @@
|
||||
#include "Timer.h"
|
||||
namespace ezui {
|
||||
Timer::Timer(Object* parentObject) :Object(parentObject)
|
||||
{
|
||||
}
|
||||
bool Timer::IsStopped() {
|
||||
return m_bPause.load();
|
||||
}
|
||||
void Timer::Start() {
|
||||
if (m_task == NULL) {
|
||||
m_event = CreateEvent(NULL, FALSE, FALSE, NULL); // 创建事件对象
|
||||
m_bExit = false;
|
||||
m_task = new Task([this]() {
|
||||
//等待信号与条件的lambda函数
|
||||
const auto wait = [this]() {
|
||||
while (!(this->m_bExit || !(this->m_bPause.load()))) {
|
||||
WaitForSingleObject(m_event, INFINITE);
|
||||
}
|
||||
};
|
||||
while (true)
|
||||
{
|
||||
wait();//等待信号与条件
|
||||
|
||||
if (this->m_bExit) {
|
||||
break;//退出循环
|
||||
}
|
||||
|
||||
//当等待时间过长(超过100毫秒) 将等待时间拆分为片段
|
||||
auto start = std::chrono::steady_clock::now();
|
||||
if (this->Interval > 100) {
|
||||
while (!(this->m_bExit)) {
|
||||
auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - start).count();
|
||||
if (elapsed >= this->Interval) {
|
||||
break;
|
||||
}
|
||||
DWORD remain = (DWORD)(this->Interval - elapsed);
|
||||
Sleep(remain > 100 ? 100 : remain);
|
||||
}
|
||||
}
|
||||
else {
|
||||
//等待时间过小 直接sleep
|
||||
Sleep(this->Interval);
|
||||
}
|
||||
|
||||
if (this->m_bExit) {
|
||||
break;//退出循环
|
||||
}
|
||||
|
||||
if (this->Tick) {
|
||||
this->Tick(this);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
//通知计时器继续运行
|
||||
m_bPause.store(false);
|
||||
if (m_event) {
|
||||
SetEvent(m_event); // 设置事件, 唤醒等待的线程
|
||||
}
|
||||
}
|
||||
void Timer::Stop() {
|
||||
//暂停计时器的运行
|
||||
m_bPause.store(true);
|
||||
}
|
||||
Timer::~Timer() {
|
||||
m_bExit = true;
|
||||
if (m_event) {
|
||||
SetEvent(m_event); // 设置事件, 唤醒等待的线程
|
||||
}
|
||||
if (m_task) {
|
||||
delete m_task; //结束掉轮询任务
|
||||
}
|
||||
if (m_event) {
|
||||
CloseHandle(m_event); // 关闭句柄, 释放资源
|
||||
}
|
||||
}
|
||||
};
|
||||
26
sources/TreeView.cpp
Normal file
26
sources/TreeView.cpp
Normal file
@@ -0,0 +1,26 @@
|
||||
#include "TreeView.h"
|
||||
#include "Label.h"
|
||||
namespace ezui {
|
||||
|
||||
TreeView::TreeView(Object* parentObj) :HListView(parentObj)
|
||||
{
|
||||
m_vList.SetAutoWidth(true);
|
||||
m_vList.SetDockStyle(DockStyle::Vertical);
|
||||
this->Add(&m_vList);
|
||||
}
|
||||
|
||||
void TreeView::AddNode(const UIString& nodeName)
|
||||
{
|
||||
Label* label = new Label(this);
|
||||
label->TextAlign = TextAlign::MiddleLeft;
|
||||
label->SetText(nodeName);
|
||||
label->SetAutoSize(true);
|
||||
m_vList.Add(label);
|
||||
}
|
||||
|
||||
TreeView::~TreeView()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
376
sources/UIManager.cpp
Normal file
376
sources/UIManager.cpp
Normal file
@@ -0,0 +1,376 @@
|
||||
#include "UIManager.h"
|
||||
#include "IFrame.h"
|
||||
|
||||
#include "tinyxml.h"
|
||||
#include "tinystr.h"
|
||||
|
||||
namespace ezui {
|
||||
inline UIString __Attribute(TiXmlElement* node, const char* szstr) {
|
||||
auto str = node->Attribute(szstr);
|
||||
if (str == NULL) return "";
|
||||
return str;
|
||||
}
|
||||
|
||||
std::map<UIString, std::function<Control* ()>> g_createFunc;
|
||||
void RegisterControl(const UIString& ctrlName, const std::function<Control* ()>& create_cb) {
|
||||
if (!ctrlName.empty() && create_cb) {
|
||||
g_createFunc[ctrlName.toLower()] = create_cb;
|
||||
}
|
||||
}
|
||||
void InitControls()
|
||||
{
|
||||
RegisterControl<Control>("control");
|
||||
RegisterControl<Control>("layout");
|
||||
RegisterControl<Control>("box");
|
||||
|
||||
RegisterControl<VListView>("vlist");
|
||||
RegisterControl<VListView>("vlistview");
|
||||
|
||||
RegisterControl<HListView>("hlist");
|
||||
RegisterControl<HListView>("hlistview");
|
||||
|
||||
RegisterControl<VLayout>("vlayout");
|
||||
RegisterControl<VLayout>("vbox");
|
||||
|
||||
RegisterControl<HLayout>("hlayout");
|
||||
RegisterControl<HLayout>("hbox");
|
||||
|
||||
RegisterControl<TileListView>("tilelist");
|
||||
RegisterControl<TileListView>("tilelistview");
|
||||
|
||||
RegisterControl<TabLayout>("tablayout");
|
||||
|
||||
RegisterControl<Spacer>("spacer");
|
||||
RegisterControl<VSpacer>("vspacer");
|
||||
RegisterControl<HSpacer>("hspacer");
|
||||
|
||||
RegisterControl<Label>("label");
|
||||
RegisterControl<Button>("button");
|
||||
RegisterControl<RadioButton>("radiobutton");
|
||||
RegisterControl<CheckBox>("checkbox");
|
||||
|
||||
RegisterControl<ComboBox>("combobox");
|
||||
RegisterControl<ComboBox>("select");
|
||||
|
||||
RegisterControl<TextBox>("edit");
|
||||
RegisterControl<TextBox>("textbox");
|
||||
RegisterControl<TextBox>("input");
|
||||
|
||||
RegisterControl<PictureBox>("img");
|
||||
RegisterControl<PictureBox>("pictureBox");
|
||||
|
||||
RegisterControl<ProgressBar>("progressbar");
|
||||
RegisterControl<ProgressBar>("progress");
|
||||
|
||||
RegisterControl<IFrame>("iframe");
|
||||
}
|
||||
};
|
||||
|
||||
namespace ezui {
|
||||
|
||||
int MathStyle(Control* ctl, const UIString& selectorName, const std::list<UIManager::Style>& selectors)
|
||||
{
|
||||
int mathCount = 0;
|
||||
for (auto& it : selectors) {
|
||||
if (it.m_selectorName == selectorName) {
|
||||
++mathCount;
|
||||
auto& styleType = it.m_styleType;
|
||||
auto& styleStr = it.m_styleStr;
|
||||
ctl->SetStyleSheet(styleType, styleStr);
|
||||
}
|
||||
}
|
||||
//返回匹配成功的个数
|
||||
return mathCount;
|
||||
}
|
||||
void ApplyStyle(Control* ctl, const UIString& selectName, const std::list<UIManager::Style>& selectors)
|
||||
{
|
||||
MathStyle(ctl, selectName, selectors);
|
||||
MathStyle(ctl, selectName + ":checked", selectors);
|
||||
MathStyle(ctl, selectName + ":active", selectors);
|
||||
MathStyle(ctl, selectName + ":hover", selectors);
|
||||
MathStyle(ctl, selectName + ":disabled", selectors);
|
||||
|
||||
//是否有滚动条 有滚动条则应用滚动条样式
|
||||
ScrollBar* scrollBar = ctl->GetScrollBar();
|
||||
if (scrollBar) {
|
||||
UIString scrollBarSelectName = UIString("%s::-webkit-scrollbar").format(selectName.c_str());
|
||||
MathStyle(scrollBar, scrollBarSelectName, selectors);
|
||||
MathStyle(scrollBar, scrollBarSelectName + ":active", selectors);
|
||||
MathStyle(scrollBar, scrollBarSelectName + ":hover", selectors);
|
||||
MathStyle(scrollBar, scrollBarSelectName + ":disabled", selectors);
|
||||
}
|
||||
}
|
||||
void UIManager::ApplyStyle(Control* ctl, const std::list<UIManager::Style>& selectors, const UIString& tagName) {
|
||||
{//加载样式 使用标签选择器
|
||||
if (!tagName.empty()) {
|
||||
UIString selectName = tagName;
|
||||
ezui::ApplyStyle(ctl, selectName, selectors);
|
||||
}
|
||||
}
|
||||
{//加载样式 使用属性选择器
|
||||
for (auto& attr : ctl->GetAttributes()) {
|
||||
UIString selectName = UIString("[%s=%s]").format(attr.first.c_str(), attr.second.c_str());
|
||||
ezui::ApplyStyle(ctl, selectName, selectors);
|
||||
}
|
||||
}
|
||||
{//加载样式 使用类选择器
|
||||
UIString classStr = ctl->GetAttribute("class");
|
||||
ui_text::Replace(&classStr, " ", ",");//去除所有空格转换为逗号
|
||||
auto classes = classStr.split(",");//分割类选择器
|
||||
for (const auto& className : classes) { //一个控件可能有多个类名
|
||||
UIString selectName = UIString(".%s").format(className.c_str());
|
||||
ezui::ApplyStyle(ctl, selectName, selectors);
|
||||
}
|
||||
}
|
||||
//加载样式 使用ID/name选择器
|
||||
if (!(ctl->Name.empty())) {
|
||||
UIString selectName = UIString("#%s").format(ctl->Name.c_str());
|
||||
ezui::ApplyStyle(ctl, selectName, selectors);
|
||||
}
|
||||
{//加载内联样式 优先级最高
|
||||
UIString sytle_static = ctl->GetAttribute("style");//内联样式语法
|
||||
if (!sytle_static.empty()) { //内联样式只允许描述静态效果
|
||||
ctl->SetStyleSheet(ControlState::Static, sytle_static.trim());
|
||||
}
|
||||
}
|
||||
}
|
||||
void UIManager::AnalysisStyle(const UIString& styleStr, std::list<UIManager::Style>* out) {
|
||||
UIString style = styleStr.trim();
|
||||
while (true)
|
||||
{
|
||||
//处理css的注释
|
||||
auto pos1 = style.find("/*");
|
||||
auto pos2 = style.find("*/", pos1 + 2);
|
||||
if (pos1 != std::string::npos && pos2 != std::string::npos) {
|
||||
style.erase(pos1, pos2 - pos1 + 2);
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
//分离每个样式
|
||||
std::list<UIString> strs;
|
||||
while (true)
|
||||
{
|
||||
auto pos1 = style.find("}");
|
||||
if (pos1 != std::string::npos) {
|
||||
strs.push_back(style.substr(0, pos1 + 1));
|
||||
style.erase(0, pos1 + 1);
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
//解析样式类型
|
||||
for (auto& style : strs) {
|
||||
size_t pos2 = style.find("}");
|
||||
if (pos2 == -1)break;
|
||||
size_t pos3 = style.find("{");
|
||||
UIString name = style.substr(0, pos3);
|
||||
size_t pos4 = name.find(":");
|
||||
UIString style_type;
|
||||
UIString str = style.substr(pos3 + 1, pos2 - pos3 - 1);
|
||||
if (pos4 != std::string::npos) {
|
||||
style_type = name.substr(pos4 + 1);
|
||||
style_type = style_type.trim();
|
||||
}
|
||||
//考虑到多个选择器
|
||||
auto names = name.split(",");
|
||||
for (auto& name : names) {
|
||||
//添加至集合
|
||||
UIManager::Style selector;
|
||||
selector.m_selectorName = name.trim();
|
||||
selector.m_styleStr = str.trim();
|
||||
|
||||
if (style_type == "hover") {
|
||||
selector.m_styleType = ControlState::Hover;
|
||||
}
|
||||
else if (style_type == "active") {
|
||||
selector.m_styleType = ControlState::Active;
|
||||
}
|
||||
else if (style_type == "checked") {
|
||||
selector.m_styleType = ControlState::Checked;
|
||||
}
|
||||
else {
|
||||
selector.m_styleType = ControlState::Static;
|
||||
}
|
||||
out->push_back(selector);
|
||||
}
|
||||
}
|
||||
}
|
||||
Control* UIManager::BuildControl(void* _node) {
|
||||
TiXmlElement* node = (TiXmlElement*)_node;
|
||||
Control* ctl = NULL;
|
||||
std::string tagName(node->ValueTStr().c_str());
|
||||
ctl = this->OnBuildControl(tagName);
|
||||
if (ctl == NULL) {
|
||||
UIString text = UIString("unknow element \"%s\"").format(tagName.c_str());
|
||||
::MessageBoxA(NULL, text.c_str(), "UIManager Erro", MB_OK);
|
||||
ctl = new Control;
|
||||
}
|
||||
//设置控件属性
|
||||
TiXmlAttribute* attr = node->FirstAttribute();
|
||||
do
|
||||
{
|
||||
if (!attr)break;
|
||||
UIString attrName = attr->Name();
|
||||
UIString attrValue(attr->Value());
|
||||
ctl->SetAttribute(attrName, attrValue);
|
||||
} while ((attr = attr->Next()));
|
||||
return ctl;
|
||||
}
|
||||
void UIManager::LoadControl(void* _node, Control* control) {
|
||||
TiXmlElement* node = (TiXmlElement*)_node;
|
||||
|
||||
TiXmlElement* fristChild = NULL;
|
||||
if ((fristChild = node->FirstChildElement()))//先寻找子控件
|
||||
{
|
||||
UIString tagName = fristChild->Value();
|
||||
Control* ctl = BuildControl(fristChild);
|
||||
this->RegisterControl(ctl, tagName);
|
||||
LoadControl(fristChild, ctl);
|
||||
control->Add(ctl);
|
||||
TiXmlElement* nextChild = fristChild->NextSiblingElement();
|
||||
while (nextChild)//然后寻找兄弟
|
||||
{
|
||||
UIString tagName = nextChild->Value();
|
||||
Control* ctl2 = BuildControl(nextChild);
|
||||
this->RegisterControl(ctl2, tagName);
|
||||
LoadControl(nextChild, ctl2);
|
||||
control->Add(ctl2);
|
||||
nextChild = nextChild->NextSiblingElement();
|
||||
}
|
||||
}
|
||||
}
|
||||
void UIManager::RegisterControl(Control* ctl, const UIString& tagNamee)
|
||||
{
|
||||
//弹簧需要交给控件自行处理
|
||||
if (dynamic_cast<Spacer*>(ctl) == NULL) {
|
||||
this->m_controls.emplace_front(ctl, tagNamee);
|
||||
}
|
||||
}
|
||||
Control* UIManager::OnBuildControl(const UIString& tagName_) {
|
||||
UIString tagName = tagName_.toLower();
|
||||
//优先匹配全局自定义控件
|
||||
for (auto& it : g_createFunc) {
|
||||
if (it.first == tagName) {
|
||||
Control* ctrl = it.second();
|
||||
if (ctrl) {
|
||||
return ctrl;
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
UIManager::UIManager()
|
||||
{
|
||||
}
|
||||
void UIManager::SetupUI(Window* window)
|
||||
{
|
||||
Control* root = GetRoot();
|
||||
if (root && !root->GetSize().Empty()) {
|
||||
window->SetSize(root->GetSize());
|
||||
window->CenterToScreen();
|
||||
}
|
||||
window->SetLayout(root);
|
||||
}
|
||||
void UIManager::SetupUI(Control* parentCtl)
|
||||
{
|
||||
if (GetRoot()) {
|
||||
parentCtl->Add(GetRoot());
|
||||
GetRoot()->SetDockStyle(DockStyle::Fill);
|
||||
}
|
||||
}
|
||||
|
||||
void UIManager::LoadXml(const UIString& fileName) {
|
||||
std::string data;
|
||||
if (GetResource(fileName, &data)) {
|
||||
LoadXml(data.c_str(), data.size());
|
||||
}
|
||||
}
|
||||
void UIManager::LoadXml(const char* fileData, size_t fileSize)
|
||||
{
|
||||
if (!m_rootNode.empty()) {
|
||||
ASSERT(!"XML cannot be loaded repeatedly!");
|
||||
}
|
||||
TiXmlDocument doc;
|
||||
auto result = doc.Parse(fileData, NULL, TiXmlEncoding::TIXML_ENCODING_UTF8);
|
||||
//doc.Parse
|
||||
TiXmlElement* element = doc.FirstChildElement();//read frist element
|
||||
std::list<TiXmlElement*> controlNodes;
|
||||
//存储样式
|
||||
UIString styleStr;
|
||||
//先处理样式
|
||||
do
|
||||
{
|
||||
if (element == NULL) break;
|
||||
if (element->ValueTStr() == "style") {// if element is style
|
||||
const char* szStr = element->GetText();
|
||||
if (szStr) {
|
||||
styleStr = szStr;
|
||||
}
|
||||
}
|
||||
else { //if no style , must be Control
|
||||
controlNodes.push_back(element);
|
||||
}
|
||||
} while ((element = element->NextSiblingElement()));
|
||||
//然后处理控件
|
||||
for (auto& element : controlNodes) {
|
||||
UIString tagName = element->Value();
|
||||
Control* control = BuildControl(element);//先加载根节点
|
||||
this->RegisterControl(control, tagName);
|
||||
m_rootNode.push_back(control);//存入根节点集合
|
||||
LoadControl(element, control);//加载子节点
|
||||
}
|
||||
//加载样式
|
||||
this->SetStyleSheet(styleStr);
|
||||
}
|
||||
void UIManager::LoadStyle(const UIString& fileName)
|
||||
{
|
||||
std::string data;
|
||||
if (!GetResource(fileName, &data)) {
|
||||
return;
|
||||
}
|
||||
this->SetStyleSheet(data);
|
||||
}
|
||||
void UIManager::SetStyleSheet(const UIString& styleContent)
|
||||
{
|
||||
UIString data = styleContent;
|
||||
std::list<UIManager::Style> styles;
|
||||
//分析出样式
|
||||
this->AnalysisStyle(data, &styles);
|
||||
//保存样式到全局
|
||||
this->m_styles.splice(this->m_styles.end(), styles); // 把 styles 的所有元素移动到 this->_styles 末尾
|
||||
if (!this->m_styles.empty()) {
|
||||
for (auto& it : this->m_controls) {
|
||||
//对所有加载进来的控件应用样式
|
||||
this->ApplyStyle(it.m_ctl, this->m_styles, it.m_tagName);
|
||||
}
|
||||
}
|
||||
}
|
||||
void UIManager::Free(Control** ctl)
|
||||
{
|
||||
for (auto itor = m_controls.begin(); itor != m_controls.end(); ++itor)
|
||||
{
|
||||
if (itor->m_ctl == *ctl) {
|
||||
delete* ctl;
|
||||
*ctl = NULL;
|
||||
m_controls.erase(itor);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
UIManager::~UIManager() {
|
||||
for (auto& it : m_controls) {
|
||||
delete it.m_ctl;
|
||||
}
|
||||
}
|
||||
|
||||
Control* UIManager::GetRoot(int index) {
|
||||
if (index >= 0 && index < m_rootNode.size()) {
|
||||
return m_rootNode[index];
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
};
|
||||
91
sources/UISelector.cpp
Normal file
91
sources/UISelector.cpp
Normal file
@@ -0,0 +1,91 @@
|
||||
#include "UISelector.h"
|
||||
|
||||
namespace ezui {
|
||||
UISelector::UISelector(const std::vector<Control*>& controls)
|
||||
{
|
||||
this->m_ctls = controls;
|
||||
}
|
||||
UISelector::UISelector(const std::list<Control*>& controls)
|
||||
{
|
||||
this->m_ctls.resize(controls.size());
|
||||
for (auto& it : controls) {
|
||||
this->m_ctls.push_back(it);
|
||||
}
|
||||
}
|
||||
UISelector::UISelector(Control* ctl)
|
||||
{
|
||||
this->m_ctl = ctl;
|
||||
}
|
||||
UISelector::UISelector(Control* ct, const UIString& _mathStr)
|
||||
{
|
||||
UIString mathStr = _mathStr;
|
||||
mathStr.replace(" ", " ");
|
||||
//模仿jquery进行元素匹配
|
||||
}
|
||||
UISelector::~UISelector()
|
||||
{
|
||||
}
|
||||
UISelector& UISelector::Css(const UIString& styleStr)
|
||||
{
|
||||
/* for (auto& it : this->ctls) {
|
||||
if (notCtl == it)continue;
|
||||
it->SetStyleSheet(styleStr);
|
||||
}
|
||||
if (ctl) {
|
||||
if (notCtl == ctl)return *this;
|
||||
ctl->SetStyleSheet(styleStr);
|
||||
}*/
|
||||
return *this;
|
||||
}
|
||||
UISelector& UISelector::CssHover(const UIString& styleStr)
|
||||
{
|
||||
/* for (auto& it : this->ctls) {
|
||||
if (notCtl == it)continue;
|
||||
it->SetStyleSheet(styleStr, ControlState::Hover);
|
||||
}
|
||||
if (ctl) {
|
||||
if (notCtl == ctl)return *this;
|
||||
ctl->SetStyleSheet(styleStr, ControlState::Hover);
|
||||
}*/
|
||||
return *this;
|
||||
}
|
||||
UISelector& UISelector::CssActive(const UIString& styleStr)
|
||||
{
|
||||
/* for (auto& it : this->ctls) {
|
||||
if (notCtl == it)continue;
|
||||
it->SetStyleSheet(styleStr, ControlState::Active);
|
||||
}
|
||||
if (ctl) {
|
||||
if (notCtl == ctl)return *this;
|
||||
ctl->SetStyleSheet(styleStr, ControlState::Active);
|
||||
}*/
|
||||
return *this;
|
||||
}
|
||||
UISelector& UISelector::Attr(const UIString& key, const UIString& value)
|
||||
{
|
||||
for (auto& it : this->m_ctls) {
|
||||
if (m_notCtl == it)continue;
|
||||
it->SetAttribute(key, value);
|
||||
}
|
||||
if (m_ctl) {
|
||||
if (m_notCtl == m_ctl)return *this;
|
||||
m_ctl->SetAttribute(key, value);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
UISelector& UISelector::Refresh()
|
||||
{
|
||||
for (auto& it : this->m_ctls) {
|
||||
it->Invalidate();
|
||||
}
|
||||
if (m_ctl) {
|
||||
m_ctl->Invalidate();
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
UISelector& UISelector::Not(Control* fiterCtl) {
|
||||
|
||||
this->m_notCtl = fiterCtl;
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
294
sources/UIString.cpp
Normal file
294
sources/UIString.cpp
Normal file
@@ -0,0 +1,294 @@
|
||||
#include "UIString.h"
|
||||
namespace ezui {
|
||||
|
||||
namespace ui_text {
|
||||
//-----------------------------------------------Copy Start-----------------------------------------------
|
||||
size_t String::utf8Length() const {
|
||||
auto* p = this->c_str();
|
||||
size_t pos = 0, count = 0;
|
||||
while (p[pos] && pos < this->size()) {
|
||||
if ((p[pos] & 0xc0) != 0x80) {
|
||||
++count;
|
||||
}
|
||||
++pos;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
String::String() {}
|
||||
String::~String() {}
|
||||
String::String(const String& _right)noexcept :std::string(_right) {}
|
||||
String::String(String&& _right) noexcept :std::string(std::move(_right)) {}
|
||||
String& String::operator=(const String& _right) noexcept
|
||||
{
|
||||
(std::string&)*this = _right;
|
||||
return *this;
|
||||
}
|
||||
String& String::operator=(String&& _right) noexcept
|
||||
{
|
||||
std::string::operator=(std::move(_right));
|
||||
return *this;
|
||||
}
|
||||
String::String(const std::string& str)noexcept :std::string(str) {}
|
||||
String::String(const char* szbuf)noexcept :std::string(szbuf) {}
|
||||
String::String(const wchar_t* szbuf)noexcept {
|
||||
if (szbuf == NULL)return;
|
||||
UnicodeToUTF8(szbuf, this);
|
||||
}
|
||||
String::String(const std::wstring& wstr)noexcept {
|
||||
UnicodeToUTF8(wstr, this);
|
||||
}
|
||||
void String::erase(char _ch)
|
||||
{
|
||||
Erase(this, _ch);
|
||||
}
|
||||
void String::erase(size_t pos, size_t count)
|
||||
{
|
||||
__super::erase(pos, count);
|
||||
}
|
||||
String String::replace(char oldChar, char newChar) const
|
||||
{
|
||||
String newStr = *this;
|
||||
Replace(&newStr, oldChar, newChar);
|
||||
return newStr;
|
||||
}
|
||||
String String::replace(const String& oldText, const String& newText, bool allReplace) const
|
||||
{
|
||||
String newStr = *this;
|
||||
Replace(&newStr, oldText, newText, allReplace);
|
||||
return newStr;
|
||||
}
|
||||
String String::toLower() const
|
||||
{
|
||||
String str(*this);
|
||||
Tolower(&str);
|
||||
return str;
|
||||
}
|
||||
String String::toUpper() const
|
||||
{
|
||||
String str(*this);
|
||||
Toupper(&str);
|
||||
return str;
|
||||
}
|
||||
String String::trim()const {
|
||||
String data = *this;
|
||||
const char* raw = data.c_str();
|
||||
size_t totalLen = data.size();
|
||||
// 找起始位置
|
||||
size_t start = 0;
|
||||
while (start < totalLen && raw[start] == ' ') {
|
||||
++start;
|
||||
}
|
||||
// 找结束位置(最后一个非空格字符之后的位置)
|
||||
size_t end = totalLen;
|
||||
while (end > start && raw[end - 1] == ' ') {
|
||||
--end;
|
||||
}
|
||||
return std::string(raw + start, end - start);
|
||||
}
|
||||
size_t String::count(const String& value)const
|
||||
{
|
||||
size_t count = 0;
|
||||
size_t pos = 0;
|
||||
while ((pos = this->find(value, pos)) != std::string::npos)
|
||||
{
|
||||
++count;
|
||||
pos += value.size();
|
||||
}
|
||||
return count;
|
||||
}
|
||||
bool String::operator==(const wchar_t* szbuf)const
|
||||
{
|
||||
std::string u8str;
|
||||
UnicodeToUTF8(szbuf, &u8str);
|
||||
return (*this == u8str);
|
||||
}
|
||||
bool String::operator==(const std::wstring& wStr)const
|
||||
{
|
||||
std::string u8str;
|
||||
UnicodeToUTF8(wStr, &u8str);
|
||||
return (*this == u8str);
|
||||
}
|
||||
std::wstring String::unicode() const {
|
||||
std::wstring wstr;
|
||||
UTF8ToUnicode(*this, &wstr);
|
||||
return wstr;
|
||||
}
|
||||
std::string String::ansi() const {
|
||||
std::string str;
|
||||
UTF8ToANSI(*this, &str);
|
||||
return str;
|
||||
}
|
||||
|
||||
void AnyToUnicode(const std::string& src_str, UINT codePage, std::wstring* out_wstr) {
|
||||
std::wstring& wstrCmd = *out_wstr;
|
||||
auto bytes = ::MultiByteToWideChar(codePage, 0, src_str.c_str(), src_str.size(), NULL, 0);
|
||||
wstrCmd.resize(bytes);
|
||||
bytes = ::MultiByteToWideChar(codePage, 0, src_str.c_str(), src_str.size(), const_cast<wchar_t*>(wstrCmd.c_str()), wstrCmd.size());
|
||||
}
|
||||
void UnicodeToAny(const std::wstring& wstr, UINT codePage, std::string* out_str) {
|
||||
std::string& strCmd = *out_str;
|
||||
auto bytes = ::WideCharToMultiByte(codePage, 0, wstr.c_str(), wstr.size(), NULL, 0, NULL, NULL);
|
||||
strCmd.resize(bytes);
|
||||
bytes = ::WideCharToMultiByte(codePage, 0, wstr.c_str(), wstr.size(), const_cast<char*>(strCmd.c_str()), strCmd.size(), NULL, NULL);
|
||||
}
|
||||
|
||||
//以下是静态函数
|
||||
void ANSIToUniCode(const std::string& str, std::wstring* outStr)
|
||||
{
|
||||
AnyToUnicode(str, ::GetACP(), outStr);
|
||||
}
|
||||
void UnicodeToANSI(const std::wstring& wstr, std::string* outStr)
|
||||
{
|
||||
UnicodeToAny(wstr, ::GetACP(), outStr);
|
||||
}
|
||||
|
||||
void GBKToUTF8(const std::string& str, std::string* outStr) {
|
||||
UINT gbkCodePage = 936;
|
||||
std::wstring wstr;
|
||||
AnyToUnicode(str, gbkCodePage, &wstr);
|
||||
UnicodeToUTF8(wstr, outStr);
|
||||
}
|
||||
|
||||
void UTF8ToGBK(const std::string& str, std::string* outStr) {
|
||||
UINT gbkCodePage = 936;
|
||||
std::wstring wstr;
|
||||
UTF8ToUnicode(str, &wstr);
|
||||
UnicodeToAny(wstr, gbkCodePage, outStr);
|
||||
}
|
||||
|
||||
void ANSIToUTF8(const std::string& str, std::string* outStr)
|
||||
{
|
||||
UINT codePage = ::GetACP();
|
||||
if (codePage == CP_UTF8) {
|
||||
*outStr = str;//如果本身就是utf8则不需要转换
|
||||
return;
|
||||
}
|
||||
std::wstring wstr;
|
||||
AnyToUnicode(str, codePage, &wstr);
|
||||
UnicodeToUTF8(wstr, outStr);
|
||||
}
|
||||
void UTF8ToANSI(const std::string& str, std::string* outStr) {
|
||||
UINT codePage = ::GetACP();
|
||||
if (codePage == CP_UTF8) {
|
||||
*outStr = str;//如果本身就是utf8则不需要转换
|
||||
return;
|
||||
}
|
||||
std::wstring wstr;
|
||||
UTF8ToUnicode(str, &wstr);
|
||||
UnicodeToAny(wstr, codePage, outStr);
|
||||
}
|
||||
void UnicodeToUTF8(const std::wstring& wstr, std::string* outStr)
|
||||
{
|
||||
UnicodeToAny(wstr, CP_UTF8, outStr);
|
||||
}
|
||||
void UTF8ToUnicode(const std::string& str, std::wstring* outStr) {
|
||||
AnyToUnicode(str, CP_UTF8, outStr);
|
||||
}
|
||||
|
||||
void Tolower(std::string* str_in_out)
|
||||
{
|
||||
std::string& str = *str_in_out;
|
||||
for (size_t i = 0; i < str.size(); ++i)
|
||||
{
|
||||
char& ch = (char&)str.c_str()[i];
|
||||
if (ch >= 65 && ch <= 90) {
|
||||
ch += 32;
|
||||
}
|
||||
}
|
||||
}
|
||||
void Toupper(std::string* str_in_out)
|
||||
{
|
||||
std::string& str = *str_in_out;
|
||||
for (size_t i = 0; i < str.size(); ++i)
|
||||
{
|
||||
char& ch = (char&)str.c_str()[i];
|
||||
if (ch >= 97 && ch <= 122) {
|
||||
ch -= 32;
|
||||
}
|
||||
}
|
||||
}
|
||||
void Erase(std::string* str_in_out, char _char) {
|
||||
const String& self = *str_in_out;
|
||||
char* bufStr = new char[self.size() + 1] { 0 };
|
||||
size_t pos = 0;
|
||||
for (auto& it : self) {
|
||||
if (_char == it)continue;
|
||||
bufStr[pos] = it;
|
||||
++pos;
|
||||
}
|
||||
*str_in_out = bufStr;
|
||||
delete[] bufStr;
|
||||
}
|
||||
size_t Replace(std::string* str_in_out, const std::string& oldText, const std::string& newText, bool replaceAll)
|
||||
{
|
||||
if (!str_in_out || oldText.empty()) return 0;
|
||||
size_t count = 0;
|
||||
std::string& str = *str_in_out;
|
||||
size_t pos = 0;
|
||||
while ((pos = str.find(oldText, pos)) != std::string::npos)
|
||||
{
|
||||
str.replace(pos, oldText.size(), newText);
|
||||
++count;
|
||||
if (!replaceAll) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
void Replace(std::string* str_in_out, char oldChar, char newChar)
|
||||
{
|
||||
for (auto& it : *str_in_out) {
|
||||
|
||||
if (it == oldChar) {
|
||||
it = newChar;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void __Split(const std::string& str_in, const std::string& ch_, std::vector<T>* strs_out) {
|
||||
std::vector<T>& arr = *strs_out;
|
||||
arr.clear();
|
||||
if (str_in.empty()) return;
|
||||
|
||||
std::string buf = str_in;
|
||||
size_t pos = buf.find(ch_);
|
||||
if (pos == std::string::npos) {
|
||||
arr.push_back(buf);
|
||||
return;
|
||||
}
|
||||
while (pos != std::string::npos) {
|
||||
auto item = buf.substr(0, pos);
|
||||
if (!item.empty()) {
|
||||
arr.push_back(item);
|
||||
}
|
||||
buf = buf.erase(0, pos + ch_.size());
|
||||
pos = buf.find(ch_);
|
||||
if (pos == std::string::npos) {
|
||||
if (!buf.empty()) {
|
||||
arr.push_back(buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<String> String::split(const String& ch)const
|
||||
{
|
||||
std::vector<String> strs;
|
||||
__Split<String>(*this, ch, &strs);
|
||||
return strs;
|
||||
}
|
||||
|
||||
void Split(const std::string& str_in, const std::string& ch_, std::vector<std::string>* strs_out) {
|
||||
|
||||
__Split<std::string>(str_in, ch_, strs_out);
|
||||
}
|
||||
|
||||
String ToString(double number, size_t keepBitSize) {
|
||||
std::ostringstream oss;
|
||||
oss << std::fixed << std::setprecision(keepBitSize) << number;
|
||||
return oss.str();
|
||||
}
|
||||
//-----------------------------------------------Copy End-----------------------------------------------
|
||||
};
|
||||
};
|
||||
92
sources/VLayout.cpp
Normal file
92
sources/VLayout.cpp
Normal file
@@ -0,0 +1,92 @@
|
||||
#include "VLayout.h"
|
||||
namespace ezui {
|
||||
VLayout::VLayout(Object* parentObject) :Control(parentObject)
|
||||
{
|
||||
}
|
||||
void VLayout::SetAttribute(const UIString& key, const UIString& value)
|
||||
{
|
||||
if (key == "halign" || key == "align") {
|
||||
if (value == "left") {
|
||||
ContentAlign = HAlign::Left;
|
||||
}
|
||||
else if (value == "right") {
|
||||
ContentAlign = HAlign::Right;
|
||||
}
|
||||
}
|
||||
__super::SetAttribute(key, value);
|
||||
}
|
||||
VLayout::~VLayout()
|
||||
{
|
||||
}
|
||||
|
||||
void VLayout::OnLayout()
|
||||
{
|
||||
int contentWidth = 0;
|
||||
|
||||
int fixedHeight = 0;
|
||||
int fixedTotal = 0;
|
||||
int count = 0;//可见控件总数
|
||||
for (auto& it : GetControls()) {
|
||||
if ((it->IsAutoWidth() || it->IsAutoHeight()) && it->IsPendLayout()) {
|
||||
it->RefreshLayout();
|
||||
}
|
||||
if (it->IsVisible() == false || (it->IsAutoHeight() && it->GetFixedHeight() <= 0))continue;
|
||||
++count;
|
||||
auto height = it->GetFixedHeight();
|
||||
if (height > 0) {
|
||||
fixedHeight += height;
|
||||
++fixedTotal;
|
||||
}
|
||||
fixedHeight += +it->Margin.GetVSpace();
|
||||
}
|
||||
int autoTotal = count - fixedTotal;
|
||||
if (autoTotal == 0) {
|
||||
//return;
|
||||
}
|
||||
double otherHeight = Height() * 1.0 - fixedHeight;
|
||||
double autoHeight = otherHeight / autoTotal;
|
||||
double maxBottom = 0;
|
||||
//排序
|
||||
for (auto& it : GetControls()) {
|
||||
if (it->IsVisible() == false)continue;
|
||||
|
||||
maxBottom += it->Margin.Top;
|
||||
int width = it->GetFixedWidth();
|
||||
int x = it->X();
|
||||
|
||||
if (width == 0) {
|
||||
//当控件未指定绝对宽度的时候
|
||||
width = this->Width() - it->Margin.GetHSpace();
|
||||
x = it->Margin.Left;
|
||||
}
|
||||
else {
|
||||
//当控件指定了绝对宽度的时候
|
||||
if (ContentAlign == HAlign::Left) {
|
||||
x = it->Margin.Left;
|
||||
}
|
||||
else if (ContentAlign == HAlign::Center) {
|
||||
x = int((this->Width() * 1.0 - width) / 2.0f + 0.5);
|
||||
}
|
||||
else if (ContentAlign == HAlign::Right) {
|
||||
x = this->Width() - it->Margin.Right - width;
|
||||
}
|
||||
}
|
||||
|
||||
if (it->GetFixedHeight() > 0 || it->IsAutoHeight()) {
|
||||
it->SetRect({ x, (int)maxBottom,width, it->GetFixedHeight() });
|
||||
maxBottom += it->Height();
|
||||
}
|
||||
else {
|
||||
it->SetRect({ x ,(int)maxBottom,width ,(int)autoHeight });
|
||||
maxBottom += it->Height();
|
||||
}
|
||||
maxBottom += it->Margin.Bottom;
|
||||
//统计上下文宽高
|
||||
int maxRight = it->GetRect().GetRight() + it->Margin.Right;
|
||||
if (maxRight > contentWidth) {
|
||||
contentWidth = maxRight;
|
||||
}
|
||||
this->SetContentSize({ contentWidth ,(int)(maxBottom + 0.5) });
|
||||
}
|
||||
}
|
||||
};
|
||||
94
sources/VListView.cpp
Normal file
94
sources/VListView.cpp
Normal file
@@ -0,0 +1,94 @@
|
||||
#include "VListView.h"
|
||||
namespace ezui {
|
||||
void VListView::Init()
|
||||
{
|
||||
this->GetScrollBar()->SetHeight(Height());//滚动条宽度
|
||||
this->GetScrollBar()->Parent = this;
|
||||
this->GetScrollBar()->OffsetCallback = [this](int offsetValue)->void {
|
||||
if (this->GetScrollBar()->ScrollPos() >= 1) {
|
||||
NextPage();
|
||||
}
|
||||
this->Offset(offsetValue);
|
||||
};
|
||||
}
|
||||
VListView::VListView(Object* parentObject) :PagedListView(parentObject)
|
||||
{
|
||||
Init();
|
||||
}
|
||||
VListView::~VListView()
|
||||
{
|
||||
}
|
||||
void VListView::OnLayout() {
|
||||
this->Offset(0);
|
||||
if (IsAutoHeight()) {
|
||||
this->GetScrollBar()->SetVisible(false);
|
||||
}
|
||||
else if (this->GetScrollBar()->IsVisible() == true) {
|
||||
this->GetScrollBar()->SetVisible(true);
|
||||
}
|
||||
this->GetScrollBar()->RefreshScroll();
|
||||
this->EndLayout();
|
||||
}
|
||||
|
||||
ScrollBar* VListView::GetScrollBar()
|
||||
{
|
||||
return &m_vScrollBar;
|
||||
}
|
||||
|
||||
void VListView::Offset(int offset)
|
||||
{
|
||||
int contentWidth = 0;
|
||||
int _maxBottom = offset;
|
||||
for (auto& it : GetControls()) {
|
||||
if (it->IsVisible() == false) {
|
||||
it->SetY(0);
|
||||
continue;
|
||||
}
|
||||
{
|
||||
//处理margin和x坐标
|
||||
int width = it->GetFixedWidth();
|
||||
if (width == 0) {
|
||||
width = this->Width() - it->Margin.GetHSpace();
|
||||
}
|
||||
int x = it->X();
|
||||
if (x == 0) {
|
||||
x = it->Margin.Left;
|
||||
}
|
||||
if (x == 0 && width < this->Width()) {
|
||||
x = int((this->Width() * 1.0 - width) / 2 + 0.5);
|
||||
}
|
||||
_maxBottom += it->Margin.Top;
|
||||
it->SetRect(Rect{ x,_maxBottom,width,it->Height() });
|
||||
_maxBottom += it->Height();
|
||||
_maxBottom += it->Margin.Bottom;
|
||||
}
|
||||
//计算最大宽度
|
||||
int _width = it->X() + it->Width() + it->Margin.Right;
|
||||
if (_width > contentWidth) {
|
||||
contentWidth = _width;
|
||||
}
|
||||
}
|
||||
this->SetContentSize({ contentWidth, _maxBottom - offset });
|
||||
}
|
||||
|
||||
void VListView::OnChildPaint(PaintEventArgs& args) {
|
||||
ViewControls.clear();
|
||||
//绘制子控件
|
||||
auto rect = Rect(0, 0, Width(), Height());
|
||||
for (auto& it : GetControls()) {
|
||||
if (rect.IntersectsWith(it->GetRect())) {
|
||||
ViewControls.push_back(it);
|
||||
}
|
||||
if (it->Y() >= Height()) {
|
||||
break;
|
||||
////当控件超出容器底部将不再派发绘制事件 但是仍然要进行布局
|
||||
//if (it->IsAutoHeight() && it->GetLayoutState() == LayoutState::Pend) {
|
||||
// it->RefreshLayout();
|
||||
//}
|
||||
}
|
||||
else {
|
||||
it->SendEvent(args);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
70
sources/VScrollBar.cpp
Normal file
70
sources/VScrollBar.cpp
Normal file
@@ -0,0 +1,70 @@
|
||||
#include "VScrollBar.h"
|
||||
namespace ezui {
|
||||
VScrollBar::VScrollBar(Object* parentObject):ScrollBar(parentObject)
|
||||
{
|
||||
}
|
||||
VScrollBar::~VScrollBar() { }
|
||||
void VScrollBar::ScrollTo(Control* ctl)
|
||||
{
|
||||
if (ctl && ctl->Parent && ctl->Parent == this->Parent) {
|
||||
if (ctl->Parent->IsPendLayout()) {
|
||||
ctl->Parent->RefreshLayout();
|
||||
}
|
||||
//控件的矩形位置
|
||||
const Rect& ctlRect = ctl->GetRect();
|
||||
if (ctlRect.Y >= 0 && ctlRect.GetBottom() <= Height()) {
|
||||
return;
|
||||
}
|
||||
//出现在顶部
|
||||
int offset = this->m_offset - ctlRect.Y;
|
||||
if (ctlRect.Y > 0) {
|
||||
//出现在底部
|
||||
offset += this->m_viewLength - ctlRect.Height;
|
||||
}
|
||||
__super::ScrollTo(offset, Event::None);
|
||||
}
|
||||
}
|
||||
|
||||
Rect VScrollBar::GetSliderRect() {
|
||||
Rect sliderRect;
|
||||
sliderRect.X = 0;
|
||||
sliderRect.Y = m_sliderPos;
|
||||
sliderRect.Width = Width();
|
||||
sliderRect.Height = m_sliderLength;
|
||||
if (this->Scrollable() && sliderRect.Height <= 0) {
|
||||
sliderRect.Height = 1;
|
||||
}
|
||||
return sliderRect;
|
||||
}
|
||||
void VScrollBar::GetInfo(int* viewLength, int* contentLength, int* scrollBarLength)
|
||||
{
|
||||
*viewLength = this->Parent->Height();
|
||||
*contentLength = this->Parent->GetContentSize().Height;
|
||||
*scrollBarLength = Height();
|
||||
}
|
||||
void VScrollBar::ParentSize(const Size& size) {
|
||||
this->SetRect({ size.Width - this->Width() ,0,this->Width(),size.Height });
|
||||
}
|
||||
void VScrollBar::OnMouseDown(const MouseEventArgs& arg) {
|
||||
__super::OnMouseDown(arg);
|
||||
Rect sliderRect = GetSliderRect();
|
||||
if (sliderRect.IsEmptyArea()) { return; }
|
||||
auto point = arg.Location;
|
||||
if (arg.Button == MouseButton::Left && sliderRect.Contains({ point.X,point.Y })) {
|
||||
m_mouseDown = true;
|
||||
this->m_lastPoint = point.Y;
|
||||
}
|
||||
}
|
||||
void VScrollBar::OnMouseMove(const MouseEventArgs& arg)
|
||||
{
|
||||
__super::OnMouseMove(arg);
|
||||
if (m_mouseDown) {
|
||||
auto point = arg.Location;
|
||||
int offsetY = point.Y - this->m_lastPoint;
|
||||
m_sliderPos += offsetY;
|
||||
m_lastPoint = point.Y;
|
||||
int offset = m_sliderPos * this->m_rollRate + 0.5;
|
||||
__super::ScrollTo(-offset, Event::OnMouseDrag);
|
||||
}
|
||||
}
|
||||
};
|
||||
1106
sources/Window.cpp
Normal file
1106
sources/Window.cpp
Normal file
File diff suppressed because it is too large
Load Diff
111
sources/tinystr.cpp
Normal file
111
sources/tinystr.cpp
Normal file
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
www.sourceforge.net/projects/tinyxml
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any
|
||||
damages arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any
|
||||
purpose, including commercial applications, and to alter it and
|
||||
redistribute it freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must
|
||||
not claim that you wrote the original software. If you use this
|
||||
software in a product, an acknowledgment in the product documentation
|
||||
would be appreciated but is not required.
|
||||
|
||||
2. Altered source versions must be plainly marked as such, and
|
||||
must not be misrepresented as being the original software.
|
||||
|
||||
3. This notice may not be removed or altered from any source
|
||||
distribution.
|
||||
*/
|
||||
|
||||
#ifndef TIXML_USE_STL
|
||||
|
||||
#include "tinystr.h"
|
||||
namespace ezui {
|
||||
|
||||
// Error value for find primitive
|
||||
const TiXmlString::size_type TiXmlString::npos = static_cast<TiXmlString::size_type>(-1);
|
||||
|
||||
// Null rep.
|
||||
TiXmlString::Rep TiXmlString::nullrep_ = { 0, 0, { '\0' } };
|
||||
|
||||
|
||||
void TiXmlString::reserve(size_type cap)
|
||||
{
|
||||
if (cap > capacity())
|
||||
{
|
||||
TiXmlString tmp;
|
||||
tmp.init(length(), cap);
|
||||
memcpy(tmp.start(), data(), length());
|
||||
swap(tmp);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TiXmlString& TiXmlString::assign(const char* str, size_type len)
|
||||
{
|
||||
size_type cap = capacity();
|
||||
if (len > cap || cap > 3 * (len + 8))
|
||||
{
|
||||
TiXmlString tmp;
|
||||
tmp.init(len);
|
||||
memcpy(tmp.start(), str, len);
|
||||
swap(tmp);
|
||||
}
|
||||
else
|
||||
{
|
||||
memmove(start(), str, len);
|
||||
set_size(len);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
TiXmlString& TiXmlString::append(const char* str, size_type len)
|
||||
{
|
||||
size_type newsize = length() + len;
|
||||
if (newsize > capacity())
|
||||
{
|
||||
reserve(newsize + capacity());
|
||||
}
|
||||
memmove(finish(), str, len);
|
||||
set_size(newsize);
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
TiXmlString operator + (const TiXmlString& a, const TiXmlString& b)
|
||||
{
|
||||
TiXmlString tmp;
|
||||
tmp.reserve(a.length() + b.length());
|
||||
tmp += a;
|
||||
tmp += b;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
TiXmlString operator + (const TiXmlString& a, const char* b)
|
||||
{
|
||||
TiXmlString tmp;
|
||||
TiXmlString::size_type b_len = static_cast<TiXmlString::size_type>(strlen(b));
|
||||
tmp.reserve(a.length() + b_len);
|
||||
tmp += a;
|
||||
tmp.append(b, b_len);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
TiXmlString operator + (const char* a, const TiXmlString& b)
|
||||
{
|
||||
TiXmlString tmp;
|
||||
TiXmlString::size_type a_len = static_cast<TiXmlString::size_type>(strlen(a));
|
||||
tmp.reserve(a_len + b.length());
|
||||
tmp.append(a, a_len);
|
||||
tmp += b;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#endif // TIXML_USE_STL
|
||||
1889
sources/tinyxml.cpp
Normal file
1889
sources/tinyxml.cpp
Normal file
File diff suppressed because it is too large
Load Diff
54
sources/tinyxmlerror.cpp
Normal file
54
sources/tinyxmlerror.cpp
Normal file
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
www.sourceforge.net/projects/tinyxml
|
||||
Original code (2.0 and earlier )copyright (c) 2000-2006 Lee Thomason (www.grinninglizard.com)
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any
|
||||
damages arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any
|
||||
purpose, including commercial applications, and to alter it and
|
||||
redistribute it freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must
|
||||
not claim that you wrote the original software. If you use this
|
||||
software in a product, an acknowledgment in the product documentation
|
||||
would be appreciated but is not required.
|
||||
|
||||
2. Altered source versions must be plainly marked as such, and
|
||||
must not be misrepresented as being the original software.
|
||||
|
||||
3. This notice may not be removed or altered from any source
|
||||
distribution.
|
||||
*/
|
||||
|
||||
#include "tinyxml.h"
|
||||
|
||||
// The goal of the seperate error file is to make the first
|
||||
// step towards localization. tinyxml (currently) only supports
|
||||
// english error messages, but the could now be translated.
|
||||
//
|
||||
// It also cleans up the code a bit.
|
||||
//
|
||||
namespace ezui {
|
||||
const char* TiXmlBase::errorString[TiXmlBase::TIXML_ERROR_STRING_COUNT] =
|
||||
{
|
||||
"No error",
|
||||
"Error",
|
||||
"Failed to open file",
|
||||
"Error parsing Element.",
|
||||
"Failed to read Element name",
|
||||
"Error reading Element value.",
|
||||
"Error reading Attributes.",
|
||||
"Error: empty tag.",
|
||||
"Error reading end tag.",
|
||||
"Error parsing Unknown.",
|
||||
"Error parsing Comment.",
|
||||
"Error parsing Declaration.",
|
||||
"Error document empty.",
|
||||
"Error null (0) or unexpected EOF found in input stream.",
|
||||
"Error parsing CDATA.",
|
||||
"Error when TiXmlDocument added to document, because TiXmlDocument can only be at the root.",
|
||||
};
|
||||
|
||||
}
|
||||
1645
sources/tinyxmlparser.cpp
Normal file
1645
sources/tinyxmlparser.cpp
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user