2502,索界面3

news/2025/2/5 20:03:33/

原文

SonicUI,你从未见过的方便GUI引擎-源码

介绍

SonicUI是基于原生GDIAPIGUI引擎.它提供了几个简单的UI组件来实现高效的UI效果,如自绘按钮,不规则窗口,动画,窗口中的网径图像操作方法.
主要目的是用最少的代码来达到最佳效果.

背景

周知,UI开发一般重复用无趣.因此,设计此引擎遵守了两个原则:易于使用和高效.看看下面引擎的用法,和一些有趣的要点.

使用代码

首先,让我介绍类工厂和组件管:ISonicUI.用此接口来创建和析构对象,并用作某些全局函数.

1.显示和旋转图像

图像操作接口:ISonicImage,用来加载,保存,旋转,拉伸,灰度HSL调整等.感谢CxImage的作者,我使用此库来避免编码或解码多种图像格式.

但是,在由CxImage加载图像并按标准dib格式转换后,我自己处理它们.ISonicImage用法很简单:

ISonicImage * pImg = GetSonicUI()->CreateImage();
pImg->Load("C:\\demo.png");
pImg->Rotate(90);
pImg->Draw(hdc);

2.制作一个网径

使用某些控件输出彩色,或向窗口添加网径可能很无聊.使用原生GDIAPI,你必须不断地选择不同字体或其他GDI对象进出dc.

但是,使用ISonicString,你只需三到四行就可干活.

ISonicString * pStr = GetSonicUI()->CreateString();
pStr->Format("/a='http://hi.csdn.net/zskof', c=%x/Hello I'm a clik", RGB(0, 0, 255));
pStr->TextOut(hdc, 10, 10);

注意:不要在WM_PAINT过程中创建ISonicString设置其格式,以避免重复初化,及在BeginPaint()EndPaint()间放置pStr->TextOut()方法.

是的,在没有在窗口放置控件,或不关心无聊的分发消息时,仅需三行即可构成一个网径,这似乎是不可能的吗?嗯,这只是子类的把戏.

这样,你可让你的代码超文本代码一样简单.你唯一要参加的是ISonicString的关键字.你可在ISonicUI.h接口文件中找到特定的说明.

3.制作动画自绘按钮

UI人,也很熟悉自定义按钮.使用ISonicString,可轻松创建漂亮的按钮,只与创建网径稍有不同.

void WINAPI OnMove(ISonicString * pStr, LPVOID)
{...
}ISonicImage * pImgNormal = GetSonicUI()->CreateImage();
pImgNormal->Load(BMP_NORMAL);
pImgNormal->SetColorKey(RGB(255, 0, 255));ISonicImage * pImgHover = GetSonicUI()->CreateImage();
pImgHover->Load(BMP_HOVER);
pImgHover->SetColorKey(RGB(255, 0, 255));ISonicImage * pImgClick = GetSonicUI()->CreateImage();
pImgClick->Load(BMP_CLICK);
pImgClick->SetColorKey(RGB(255, 0, 255));ISonicString * pAniButton = GetSonicUI()->CreateString();
pAniButton->Format("/a, p=%d, ph=%d, pc=%d, animation=40/",pImgNormal->GetObjectId(), pImgHover->GetObjectId(), pImgClick->GetObjectId());
pAniButton->Delegate(DELEGATE_EVENT_CLICK, NULL, NULL, OnMove);
pAniButton->TextOut(hdc, 10, 10);

"p,ph,pc"关键字代表按钮的三个状态(正常,移过,点击).每个关键字按其显示项,指定一个ISonicImage.如果你取得一个包含三个状态源图像,则也没有关系.

只需要按ISonicImage的相同对象ID指定"p,ph,pc",ISonicImage会完成所有操作.内部就搞了源剪切.

"animation=40"表示这是一个阴影按钮,即,在切换状态显示动画.40阴影速度,越高,速度越快.Delegate()方法给按回调转发点击事件的过程,然后,如果你点击按钮,则调用该过程.

4.制作不规则窗口

ISonicWndEffect组件用来造不规则窗口,或给窗口加动画,如平滑移动,平滑旋转或平滑拉伸等.制作不规则窗口的方法有两个:使用窗口区域分层窗口.
首先是窗口区域方式:

...
// ISonicImage * pImg
SetWindowRgn(hWnd, pImg->CreateRgn());

第二个是使用分层窗口:

...
ISonicWndEffect * pEffect = GetSonicUI()->CeateWndEffect();
// 用α每像素附加模式
pEffect->Attach(hWnd, TRUE);
// ISonicImage * pImg
pEffect->SetShapeByImage(pImg);

5.其他组件

