Duilib拡張カラーセレクタコントロールを共有します(Duilibの肌交換時に使用します)


概要
[カラーセレクタ](Color Selector)パレットではありません.パレットでは任意のカラー値を選択できますが、カラー選択期間では一定のカラー値しか使用できません.例えば、QQパソコンの執事の肌交換機能では、デフォルトの色をインタフェース全体の背景色として使用することができ、その中の色は固定されており、選択をやり直すしかありません.それが色セレクタです.
インプリメンテーション
データ構造:まず、固定された2 Dテーブルを使用して、行、列の座標に基づいて色のセットを格納する必要があります.ユーザーが色を選択したとき、この点に基づいてテーブルの対応する色の値をクエリーします.
インタフェース処理:選択された状態とマウスのフォーカス状態.現在選択されている色の座標を記録し、変更を選択して色値を復元する必要があります.マウスの移動中にフォーカスブロックのカラー値を描画し、前のカラーブロックの値を復元します.残りはGDI基本功,領域充填,二重バッファ描画,局所リフレッシュである.
コード#コード#
わざわざ注釈をつけて、読者にもっとよく理解してほしい.
/**********************************
*Author:	Jelin
*Time:		2014-12-18
*Email:	mailto://[email protected]
*/
#pragma once
#include <map>
using std::map;


//        
template<class T, int row=0, int column=0>
class CMyArray
{
public:
	CMyArray()
		:m_pData(NULL)
		,m_nRow(row)
		,m_nColumn(column)
	{
		if ( m_nRow>0 && m_nColumn>0 )
			Allocate();
	}
	~CMyArray()
	{
		Free();
	}
	bool Resize(const int nRow, const int nColumn)
	{
		if ( nRow<=0 || nColumn<=0 )
			return false;
		m_nRow		= nRow;
		m_nColumn	= nColumn;
		Allocate();
		return true;
	}
	T GetAt(const int nRow, const int nColumn)
	{
		if ( nRow>=m_nRow || nColumn>=m_nColumn )
			return T();
		return *(m_pData+nRow*m_nColumn+nColumn);
	}
	void SetAt(const int nRow, const int nColumn, const T& data)
	{
		if ( nRow>=m_nRow || nColumn>=m_nColumn )
			return ;
		*(m_pData+nRow*m_nColumn+nColumn) = data;
	}
protected:
	void Allocate()
	{
		Free();
		m_pData = (T*)malloc(m_nRow*m_nColumn*sizeof(T));
	}
	void Free()
	{
		if ( m_pData )
		{
			free(m_pData);
			m_pData = NULL;
		}
	}
private:
	T*	m_pData;
	int	m_nRow;
	int m_nColumn;
};

//            
class CSelectColorCallback
{
public:
	virtual void	ColorTest(DWORD color)=0;
};


class CColorSkinUI
	: public CControlUI
{
public:
	CColorSkinUI();
	virtual ~CColorSkinUI();
	virtual void	SetPos(RECT rc);
	virtual void	DoInit();
	virtual void	SetAttribute(LPCTSTR pstrName, LPCTSTR pstrValue);
	virtual void	DoPaint(HDC hDC, const RECT& rcPaint);
	virtual void	DoEvent(TEventUI& event);
	void	SetSelectColorCallback(CSelectColorCallback* pCallback) { m_pSelectCallback = pCallback; }
	DWORD	GetSelectColor();
	int		GetCurSelLine()					{ return m_nCurSelLine; }
	int		GetCurSelCol()					{ return m_nCurSelCol; }
	void	SetCurSelLine(const int nLine)	{ m_nCurSelLine = nLine; }
	void	SetCurSelCol(const int nColumn) { m_nCurSelCol = nColumn; }
	void	SetCurSelPos(const int nLine, const int nColumn);
protected:
	void	InitBitmap(int nWidth, int nHeight);
	void	PaintPalette(bool bPaintAll=true, const int nCurLine=0, const int nCulCol=0);
	DWORD	PaintSide(HDC hDC, const int nLine, const int nColumn, bool bSelect);
private:
	int		m_nSideLen;
	int		m_nLine;
	int		m_nColumn;
	HDC		m_hMemDC;
	HBITMAP	m_hMemBmp;
	BITMAP	m_bitmap;
	void*	m_pBuffer;
	int		m_nPreWidth;
	int		m_nPreHeight;
	int		m_nCurSelLine;
	int		m_nCurSelCol;
	int		m_nPreCurSelLine;
	int		m_nPreCurSelCol;
	CMyArray<DWORD> m_arrColor;
	CSelectColorCallback*	m_pSelectCallback;
};

