minigui/mgncs:自定義渲染器(renderer)實現透明背景按鈕(transparent button)

一般來說,對於mStatic,mImage這樣的控件,只要設置了透明屬性(transparent=true),就可以實現背景透明,但對於mButton按鈕卻不行,即使設置了透明屬性,也不能實現透明背景。

miniStudio中對按鈕設置透明屬性示例:
這裏寫圖片描述
這是爲什麼呢?通過跟蹤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)
這裏寫圖片描述

知道了原因,解決問題的方案就有了

解決方案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 <minigui/common.h>
#include <minigui/minigui.h>
#include <minigui/gdi.h>
#include <minigui/window.h>
#include <minigui/fixedmath.h>

#include <mgncs/mgncs.h>
// 自定義的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);
}

有邊框的背景透明按鈕和無邊框的透明背景按鈕的顯示效果對比:
這裏寫圖片描述這裏寫圖片描述

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章