还有许多其他组件,如ISonicTextScrollBarISonicAnimation,你可用它们实现许多熟悉的UI效果,如滚动文本,平滑移动图片,旋转或平滑拉伸并很爽.

用法非常简单,可在ISonicUI.h接口文件中查找它.在此,这里说下兴趣点.

兴趣点

这里展示一些包含ASMAPI勾挂技术的技巧.

1.闭包

当然,希望找到简单方法来给不同过程转发自绘按钮,以可普遍使用组件.但是函数声明中有问题.VC禁止你按普通方式按参数,传递类的成员函数.

你必须使用与类相关成员函数指针,且显然违反"通用"原则.因此,我使用易失参数来避免该限制.

void ISonicBase::Delegate(UINT message, LPVOID pReserve, LPVOID pClass, ...)
{if(IsValid() == FALSE){return;}ISonicBaseData * pData = dynamic_cast(this);if(pData == NULL){return;}DELEGATE_PARAM pm = {0};pm.pClass = pClass;pm.pReserve = pReserve;va_list argPtr;va_start(argPtr, pClass);pm.pFunc = va_arg(argPtr, LPVOID);va_end(argPtr);pData->m_mapDelegate[message] = pm;
}

而且也无法正常回调.不用担心,只需一点ASM代码即可完成工作.

void ISonicBaseData::OnDelegate(UINT message)
{MSG_TO_DELEGATE_PARAM::iterator it = m_mapDelegate.find(message);if(it == m_mapDelegate.end()){return;}DELEGATE_PARAM &pm = it->second;if(pm.pFunc == NULL || IsBadCodePtr((FARPROC)pm.pFunc)){return;}ISonicBase * pBase = dynamic_cast(this);if(pBase == NULL){return;}LPVOID pReserve = pm.pReserve;LPVOID pClass = pm.pClass;LPVOID pFunc = pm.pFunc;__asm{push ecxpush [pReserve]push [pBase]mov ecx, [pClass]call [pFunc]pop ecx}
}

有时,这破坏了C++语法检查的安全性,因此必须确保回调函数的声明严格遵守以下规则:

void WINPAI Func(ISonicBase *, LPVOID)

否则,会导致栈崩溃某些致命错误.

2.分层窗口

广泛使用分层窗口来实现透明窗口或不规则窗口.有两个用来显示分层窗口API:SetLayeredWindowAttributesUpdateLayeredWindow.

但是,尽管如MSDN所述,SetLayeredWindowAttributes内部使用UpdateLayeredWindow,但对应用开发者来说,这两个函数之间有致命的区别.

在此,只能说主要区别是使用UpdateLayeredWindow时,会丢弃WM_PAINT消息,不会显示你的所有子控件,且通用的GDIAPI可能无法正常工作,而SetLayeredWindowAttributes使用重定向机制来使一切正常运行.

似乎UpdateLayeredWindow只是一个麻烦制造者,但是如果你要创建一个每像素α窗口并按背景使用PNG实现某些阴影效果,则只能选择UpdateLayeredWindow.

因为ISonicWndEffect只是,在现有窗柄上附加的"附件",我该如何要求引擎用户重写BeginPaintEndPaint间的所有渲染代码?因此,我使用了API勾挂技巧.

// ...HMODULE hMod = GetModuleHandle("User32.dll");if(hMod == NULL){return FALSE;}m_pOldBeginPaint = ReplaceFuncAndCopy(GetProcAddress(hMod, "BeginPaint"), MyBeginPaint);m_pOldEndPaint = ReplaceFuncAndCopy(GetProcAddress(hMod, "EndPaint"), MyEndPaint);HDC CSonicUI::MyBeginPaint( HWND hwnd, LPPAINTSTRUCT lpPaint )
{HDC hdc;if(m_hPaintDC){memset(lpPaint, 0, sizeof(PAINTSTRUCT));lpPaint->hdc = m_hPaintDC;GetClientRect(hwnd, &lpPaint->rcPaint);hdc = m_hPaintDC;g_UI.m_rtUpdate = lpPaint->rcPaint;}else{GetUpdateRect(hwnd, g_UI.m_rtUpdate, FALSE);__asm{push [ebp + 0ch]push [ebp + 8h]call [m_pOldBeginPaint]mov [hdc], eax}}g_UI.m_bPainting = TRUE;return hdc;
}BOOL CSonicUI::MyEndPaint( HWND hWnd, CONST PAINTSTRUCT *lpPaint )
{BOOL bRet = TRUE;// ...if(m_hPaintDC){m_hPaintDC = NULL;return TRUE;}else{__asm{push [ebp + 0ch]push [ebp + 8h]call [m_pOldEndPaint]mov [bRet], eax}}GetClientRect(hWnd, g_UI.m_rtUpdate);g_UI.m_bPainting = FALSE;return bRet;
}