#include "StdAfx.h"
#include "UIColorSkin.h"

#define MAX_VALUE			230		//      
#define ORG_VALUE			125		//      
#define ASS_VALUE			100		//     
#define RATE_SEL_VALUE		0.6		//   ,     
#define RATE_OVER_VALUE		0.8		//     ,     

//     ,   
static const DWORD g_Palette[]=
{
	RGB(ORG_VALUE, 0, 0), RGB(ORG_VALUE, 0, ASS_VALUE), RGB(ORG_VALUE, ASS_VALUE, 0), RGB(ORG_VALUE, ASS_VALUE, ASS_VALUE), 	
	RGB(0, ORG_VALUE, 0), RGB(0, ORG_VALUE, ASS_VALUE), RGB(ASS_VALUE, ORG_VALUE, 0), RGB(ASS_VALUE, ORG_VALUE, ASS_VALUE), 
	RGB(0, 0, ORG_VALUE), RGB(0, ASS_VALUE, ORG_VALUE), RGB(ASS_VALUE, 0, ORG_VALUE), RGB(ASS_VALUE, ASS_VALUE, ORG_VALUE), 
	RGB(ORG_VALUE, ORG_VALUE, 0), RGB(ORG_VALUE, ORG_VALUE, ASS_VALUE), 
	RGB(ORG_VALUE, 0, ORG_VALUE), RGB(ORG_VALUE, ASS_VALUE, ORG_VALUE), 
	RGB(ASS_VALUE, ORG_VALUE, ORG_VALUE), RGB(0, ORG_VALUE, ORG_VALUE),
	RGB(ORG_VALUE, ORG_VALUE, ORG_VALUE),
};
const int g_nMaxColumn = sizeof(g_Palette)/sizeof(DWORD);

CColorSkinUI::CColorSkinUI()
:m_nSideLen(0)
,m_nLine(0)
,m_nColumn(0)
,m_hMemDC(NULL)
,m_hMemBmp(NULL)
,m_pBuffer(NULL)
,m_nPreWidth(-1)
,m_nPreHeight(-1)
,m_nCurSelLine(0)
,m_nCurSelCol(0)
,m_nPreCurSelLine(-1)
,m_nPreCurSelCol(-1)
,m_pSelectCallback(NULL)
{
	ZeroMemory(&m_bitmap, sizeof(BITMAP));
}

CColorSkinUI::~CColorSkinUI()
{

	if ( m_hMemDC )
	{
		DeleteDC(m_hMemDC);
		m_hMemDC = NULL;
	}
	if ( m_hMemBmp )
	{
		DeleteObject(m_hMemBmp);
		m_hMemBmp = NULL;
	}
	if ( m_pBuffer )
	{
		free(m_pBuffer);
		m_pBuffer = NULL;
	}
}

void CColorSkinUI::SetPos( RECT rc )
{
	CControlUI::SetPos(rc);
	int nWidth	= GetWidth();
	int nHeight = GetHeight();
	m_nColumn	= (nWidth+1)/(m_nSideLen+1);
	if ( m_nColumn>g_nMaxColumn )
	{
		m_nColumn = g_nMaxColumn;
		m_nSideLen = (nWidth+1)/m_nColumn-1;
	}
	m_nLine		= (nHeight+1)/(m_nSideLen+1);
	m_arrColor.Resize(m_nLine, m_nColumn);
	InitBitmap(nWidth, nHeight);
}

void CColorSkinUI::DoInit()
{
	CControlUI::DoInit();
}

void CColorSkinUI::SetAttribute( LPCTSTR pstrName, LPCTSTR pstrValue )
{
	if ( _tcscmp(pstrName, _T("sidelen")) == 0 )
		m_nSideLen = _ttoi(pstrValue);
	else
		CControlUI::SetAttribute(pstrName, pstrValue);
}

