图片浏览器开发日志-09(简单flat 风格按钮实现)

本文参考了CButtonST代码实现。 CButtonST 的功能相当丰富,比较复杂,支持声音等等。本文对其进行了大量简化,完成了一种效果还算不错的flat风格按钮。

按钮风格设计

按钮的简单外界影响,无非是 鼠标离开,鼠标在按钮上和点击等单个状态(其他状态暂不考虑)。三个状态,一般的按钮设计要对应三种图标或者图片,本文利用图像处理的简单技巧,每个按钮只输入一个图片就可以了。其余两种状态的图片是系统计算出来的,这样做的好处是代码简洁,图片文件也只有原来的三分之一。对应界面设计的外行来说,这样省去很多麻烦。
笔者设计了一个CSingleFlatButton类,实现了一个简单的flatbutton。
效果如图

鼠标没有经过按钮是的按钮颜色,是蓝色,如图示。
在这里插入图片描述
鼠标移到按钮上方是,按钮颜色发生变化。这个变化是程序通过改变贴图实现的,而贴图是自动计算出来的。
在这里插入图片描述
鼠标点击时的效果,按钮图片是一个简单的正透视效果,实际效果有被推倒的感觉。
在这里插入图片描述
下面是代码

#include "stdafx.h"
#include "CSingleFlatButton.h"

CSingleFlatButton::CSingleFlatButton()
{
    
    

	m_bMouseOnButton = false;
	m_bDrawBorder = true;
	m_bIsFlat = true;
	m_bIsPressed = false;
}

CSingleFlatButton::~CSingleFlatButton()
{
    
    

}

BEGIN_MESSAGE_MAP(CSingleFlatButton, CButton)
	ON_WM_MOUSEMOVE()
	ON_MESSAGE(WM_MOUSELEAVE, OnMouseLeave)
END_MESSAGE_MAP()
void CSingleFlatButton::DrawItem(LPDRAWITEMSTRUCT lpDIS)
{
    
    
	CDC*	pDC = CDC::FromHandle(lpDIS->hDC);
	CRect itemRect = lpDIS->rcItem;
	m_bIsPressed = (lpDIS->itemState & ODS_SELECTED);
	// Draw button border
	OnDrawBorder(pDC, &itemRect);
	int x, y;
	if (m_bIsPressed) {
    
    
		
		COLORREF clr = RGB(128, 128, 128);
		//clr = CTLCOLOR_STATIC;
		clr = pDC->GetBkColor();
		CBrush *brush = new CBrush(clr);
		//GetDlgItem(IDC_STATIC)->GetDC()->FillRect(rect, brush);
		pDC->FillRect(itemRect, brush);
		//imgCtrl->ReleaseDC(imgCtrl->GetDC());
		delete brush;


		CDC *cdc = GetDC();
		m_pressImg.Draw(cdc->m_hDC,
			itemRect.left,
			itemRect.top,
			itemRect.Width(),
			itemRect.Height());
		ReleaseDC(cdc);

		//DrawState does not support alpha channel ...
		/*pDC->DrawState(CPoint(x, y), CSize(m_mouseOnImg.GetWidth(), m_mouseOnImg.GetHeight()),
			m_mouseOnImg, DST_BITMAP);*/
	}else if (m_bMouseOnButton) 
	{
    
    
		x = itemRect.left +1;
		y = itemRect.top +1;

		pDC->DrawState(CPoint(x, y), CSize(m_mouseOnImg.GetWidth(), m_mouseOnImg.GetHeight()),
			m_mouseOnImg, DST_BITMAP );
	}
	else {
    
    
		x = itemRect.left/*+1*/;
		y = itemRect.top;

		pDC->DrawState(CPoint(x,y), CSize(m_mouseLeaveImg.GetWidth(), m_mouseLeaveImg.GetHeight()),
			m_mouseLeaveImg, DST_BITMAP );

	}

}

BOOL CSingleFlatButton::PreTranslateMessage(MSG * pMsg)
{
    
    
	return CButton::PreTranslateMessage(pMsg);
}

void CSingleFlatButton::setImg(CImage img)
{
    
    
	//normalImg = img;
	perspectiveImg(img, img.GetHeight(), img.GetWidth(), L"D", m_mouseLeaveImg);

	createRelatedImg();
}
void CSingleFlatButton::setImg(CString imgFile)
{
    
    
	if (!m_mouseLeaveImg.IsNull()) {
    
    
		m_mouseLeaveImg.Destroy();
	}
	m_mouseLeaveImg.Load(imgFile);
	createRelatedImg();
}
void CSingleFlatButton::PreSubclassWindow()
{
    
    
	ModifyStyle(0, BS_OWNERDRAW);
	CButton::PreSubclassWindow();
}