这样,当我想使用(内部由UpdateLayeredWindow实现的)每像素α模式重画ISonicWndEffect附加的窗口时,我只是向窗口发送了假WM_PAINT消息,并按WM_PAINT字参使用memdc,无需更改渲染代码正确渲染了.

实际上,使用此技巧带来为一些礼物.使用此引擎时,即使隐藏了窗口,也可在指定memdc绘画所有窗口.

结论

还有许多其他技巧和技术,如按转换运算,更新脏矩形机制,SSE2指令等,以优化引擎效率.

历史

更改了函数勾挂代码,以避免泄漏内存警告.修改了CSonicString::TextOut一些代码,使其可与内存dc一起使用,而无需指定窗柄.给ISonicImage添加了高斯模糊甚至模糊功能.

修复了ISonicImage中的可能导致崩溃服务器错误.给ISonicWndEffect添加DirectTransfrom方法.给ISonicTextScrollBar添加了一些功能.

添加了ISonicSkin组件.可仅用三行代码装饰窗口和对话框.很酷,很方便!添加了统一支持,添加了静态库输出支持.
几种类型从MFC,更改为ATL支持,以减轻引擎重量.优化了一些内核工具,如脏矩形更新机制.
ISonicUIISonicString添加了接口,ISonicString中修改了"p"关键字的格式.

这是一个制作带四种状态平铺图像的自绘按钮示例:

ISonicString::Format("/a, p4=%d/", pImg->GetObjectId());

丢弃原来的"ph""pc"关键字.见ISonicUI.h获取更多细节.


http://www.ppmy.cn/news/1569592.html

相关文章

[MySQL]事务的隔离级别原理与底层实现

目录 1.为什么要有隔离性 2.事务的隔离级别 读未提交 读提交 可重复读 串行化 3.演示事务隔离级别的操作 查看与设置事务的隔离级别 演示读提交操作 演示可重复读操作 1.为什么要有隔离性 在真正的业务场景下,MySQL服务在同一时间一定会有大量的客户端进程…

2025年时序数据库发展方向和前景分析

2025年时序数据库发展方向和前景分析 随着物联网设备激增、实时监控需求上升和数据量爆炸式增长,时序数据库(Time Series Database, TSDB)正成为关键的数据基础设施。据IDC预测,到2025年全球将有416亿联网IoT设备,每年…

前端 Vue 性能提升策略

一、引言 前端性能优化是确保 Web 应用快速响应和流畅用户体验的关键。对于使用 Vue.js 构建的应用,性能优化不仅涉及通用的前端技术,还包括针对 Vue 特性的特定优化措施。本文将从多个方面探讨如何全面提升前端和 Vue 应用的性能。 二、前端性能优化基础 1. 减少初始加载…

国产之光DeepSeek架构理解与应用分析

目录 初步探索DeepSeek的设计 一、核心架构设计 二、核心原理与优化 三、关键创新点 四、典型应用场景 五、与同类模型的对比优势 六、未来演进方向 从投入行业生产的角度看 一、DeepSeek的核心功能扩展 二、机械电子工程产业中的具体案例 1. 预测性维护(Predictive…

Python 与 PostgreSQL 集成:深入 psycopg2 的应用与实践

title: Python 与 PostgreSQL 集成:深入 psycopg2 的应用与实践 date: 2025/2/4 updated: 2025/2/4 author: cmdragon excerpt: PostgreSQL 作为开源关系型数据库的佼佼者,因其强大的功能与性能被广泛应用于各种项目中。而 Python 则因其简洁易用的语法、丰富的库和强大的…

【2025年更新】1000个大数据/人工智能毕设选题推荐

文章目录 前言大数据/人工智能毕设选题:后记 前言 正值毕业季我看到很多同学都在为自己的毕业设计发愁 Maynor在网上搜集了1000个大数据的毕设选题,希望对大家有帮助~ 适合大数据毕业设计的项目,完全可以作为本科生当前较新的毕…

第十八章 视图

目录 一、概述 二、语法 2.1. 创建视图 2.2. 查询视图 2.3. 修改视图 2.4. 删除视图 2.5. 示例 三、检查选项 3.1. CASCADED(级联) 3.2. LOCAL(本地) 四、视图的更新 五、视图作用 5.1. 简单 5.2. 安全 5.3. 数据独…

2.1.3 相机图像信号处理的基本流程

文章目录 ISP基本流程ISP各基本流程职责 ISP基本流程 图像信号处理将传感器采集到的Bayer阵列数据转换成符合人眼观感的图像数据。ISP(Image Signal Processing)图像信号处理基本流程包括坏点校正(DPC, Defect Pixel Correction),黑电平校正&…