void CColorSkinUI::DoPaint( HDC hDC, const RECT& rcPaint )
{
	if ( NULL == m_hMemDC )
		m_hMemDC = CreateCompatibleDC(hDC);
	RECT rcInsert;
	IntersectRect(&rcInsert, &m_rcItem, &rcPaint);
	HBITMAP hOldBmp = (HBITMAP)SelectObject(m_hMemDC, m_hMemBmp);
	::BitBlt(hDC, rcInsert.left, rcInsert.top, rcInsert.right-rcInsert.left, rcInsert.bottom-rcInsert.top, \
		m_hMemDC, rcInsert.left-m_rcItem.left, rcInsert.top-m_rcItem.top, SRCCOPY);
	m_hMemBmp = (HBITMAP)::SelectObject(m_hMemDC, hOldBmp);
}

void CColorSkinUI::InitBitmap(int nWidth, int nHeight)
{
	m_bitmap.bmWidth	=nWidth;
	m_bitmap.bmHeight	=nHeight;
	m_bitmap.bmBitsPixel=32;
	m_bitmap.bmPlanes	=1;
	m_bitmap.bmWidthBytes=nWidth*4;
	m_bitmap.bmType		=0;
	int nBytes = (((nWidth*32+31)>>5)<<2)*nHeight;
	if ( nWidth != m_nPreWidth || nHeight != m_nPreHeight )
	{
		if ( m_pBuffer )
			free(m_pBuffer);
		m_pBuffer	=malloc(nBytes);
		m_nPreWidth	=nWidth;
		m_nPreHeight=nHeight;
	}
	memset(m_pBuffer, 0xff, nBytes);
	m_bitmap.bmBits		=m_pBuffer;
	PaintPalette();
}

void CColorSkinUI::DoEvent( TEventUI& event )
{
	switch( event.Type )
	{
	case UIEVENT_MOUSEMOVE:
		{//                  
			int xPos = (event.ptMouse.x-m_rcItem.left)/(m_nSideLen+1);
			int yPos = (event.ptMouse.y-m_rcItem.top)/(m_nSideLen+1);
			PaintPalette(false, yPos, xPos);//    ,      
			Invalidate();
			break;
		}
	case UIEVENT_BUTTONDOWN:
		{
			int xPos = (event.ptMouse.x-m_rcItem.left)/(m_nSideLen+1);
			int yPos = (event.ptMouse.y-m_rcItem.top)/(m_nSideLen+1);
			if ( yPos>=m_nLine || xPos>=m_nColumn )
				break;
			m_nCurSelCol  = xPos;
			m_nCurSelLine = yPos;
			//        
			PaintPalette(true);
			if ( m_pSelectCallback )
			{//     ,             (        ……)
				DWORD dwColor = m_arrColor.GetAt(m_nCurSelLine, m_nCurSelCol);
				dwColor = 0xff000000 + RGB( GetBValue(dwColor), GetGValue(dwColor), GetRValue(dwColor) );
				m_pSelectCallback->ColorTest(dwColor);
			}
			Invalidate();
		}
		break;
	case UIEVENT_MOUSEHOVER:
		break;
	case UIEVENT_MOUSELEAVE:
		//                 
		PaintPalette(false, m_nCurSelLine, m_nCurSelCol);
		Invalidate();
		break;
	default:
		break;
	}
	CControlUI::DoEvent(event);
}