void CSingleFlatButton::createRelatedImg()
{
    
    
	int tH, tTopL;
	tH = m_mouseLeaveImg.GetHeight()*0.8;
	tTopL = m_mouseLeaveImg.GetWidth()*0.8;
	perspectiveImg(m_mouseLeaveImg, tH, tTopL, L"D", m_pressImg);
	m_myImg.FireStyle(m_mouseLeaveImg, m_mouseOnImg);

}
// the dst image is the as the same size as the original one.
void CSingleFlatButton::perspectiveImg(CImage &src, int tH, int tTopL, CString dir, CImage &dst) {
    
    
	int src_width = src.GetWidth();
	int src_height = src.GetHeight();
	CImage img1;
	float dstX, dstY, dstW, dstH, srcX, srcY, srcW, srcH;
	int tBotL;

	if (dir == "U" || dir == "D") {
    
    
		dstH = tH;
		dstW = src_width;
	}
	else {
    
    
		dstH = src_height;
		dstW = tH;
	}
	img1.Create(dstW, dstH, 24);
	CImage dst0;
	if (!dst0.IsNull()) {
    
    
		dst0.Destroy();
	}
	dst0.Create(src.GetWidth(), src.GetHeight(), 24);
	CDC* pSrc0DC = CDC::FromHandle(src.GetDC());
	CDC* pSrc1DC = CDC::FromHandle(img1.GetDC());


	CDC* pDstDC = CDC::FromHandle(dst0.GetDC());


	int ModeOld = SetStretchBltMode(pSrc1DC->m_hDC, COLORONCOLOR);//设置指定设备环境中的位图拉伸模式, looks like STRETCH_HALFTONE mode,  0.16 ms or 0.00 ms

	//1# get imag1 
	pSrc1DC->StretchBlt(0, 0, dstW, dstH, pSrc0DC, 0, 0, src_width, src_height, SRCCOPY);
	SetStretchBltMode(pSrc1DC->m_hDC, ModeOld);

	ModeOld = SetStretchBltMode(pDstDC->m_hDC, COLORONCOLOR);//设置指定设备环境中的位图拉伸模式, looks like STRETCH_HALFTONE mode,  0.16 ms or 0.00 ms


	if (dir == "U" || dir == "D") {
    
    
		srcH = 1;
		dstH = 1;
		srcX = 0;
		srcW = src_width;
		tBotL = src_width;
	}
	else {
    
    
		srcW = 1;
		dstW = 1;
		srcY = 0;
		srcH = src_height;
		tBotL = src_height;
	}


	int dL;// the difference of the two parallel edge of the trapezoid
	int L;


	for (int h = 0; h < tH; h++)
	{
    
    


		if (dir == "U") {
    
    
			srcY = h ;
			dstY = srcY;
			dL = (tBotL - tTopL) / 2.0 / tH * h;
			L = tBotL - dL * 2;
			dstW = L;
			dstX = dL;

		}
		else if (dir == "D") {
    
    
			srcY = tH - h;
			dstY = srcY + src_height -tH;
			dL = (tBotL - tTopL) / 2.0 / tH * h;
			L = tBotL - dL * 2;
			dstW = L;
			dstX = dL;

		}
		else if (dir == "L") {
    
    
			srcX = h;
			dstX = srcX;
			dL = (tBotL - tTopL) / 2.0 / tH * h;
			L = tBotL - dL * 2;
			dstH = L;
			dstY = dL;

		}
		else if (dir == "R") {
    
    
			srcX = tH - h;
			dstX = srcX + src_width-tH;
			dL = (tBotL - tTopL) / 2.0 / tH * h;
			L = tBotL - dL * 2;
			dstH = L;
			dstY = dL;
		}
		else {
    
    
			throw "Bad parameters...";
		}


		pDstDC->StretchBlt(dstX, dstY, dstW, dstH, pSrc1DC, srcX, srcY, srcW, srcH, SRCCOPY);
	}
	SetStretchBltMode(pDstDC->m_hDC, ModeOld);
	src.ReleaseDC();
	img1.ReleaseDC();
	dst0.ReleaseDC();
	GetTransparentIMG(RGB(255, 255, 255), 0, dst0, dst);
	return;
}
// parameters:
// transClr: the color need to be transparent 
// alpha: 0-255 the value for alphas channel , 0 is fully transparent and 255 is opaque 
// srcImg: 16bit above image
// dstImg: the output image with alpha channel
void CSingleFlatButton::GetTransparentIMG(COLORREF transClr, int alpha, CImage srcImg, CImage & dstImg)
{
    
    
	//imgSrc = (CImage)this;
	int maxY = srcImg.GetHeight();
	int maxX = srcImg.GetWidth();

	if (!dstImg.IsNull())
	{
    
    
		dstImg.Destroy();
	}
	dstImg.Create(maxX, maxY, 32, CImage::createAlphaChannel);//图像大小与srcImg相同,每个像素占1字节

	if (dstImg.IsNull())
		return;

	byte* pDataSrc = (byte*)srcImg.GetBits();//获取指向图像数据的指针
	byte* pDataDst = (byte*)dstImg.GetBits();
	int pitchSrc = srcImg.GetPitch();		 //获取每行图像占用的字节数 +:top-down;-:bottom-up DIB
	int pitchDst = dstImg.GetPitch();
	int bitCountSrc = srcImg.GetBPP() / 8;   // 获取每个像素占用的字节数
	int bitCountDst = dstImg.GetBPP() / 8;
	int srcR, srcG, srcB, avg;
	for (int i = 0; i < maxX; i++)
	{
    
    
		for (int j = 0; j < maxY; j++)
		{
    
    
			srcB = *(pDataSrc + pitchSrc * j + i * bitCountSrc);
			srcG = *(pDataSrc + pitchSrc * j + i * bitCountSrc + 1);
			srcR = *(pDataSrc + pitchSrc * j + i * bitCountSrc + 2);
			avg = (int)(srcR + srcG + srcB) / 3;

			long bkColor = srcR * srcG*srcB;


			if (transClr == RGB(srcR, srcG, srcB))
			{
    
    
				*(pDataDst + pitchDst * j + i * bitCountDst + 3) = alpha;// transparent 
			}
			else {
    
    
				*(pDataDst + pitchDst * j + i * bitCountDst + 3) = 255;// opaque 
				*(pDataDst + pitchDst * j + i * bitCountDst) = srcB;
				*(pDataDst + pitchDst * j + i * bitCountDst + 1) = srcG;
				*(pDataDst + pitchDst * j + i * bitCountDst + 2) = srcR;
			}
		}
	}
	return;
}

