当前位置: 首页 > article >正文

2502,索界面3

原文

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 ecx
        push [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.kler.cn/a/532424.html

相关文章:

  • 2. 【.NET Aspire 从入门到实战】--理论入门与环境搭建--.NET Aspire 概览
  • mysql 学习8 函数,字符串函数,数值函数,日期函数,流程函数
  • 【PDF提取局部内容改名】批量获取PDF局部文字内容改名 基于QT和百度云api的完整实现方案
  • 排序算法--桶排序
  • Error: Expected a mutable image
  • 知识库建设与知识管理实践对企业发展的助推作用探索
  • 第十八章 视图
  • wordpress安装
  • 【Git】一、初识Git Git基本操作详解
  • 阿里云 ROS 与 Terraform:它们的差异与如何选择适合的自动化工具?
  • llama.cpp的C语言API使用
  • Linux环境下的Java项目部署技巧:安装 Nginx
  • 复现论文“去模糊算法”
  • Python分享10个Excel自动化脚本
  • ubuntu ip设置
  • 电路研究9.2.2.1——合宙Air780EP中分组域相关命令分析
  • 仿真设计|基于51单片机的分贝检测与远程传输系统仿真
  • 回溯法-排列,组合
  • llama.cpp GGML Quantization Type
  • 5.角色基础移动
  • 云夹:重新定义你的书签管理体验
  • [mmdetection]fast-rcnn模型训练自己的数据集的详细教程
  • concurrentHasMap为什么不允许kv为null
  • 使用线性回归模型逼近目标模型 | PyTorch 深度学习实战
  • Go方法接收者中值类型接收者和指针类型接收者的对比
  • STM32F103ZET6完整技术点(持续更新~)