1. 程式人生 > >minigui/mgncs:自定義渲染器(renderer)實現透明背景按鈕(transparent button)

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)對所有同類型的控制元件都有效