void CSingleFlatButton::OnMouseMove(UINT nFlags, CPoint point)
{
    
    
	CWnd*				wndUnderMouse = NULL;
	CWnd*				wndActive = this;
	TRACKMOUSEEVENT		csTME;

	CButton::OnMouseMove(nFlags, point);

	ClientToScreen(&point);
	wndUnderMouse = WindowFromPoint(point);

	// If the mouse enter the button with the left button pressed then do nothing
	//if (nFlags & MK_LBUTTON && m_bMouseOnButton == FALSE) return;

	// If our button is not flat then do nothing
	//if (m_bIsFlat == FALSE) return;

	//if (m_bAlwaysTrack == FALSE)	wndActive = GetActiveWindow();

	if (wndUnderMouse && wndUnderMouse->m_hWnd == m_hWnd && wndActive)
	{
    
    
		if (!m_bMouseOnButton)
		{
    
    
			m_bMouseOnButton = TRUE;

			Invalidate();
			csTME.cbSize = sizeof(csTME);
			csTME.dwFlags = TME_LEAVE;
			csTME.hwndTrack = m_hWnd;
			::_TrackMouseEvent(&csTME);
		} // if
	}
	else CancelHover();

}
LRESULT CSingleFlatButton::OnMouseLeave(WPARAM wParam, LPARAM lParam)
{
    
    
	CancelHover();
	return 0;
} // End of OnMouseLeave

