📄 buttonex.cpp
字号:
//Check to see that the coordinates are valid
ASSERT( (p.x + lRadius <= LONG_MAX) && (p.y + lRadius <= LONG_MAX) );
ASSERT( (p.x - lRadius >= LONG_MIN) && (p.y - lRadius >= LONG_MIN) );
//Set starting values
lXoffset = lRadius;
lYoffset = 0;
lError = -lRadius;
do {
const double Pi = 3.141592654,
Pi_on_2 = Pi * 0.5,
Three_Pi_on_2 = Pi * 1.5;
COLORREF crColour;
double dAngle = atan2(lYoffset, lXoffset);
//Draw the current pixel, reflected across all four arcs
crColour = GetColour(dAngle, crBright, crDark);
pDC->SetPixelV(p.x + lXoffset, p.y + lYoffset, crColour);
crColour = GetColour(Pi_on_2 - dAngle, crBright, crDark);
pDC->SetPixelV(p.x + lYoffset, p.y + lXoffset, crColour);
crColour = GetColour(-Pi_on_2 + dAngle, crBright, crDark);
pDC->SetPixelV(p.x + lYoffset, p.y - lXoffset, crColour);
crColour = GetColour(-dAngle, crBright, crDark);
pDC->SetPixelV(p.x + lXoffset, p.y - lYoffset, crColour);
//Advance the error term and the constant X axis step
lError += lYoffset++;
//Check to see if error term has overflowed
if ((lError += lYoffset) >= 0)
lError -= --lXoffset * 2;
} while (lYoffset <= lXoffset); //Continue until halfway point
}
// The original Drawcircle function is split up into DrawCircleRight and DrawCircleLeft
// to make stretched buttons
//
void CBtnEx::DrawCircleLeft(CDC* pDC, CPoint p, LONG lRadius, COLORREF crBright, COLORREF crDark)
{
LONG lError, lXoffset, lYoffset;
//Check to see that the coordinates are valid
ASSERT( (p.x + lRadius <= LONG_MAX) && (p.y + lRadius <= LONG_MAX) );
ASSERT( (p.x - lRadius >= LONG_MIN) && (p.y - lRadius >= LONG_MIN) );
//Set starting values
lXoffset = lRadius;
lYoffset = 0;
lError = -lRadius;
do {
const double Pi = 3.141592654,
Pi_on_2 = Pi * 0.5,
Three_Pi_on_2 = Pi * 1.5;
COLORREF crColour;
double dAngle = atan2(lYoffset, lXoffset);
//Draw the current pixel, reflected across all eight arcs
crColour = GetColour(Pi_on_2 + dAngle, crBright, crDark);
pDC->SetPixelV(p.x - lYoffset, p.y + lXoffset, crColour);
crColour = GetColour(Pi - dAngle, crBright, crDark);
pDC->SetPixelV(p.x - lXoffset, p.y + lYoffset, crColour);
crColour = GetColour(-Pi + dAngle, crBright, crDark);
pDC->SetPixelV(p.x - lXoffset, p.y - lYoffset, crColour);
crColour = GetColour(-Pi_on_2 - dAngle, crBright, crDark);
pDC->SetPixelV(p.x - lYoffset, p.y - lXoffset, crColour);
//Advance the error term and the constant X axis step
lError += lYoffset++;
//Check to see if error term has overflowed
if ((lError += lYoffset) >= 0)
lError -= --lXoffset * 2;
} while (lYoffset <= lXoffset); //Continue until halfway point
}
/////////////////////////////////////////////////////////////////////////////
// CBtnEx
CBtnEx::CBtnEx()
{
m_bDrawDashedFocusCircle = TRUE;
m_bgTransparent = FALSE;
m_3DText = FALSE;
m_nBorder = 2;
m_disParentRepaint = FALSE;
m_pattern = patternRound;
m_clr3dShadow = ::GetSysColor(COLOR_3DSHADOW);
m_clr3dLight = ::GetSysColor(COLOR_3DLIGHT);
m_clr3dDKShadow = ::GetSysColor(COLOR_3DDKSHADOW);
m_clr3dHighLight = ::GetSysColor(COLOR_3DHIGHLIGHT);
m_clrFaceN = ::GetSysColor(COLOR_BTNFACE);
m_clrFaceS = m_clrFaceN;
m_clrFaceH = m_clrFaceN;
m_clrFaceD = m_clrFaceN;
m_bHover = FALSE;
m_bCapture = FALSE;
m_bMouseDown = FALSE;
m_enableHover = TRUE;
m_enableFlat = FALSE;
m_hCursor = NULL;
}
CBtnEx::~CBtnEx()
{
}
BEGIN_MESSAGE_MAP(CBtnEx, CButton)
//{{AFX_MSG_MAP(CBtnEx)
ON_WM_SIZE()
ON_WM_ERASEBKGND()
ON_WM_MOUSEMOVE()
ON_WM_LBUTTONDOWN()
ON_WM_LBUTTONUP()
ON_WM_CREATE()
ON_WM_SETCURSOR()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CBtnEx message handlers
void CBtnEx::OnSize( UINT nType, int cx, int cy )
{
CButton::OnSize( nType, cx, cy );
SetPattern( m_pattern );
}
void CBtnEx::OnLButtonDown(UINT nFlags, CPoint point)
{
// record that mouse is down
m_bMouseDown = true;
if (!m_bCapture) {
SetCapture();
m_bCapture = true;
}
CButton::OnLButtonDown(nFlags, point);
}
void CBtnEx::OnLButtonUp(UINT nFlags, CPoint point)
{
// record that mouse is released
CButton::OnLButtonUp(nFlags, point);
m_bMouseDown = false;
if (m_bCapture) {
ReleaseCapture();
m_bCapture = false;
}
CheckHover(point);
}
void CBtnEx::OnMouseMove(UINT nFlags, CPoint point)
{
// Test if mouse is above the button.
if( !m_bMouseDown )
CheckHover(point);
CButton::OnMouseMove(nFlags, point);
}
void CBtnEx::PopupMenu(UINT menuID, int subMenuID/*=0*/)
{
/* m_bMouseDown = false;
if (m_bCapture) {
ReleaseCapture();
m_bCapture = false;
}
m_bHover = false;
*/
//准备弹出菜单的位置
CPoint point;
{
CRect rect;
GetWindowRect(rect);
point.x = rect.left;
point.y = rect.bottom-3;
}
CMenu menu;
CMenu* pPopup;
CWnd* pWndPopupOwner = this;
VERIFY(menu.LoadMenu(menuID));
pPopup = menu.GetSubMenu(subMenuID);
ASSERT(pPopup != NULL);
while (pWndPopupOwner->GetStyle() & WS_CHILD)
pWndPopupOwner = pWndPopupOwner->GetParent();
pPopup->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON,
point.x, point.y, pWndPopupOwner);
//复位鼠标,否则执行菜单后按钮可能仍处于弹起状态
PostMessage( WM_LBUTTONUP, 0, 0 );
}
BOOL CBtnEx::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
{
if( m_hCursor != NULL )
{
SetCursor( m_hCursor );
::SetCursor( m_hCursor );
return TRUE;
}
return CButton::OnSetCursor(pWnd, nHitTest, message);
}
void CBtnEx::CheckHover(CPoint point)
{
if (HitTest(point))
{
if (!m_bCapture)
{
SetCapture();
m_bCapture = true;
}
if ( !m_bHover && m_enableHover )
{
m_bHover = true;
RedrawWindow();
}
}
else
{
if (m_bCapture)
{
ReleaseCapture();
m_bCapture = false;
}
m_bHover = false;
RedrawWindow();
}
}
BOOL CBtnEx::HitTest(CPoint point)
{
return m_rgn.PtInRegion( point );
}
LRESULT CBtnEx::DefWindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{
//引用自CBtnRgn
// I have noticed that default windows buttons can be clicked very quickly.
// Double or single click both result in a button being pushed down.
// For owner drawn buttons this is not the case. Double click does
// not push button down. Here is a solution for the problem:
// double click message is substituted for single click.
if (message == WM_LBUTTONDBLCLK)
message = WM_LBUTTONDOWN;
return CButton::DefWindowProc(message, wParam, lParam);
}
int CBtnEx::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CButton::OnCreate(lpCreateStruct) == -1)
return -1;
if( GetExStyle() & WS_EX_TRANSPARENT )
{//如果窗口有透明属性,则将本对象强制设置为透明属性
m_bgTransparent = TRUE;
}
else if( m_bgTransparent )
{//如果用户设置了透明属性,则将窗口强制设置为透明属性
ModifyStyleEx( 0, WS_EX_TRANSPARENT );
}
SetPattern( m_pattern );
return 0;
}
void CBtnEx::PreSubclassWindow()
{
CButton::PreSubclassWindow();
//初始化参数
m_bHover = FALSE;
m_bCapture = FALSE;
m_bMouseDown = FALSE;
//设成自绘属性以响应DrawItem()
ModifyStyle(0, BS_OWNERDRAW);
if( GetExStyle() & WS_EX_TRANSPARENT )
{//如果窗口有透明属性,则将本对象强制设置为透明属性
m_bgTransparent = TRUE;
}
else if( m_bgTransparent )
{//如果用户设置了透明属性,则将窗口强制设置为透明属性
ModifyStyleEx( 0, WS_EX_TRANSPARENT );
}
SetPattern( m_pattern );
}
void CBtnEx::SetPattern( EBtnPat pattern )
{
m_pattern = pattern;
if( !::IsWindow( GetSafeHwnd() ) )
{//窗口尚未建立
return;
}
CRect rect;
GetClientRect(rect);
if( m_pattern == patternRound )
{// set m_bStretch if the button is not square and landscape
m_bStretch = rect.Width() > rect.Height();
}
else
{
m_bStretch = TRUE;
}
// Resize the window to make it square if it is not stretched
if(!m_bStretch) rect.bottom = rect.right = min(rect.bottom,rect.right);
// Set the window region so mouse clicks only activate the rgn section
// of the button
switch( m_pattern )
{
case patternRound:
m_rgn.DeleteObject();
{//计算圆按钮,取圆与RECT的并集
CRgn rgnRound;
CRect rc, rc0( rect );
int h = rect.Height();
rc0.DeflateRect( 1, 1 );
rc = rc0;
rc.DeflateRect( h/2, 0 );
m_rgn.CreateRectRgnIndirect( rc );
rc = rc0;
rc.right = rc.left + h;
rgnRound.CreateEllipticRgnIndirect( rc );
m_rgn.CombineRgn( &m_rgn, &rgnRound, RGN_OR );
rc = rc0;
rc.left = rc.right - h;
rgnRound.DeleteObject();
rgnRound.CreateEllipticRgnIndirect( rc );
m_rgn.CombineRgn( &m_rgn, &rgnRound, RGN_OR );
}
break;
case patternRect:
m_rgn.DeleteObject();
m_rgn.CreateRectRgnIndirect( rect );
break;
default://如果m_rgn存在则不改变
if( m_rgn.GetSafeHandle() == NULL )
{
m_rgn.CreateRectRgnIndirect( rect );
}
}
if( m_wndRgn.GetSafeHandle() == NULL )
{
m_wndRgn.CreateRectRgn( 0, 0, 0, 0 );
}
m_wndRgn.CopyRgn( &m_rgn );
SetWindowRgn(NULL, TRUE);
SetWindowRgn(m_wndRgn, TRUE);
//Resize the window if it is not stretched
if(!m_bStretch)
{// Convert client coords to the parents client coords
CWnd* pParent = GetParent();
ClientToScreen(rect);
if(pParent)
{
pParent->ScreenToClient(rect);
MoveWindow(rect.left, rect.top, rect.Width(), rect.Height() );
}
}
// Get the vital statistics of the window
// m_ptLeft/m_ptRight are the centerpoints of the left/right arcs of stretched buttons
m_rgn.GetRgnBox( rect );
m_ptCentre = m_ptLeft = m_ptRight = rect.CenterPoint();
m_nRadius = rect.bottom/2-1;
m_ptLeft.x = m_nRadius;
m_ptRight.x = rect.right - m_nRadius - 1;
}
BOOL CBtnEx::SetFaceIMG(UINT bmpID, int cx, COLORREF mask)
{
CBitmap bmp;
if( !bmp.LoadBitmap( bmpID ) )
{
return FALSE;
}
return SetFaceIMG( &bmp, cx, mask );
}
BOOL CBtnEx::SetFaceIMG( CBitmap * pBMP, int cx, COLORREF mask)
{
BITMAP sBMP;
pBMP->GetBitmap( &sBMP );
m_faceIMG.DeleteImageList();
m_faceIMG.Create( cx, sBMP.bmHeight, ILC_COLOR24|ILC_MASK, 0, 1 );
return ( m_faceIMG.Add( pBMP, mask ) >= 0 );
}
BOOL CBtnEx::NotifyParentRepaint( LPRECT rect)
{
//这是实在不得已才用的方法:
//本对象更新时,父窗口得不到更新,但父窗口更新时本对象自动更新
//但本对象更新时,确实需要父窗口更新一次
//如果不用disParentRepaint来锁定,就会进入死循环
if( !m_bgTransparent )
{//不透明,不用通知父窗口,自己画就行了
return FALSE;
}
if( !m_disParentRepaint )
{
if( GetParent() != NULL )
{
MapWindowPoints( GetParent(), rect );
m_disParentRepaint = TRUE;
GetParent()->InvalidateRect( rect );
return TRUE;
}
else
{
return FALSE;
}
}
else
{
m_disParentRepaint = FALSE;
return FALSE;
}
}
void CBtnEx::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
ASSERT(lpDrawItemStruct != NULL);
if( NotifyParentRepaint( &(lpDrawItemStruct->rcItem) ) )
{//已通知父窗口重画,自己暂时不用画,等父窗口发来的信号再画
return;
}
CDC * pDC = CDC::FromHandle(lpDrawItemStruct->hDC);
CRect rect = lpDrawItemStruct->rcItem;
UINT state = lpDrawItemStruct->itemState;
int nRadius = m_nRadius;
pDC->SelectClipRgn(&m_rgn);
DrawItemBG( pDC, state, rect, nRadius );//画背景
DrawItemFace( pDC, state, rect, nRadius );//画表面
pDC->SelectClipRgn( NULL );
DrawItemBorder( pDC, state, rect, nRadius );//画边框
}
void CBtnEx::DrawItemFace( CDC * pDC, UINT state, CRect & rect, int & nRadius )
{
if( m_faceIMG.GetSafeHandle() != NULL
&& m_faceIMG.GetImageCount() > 0 )
{
DrawFaceIMG( pDC, state, rect, nRadius );
}
else
{
DrawFaceText( pDC, state, rect, nRadius );
}
}
void CBtnEx::DrawItemBorder( CDC * pDC, UINT state, CRect & rect, int & nRadius )
{
if( !(state & ODS_SELECTED) && !m_bHover )
{//不是按下或弹起状态
if( m_enableFlat )
{//使用平面按钮,不画边框
return;
}
}
switch( m_pattern )
{
case patternRound:
DrawBorderRound( pDC, state, rect, nRadius );
break;
case patternRect:
DrawBorderRect( pDC, state, rect, nRadius );
break;
default:
DrawBorderOther( pDC, state, rect, nRadius );
}
}
void CBtnEx::DrawItemBG(CDC * pDC, UINT state, CRect & rect, int & nRadius )
{
if( !m_bgTransparent )
{
COLORREF clr;
if( state & ODS_DISABLED )
clr = m_clrFaceD;
else if( state & ODS_SELECTED )
clr = m_clrFaceS;
else if( m_bHover )
clr = m_clrFaceH;
else
clr = m_clrFaceN;
pDC->FillSolidRect(rect, clr);
}
}
void CBtnEx::DrawFaceText(CDC * pDC, UINT state, CRect & rect, int & nRadius )
{
// draw the text if there is any
CString strText;
GetWindowText(strText);
if( strText.IsEmpty() )
{
return;
}
UINT nStyle = GetStyle();
CSize Extent = pDC->GetTextExtent(strText);
if( strText.Find( '&' ) >= 0 )
{//有&符号
CSize ex = pDC->GetTextExtent( "&" );
Extent.cx -= ex.cx;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -