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