void CColorSkinUI::PaintPalette(bool bPaintAll/*=true*/, const int nCurLine/*=0*/, const int nCulCol/*=0*/)
{//                   ,                     
	if ( nCurLine == m_nPreCurSelLine && nCulCol == m_nPreCurSelCol )
		return ;//       ,    
	if ( nCurLine>=m_nLine || nCulCol>=m_nColumn )
		return ;//            ,    
	if ( bPaintAll )
	{
		if ( m_hMemBmp )
		{
			DeleteObject(m_hMemBmp);
			m_hMemBmp = NULL;
		}
		m_hMemBmp = CreateBitmapIndirect(&m_bitmap);
	}
	//   m_hMemBmp    
	HDC hDC = CreateCompatibleDC(NULL);
	HBITMAP hBmp = (HBITMAP)SelectObject(hDC, m_hMemBmp);
	if ( bPaintAll )
	{
		for ( int i=0; i<m_nLine; ++i )
			for ( int j=0; j<m_nColumn; ++j )
			{
				DWORD dwColor = PaintSide(hDC, i, j, (i == m_nCurSelLine && j == m_nCurSelCol));
				m_arrColor.SetAt(i, j, dwColor);
			}
	}
	else
	{
		if ( m_nPreCurSelCol!=-1 && m_nPreCurSelLine!=-1 )
		{//        
			PaintSide(hDC, m_nPreCurSelLine, m_nPreCurSelCol, (m_nPreCurSelLine == m_nCurSelLine && m_nPreCurSelCol == m_nCurSelCol));
		}
		//         
		PaintSide(hDC, nCurLine, nCulCol, true);
		m_nPreCurSelLine= nCurLine;
		m_nPreCurSelCol	= nCulCol;
	}
	m_hMemBmp = (HBITMAP)SelectObject(hDC, hBmp);
	DeleteDC(hDC);
}

DWORD CColorSkinUI::PaintSide( HDC hDC, const int nLine, const int nColumn, bool bSelect )
{//        
	DWORD dwRetColor = 0;
	RECT rcFill = {0, 0, 0, 0};
	DWORD dwColor = 0, dwPreColor = 0;
	int r = 0, g = 0, b = 0;
	float fRate = (MAX_VALUE-ORG_VALUE)/(float)(m_nLine-1);
	rcFill.left	= (m_nSideLen+1)*nColumn;
	rcFill.top	= (m_nSideLen+1)*nLine;
	rcFill.right= rcFill.left+m_nSideLen;
	rcFill.bottom=rcFill.top+m_nSideLen;
	if ( nLine == 0 )
		dwColor = g_Palette[nColumn];//            
	else
	{//               
		r = GetRValue(g_Palette[nColumn]);
		g = GetGValue(g_Palette[nColumn]);
		b = GetBValue(g_Palette[nColumn]);
		if ( r>=ORG_VALUE ) r+=(int)(nLine*fRate);
		if ( g>=ORG_VALUE ) g+=(int)(nLine*fRate);
		if ( b>=ORG_VALUE ) b+=(int)(nLine*fRate);
		dwColor = RGB(r, g, b);
	}
	dwRetColor = dwColor;
	if ( bSelect )
	{//     ,        0.6 ,           
		dwColor = RGB(GetRValue(dwColor)*RATE_SEL_VALUE, GetGValue(dwColor)*RATE_SEL_VALUE, \
			GetBValue(dwColor)*RATE_SEL_VALUE );
	}
	HBRUSH hBrush = CreateSolidBrush(dwColor);
	FillRect(hDC, &rcFill, hBrush);
	DeleteObject(hBrush);
	return dwRetColor;
}

DWORD CColorSkinUI::GetSelectColor()
{
	DWORD dwColor = m_arrColor.GetAt(m_nCurSelLine, m_nCurSelCol);
	//               RGB  
	dwColor = 0xff000000 + RGB( GetBValue(dwColor), GetGValue(dwColor), GetRValue(dwColor) );
	return dwColor;
}

void CColorSkinUI::SetCurSelPos( const int nLine, const int nColumn )
{
	m_nCurSelLine	= nLine;
	m_nCurSelCol	= nColumn;
}

コンフィギュレーション
リロードコントロール関数の作成、新規追加コントロール処理
CControlUI* CSkinWnd::CreateControl( LPCTSTR pstrClass )
{
	if ( _tcscmp(pstrClass, _T("ColorSkin")) == 0 )
	{	
		return new CColorSkinUI;
	}
	return NULL;
}

XMLレイアウトの設定
<HorizontalLayout  bkcolor="#FFFFFFFF">
			<ColorSkin name="color" float="true" pos="9,6,0,0" sidelen="16" width="310" height="120" bkcolor="#FFFFFFFF"/>
			<Button name="btn_ok" text="  " float="true" pos="220,132,0,0"  width="76" height="26" textcolor="#FFF9F9F9" align="center" normalimage="blue_nor.png" hotimage="blue_hot.png" pushedimage="blue_down.png" hottextcolor="#FFFFFFFF" font="3"/>
        </HorizontalLayout>

うんてん