void CSingleFlatButton::CancelHover()
{
    
    
	// Only for flat buttons
	if (m_bIsFlat)
	{
    
    
		if (m_bMouseOnButton)
		{
    
    
			m_bMouseOnButton = FALSE;
			Invalidate();
		} // if
	} // if
} // End 
bool CSingleFlatButton::OnDrawBorder(CDC* pDC, CRect* pRect)
{
    
    
	// Draw pressed button
	if (m_bIsPressed)
	{
    
    
		if (m_bIsFlat)
		{
    
    
			if (m_bDrawBorder)
				pDC->Draw3dRect(pRect, ::GetSysColor(COLOR_BTNSHADOW), ::GetSysColor(COLOR_BTNHILIGHT));
		}
		else
		{
    
    
			CBrush brBtnShadow(GetSysColor(COLOR_BTNSHADOW));
			pDC->FrameRect(pRect, &brBtnShadow);
		}
	}
	else // ...else draw non pressed button
	{
    
    
		CPen penBtnHiLight(PS_SOLID, 0, GetSysColor(COLOR_BTNHILIGHT)); // White
		CPen pen3DLight(PS_SOLID, 0, GetSysColor(COLOR_3DLIGHT));       // Light gray
		CPen penBtnShadow(PS_SOLID, 0, GetSysColor(COLOR_BTNSHADOW));   // Dark gray
		CPen pen3DDKShadow(PS_SOLID, 0, GetSysColor(COLOR_3DDKSHADOW)); // Black

		if (m_bIsFlat)
		{
    
    
			if (m_bMouseOnButton && m_bDrawBorder)
				pDC->Draw3dRect(pRect, ::GetSysColor(COLOR_BTNHILIGHT), ::GetSysColor(COLOR_BTNSHADOW));
		}
		else
		{
    
    
			// Draw top-left borders
			// White line
			CPen* pOldPen = pDC->SelectObject(&penBtnHiLight);
			pDC->MoveTo(pRect->left, pRect->bottom - 1);
			pDC->LineTo(pRect->left, pRect->top);
			pDC->LineTo(pRect->right, pRect->top);
			// Light gray line
			pDC->SelectObject(pen3DLight);
			pDC->MoveTo(pRect->left + 1, pRect->bottom - 1);
			pDC->LineTo(pRect->left + 1, pRect->top + 1);
			pDC->LineTo(pRect->right, pRect->top + 1);
			// Draw bottom-right borders
			// Black line
			pDC->SelectObject(pen3DDKShadow);
			pDC->MoveTo(pRect->left, pRect->bottom - 1);
			pDC->LineTo(pRect->right - 1, pRect->bottom - 1);
			pDC->LineTo(pRect->right - 1, pRect->top - 1);
			// Dark gray line
			pDC->SelectObject(penBtnShadow);
			pDC->MoveTo(pRect->left + 1, pRect->bottom - 2);
			pDC->LineTo(pRect->right - 2, pRect->bottom - 2);
			pDC->LineTo(pRect->right - 2, pRect->top);
			//
			pDC->SelectObject(pOldPen);
		} // else
	} // else

	//return BTNST_OK;
	return true;
} // End of OnDrawBorder

下面是H文件

#pragma once
#include <afxwin.h>
#include "CMyImage.h"
class CSingleFlatButton :
	public CButton
{
    
    
public:
	CSingleFlatButton();
	~CSingleFlatButton();
public:
	virtual void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct);
	virtual BOOL PreTranslateMessage(MSG* pMsg);
	void setImg(CImage img);
	void setImg(CString imgFile);
protected:
	virtual void PreSubclassWindow();
	
	
private:
	CImage m_mouseLeaveImg;//need to be set by the user
	CImage m_mouseOnImg;// generated from the normaImg
	CImage m_pressImg;//generated from the normalImg
	bool m_bMouseOnButton;
	bool m_bIsFlat;
	bool m_bDrawBorder;
	bool m_bIsPressed;
	CMyImage m_myImg;//used to process image 
private:
	void createRelatedImg();
	void perspectiveImg(CImage &src, int tH, int tTopL, CString dir, CImage &dst);
	void GetTransparentIMG(COLORREF transClr, int alpha,CImage srcImg, CImage & dstImg);
protected:
	afx_msg void OnMouseMove(UINT nFlags, CPoint point);
	LRESULT OnMouseLeave(WPARAM wParam, LPARAM lParam);
	void CancelHover();
	bool OnDrawBorder(CDC* pDC, CRect* pRect);
	DECLARE_MESSAGE_MAP()
};

使用方法:
定义控件变量

CSingleFlatButton m_btnBrowse;
CSingleFlatButton m_btnPrev;

在OnInitDialog()中添加如下代码
// m_btnImgPath 存放图片所在文件夹
CString filePath;
filePath = m_btnImgPath + L"browse.png";
m_btnBrowse.setImg(filePath);

filePath = m_btnImgPath + L"Maximum.png";
m_btnMaximum.setImg(filePath);

说明:mousemove事件中增加了Mouseleav 事件生成的代码,涉及到TRACKMOUSEEVENT,读者可自行网搜之。

下一步将使用文件方式改造成使用资源的方式,这样代码封闭性好一些。

2020-04-10 于泛五道口地区

猜你喜欢

转载自blog.csdn.net/Uman/article/details/105430327