minigui/mgncs:自定义渲染器(renderer)实现透明背景按钮(transparent button)

一般来说,对于mStatic,mImage这样的控件,只要设置了透明属性(transparent=true),就可以实现背景透明,但对于mButton按钮却不行,即使设置了透明属性,也不能实现透明背景。

miniStudio中对按钮设置透明属性示例:
minigui/mgncs:自定义渲染器(renderer)实现透明背景按钮(transparent button)_第1张图片
这是为什么呢?通过跟踪minigui/mgncs的代码发现了原因:
以下是libmgncs-1.2.0/src/renderer/flat/flat_boxpieces.c中,按钮类(mButtonBoxPiece)渲染器的paint处理函数(flat风格)的代码:

// button box
static void flat_buttonbox_paint(mButtonBoxPiece *self, HDC hdc, mWidget * owner, DWORD add_data)
{
    RECT rc;

    if(!_c(self)->getRect(self, &rc)) return ; flat_draw_3dbox(hdc, &rc, NCSRF_FILL, add_data&NCS_PIECE_PAINT_STATE_MASK, NCS_PIECE_PAINT_GET_CHECK(add_data), owner); }

从这个代码可以看出,每次重绘窗口区域时,不论是否有设置透明属性,都会先调用flat_draw_3dbox函数填充背景。
其他风格的渲染器处理mButtonBoxPiece的逻辑也是一样的(classic,fashion,skin,flat)
minigui/mgncs:自定义渲染器(renderer)实现透明背景按钮(transparent button)_第2张图片

知道了原因,解决问题的方案就有了

解决方案1

修改libmgncs源码:
还以上面的flat_buttonbox_paint函数为例,在函数开始添加一行代码判断透明属性是否设置就可以了。

////////////////////////////////////////////
// button box
static void flat_buttonbox_paint(mButtonBoxPiece *self, HDC hdc, mWidget * owner, DWORD add_data)
{
    /* 如果有透明直接返回 */
    if(GetWindowExStyle(owner->hwnd) & WS_EX_TRANSPARENT)return;
    RECT rc;

    if(!_c(self)->getRect(self, &rc))
        return ;

    flat_draw_3dbox(hdc, &rc, NCSRF_FILL, add_data&NCS_PIECE_PAINT_STATE_MASK,
            NCS_PIECE_PAINT_GET_CHECK(add_data), owner);
}

上面这样修改带来的效果就是按钮边框也没有了。如果你要背景透明但还希望画上按钮边框,修改如下:

static void flat_buttonbox_paint(mButtonBoxPiece *self, HDC hdc, mWidget * owner, DWORD add_data)
{
    // 根据透明背景属性来决定flat_draw_3dbox函数的flag参数
    int flag = GetWindowExStyle(owner->hwnd) & WS_EX_TRANSPARENT ? 0 : NCSRF_FILL;
    RECT rc;

    if(!_c(self)->getRect(self, &rc))
        return ;

    flat_draw_3dbox(hdc, &rc, flag, add_data&NCS_PIECE_PAINT_STATE_MASK,
            NCS_PIECE_PAINT_GET_CHECK(add_data), owner);
}

解决方案2

如果你不想修改源码,可以在外部自己为mButtonBoxPiece写个渲染器函数,然后调用ncsRegisterCtrlRDRs替换掉flat_buttonbox_paint函数就可以了:

#include 
#include 
#include 
#include 
#include 

#include 
// 自定义的mButtonBoxPiece 渲染器函数,do nothing
static void transparent_buttonbox_paint(mButtonBoxPiece *self, HDC hdc, mWidget * owner, DWORD add_data)
{
}

//init boxpiece
void transparent_init_boxpiece_renderer(void)
{
    NCS_RDR_ENTRY entries [] = {
        {Class(mButtonBoxPiece).typeName, (mWidgetRenderer*)(void*)transparent_buttonbox_paint},
    };
    // 注册控件的渲染器函数,修改flat风格的button渲染器函数,
    // 可以用这个函数同时替换其他风格(`classic`,`fashion`,`skin`,`flat`)的对应函数
    ncsRegisterCtrlRDRs("flat",
        entries,
        sizeof(entries)/sizeof(NCS_RDR_ENTRY));
    ncsRegisterCtrlRDRs("classic",
        entries,
        sizeof(entries)/sizeof(NCS_RDR_ENTRY));
    ncsRegisterCtrlRDRs("fashion",
        entries,
        sizeof(entries)/sizeof(NCS_RDR_ENTRY));
    ncsRegisterCtrlRDRs("skin",
        entries,
        sizeof(entries)/sizeof(NCS_RDR_ENTRY));
}

注意要在应用程序启动前调用transparent_init_boxpiece_renderer函数完成transparent_buttonbox_paint的注册。
同样,如果你希望背景透明的同时保留按钮边框。上面transparent_buttonbox_paint要这样写:

// 声明外部调用函数
void flat_draw_3dbox(HDC hdc, const RECT *rc, int flag ,int state, int check_state, mWidget *owner);

static void transparent_buttonbox_paint(mButtonBoxPiece *self, HDC hdc, mWidget * owner, DWORD add_data)
{
    int flag = GetWindowExStyle(owner->hwnd) & WS_EX_TRANSPARENT ? 0 : NCSRF_FILL;
    RECT rc;

    if(!_c(self)->getRect(self, &rc))
        return ;

    flat_draw_3dbox(hdc, &rc, flag, add_data&NCS_PIECE_PAINT_STATE_MASK,
            NCS_PIECE_PAINT_GET_CHECK(add_data), owner);
}

有边框的背景透明按钮和无边框的透明背景按钮的显示效果对比:
minigui/mgncs:自定义渲染器(renderer)实现透明背景按钮(transparent button)_第3张图片minigui/mgncs:自定义渲染器(renderer)实现透明背景按钮(transparent button)_第4张图片

注意:修改渲染器(renderer)对所有同类型的控件都有效

你可能感兴趣的:(embedded,minigui,MiniGUI)