Files
foobar2000-sdk/foobar2000/ATLHelpers/CButtonLite.h

268 lines
7.3 KiB
C++

#pragma once
#include <functional>
#include "WTL-PP.h"
typedef CWinTraits<WS_CHILD|WS_TABSTOP,0> CButtonLiteTraits;
class CButtonLite : public CWindowImpl<CButtonLite, CWindow, CButtonLiteTraits > {
public:
BEGIN_MSG_MAP_EX(CButtonLite)
MESSAGE_RANGE_HANDLER_EX(WM_MOUSEFIRST, WM_MOUSELAST, MousePassThru);
MSG_WM_SETTEXT(OnSetText)
MSG_WM_PAINT( OnPaint )
MSG_WM_MOUSEMOVE(OnMouseMove)
MSG_WM_LBUTTONDOWN(OnLButtonDown)
MSG_WM_SETFOCUS(OnSetFocus)
MSG_WM_KILLFOCUS(OnKillFocus)
MSG_WM_KEYDOWN(OnKeyDown)
MSG_WM_KEYUP(OnKeyUp)
MSG_WM_CHAR(OnChar)
MSG_WM_ENABLE(OnEnable)
MESSAGE_HANDLER_EX(WM_GETDLGCODE, OnGetDlgCode)
MSG_WM_SETFONT(OnSetFont)
MSG_WM_GETFONT(OnGetFont)
MSG_WM_CREATE(OnCreate)
END_MSG_MAP()
std::function<void () > ClickHandler;
unsigned Measure() {
auto font = myGetFont();
LOGFONT lf;
font.GetLogFont( lf );
MakeBoldFont( lf );
CFont bold;
bold.CreateFontIndirect(&lf);
CWindowDC dc(*this);
auto oldFont = dc.SelectFont( bold );
CSize size (0,0);
CSize sizeSpace (0,0);
dc.GetTextExtent(m_textDrawMe, m_textDrawMe.GetLength(), &size);
dc.GetTextExtent(L" ", 1, &sizeSpace);
dc.SelectFont( oldFont );
return size.cx + sizeSpace.cx;
}
std::function< void (HWND) > TabCycleHandler;
std::function< HBRUSH (CDCHandle) > CtlColorHandler;
std::function< bool (HWND) > WantTabCheck;
CWindow WndCtlColorTarget;
// Rationale: sometimes you want a different text to be presented to accessibility APIs than actually drawn
// For an example, a clear button looks best with a multiplication sign, but the narrator should say "clear" or so when focused
void DrawAlternateText( const wchar_t * textDrawMe ) {
m_textDrawMe = textDrawMe;
}
protected:
CFontHandle m_font;
void OnSetFont(HFONT font, BOOL bRedraw) {
m_font = font; if (bRedraw) Invalidate();
}
HFONT OnGetFont() {
return m_font;
}
LRESULT OnGetDlgCode(UINT, WPARAM wp, LPARAM lp) {
if ( wp == VK_TAB && TabCycleHandler != NULL) {
if ( WantTabCheck == NULL || WantTabCheck(m_hWnd) ) {
TabCycleHandler( m_hWnd );
return DLGC_WANTTAB;
}
}
SetMsgHandled(FALSE); return 0;
}
void OnChar(UINT nChar, UINT nRepCnt, UINT nFlags) {
}
void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) {
switch(nChar) {
case VK_SPACE:
case VK_RETURN:
TogglePressed(true); break;
}
}
void OnKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags) {
switch(nChar) {
case VK_SPACE:
case VK_RETURN:
TogglePressed(false);
OnClicked();
break;
}
}
void OnSetFocus(CWindow wndOld) {
m_focused = true; Invalidate();
}
void OnKillFocus(CWindow wndFocus) {
m_focused = false; Invalidate();
}
CFontHandle myGetFont() {
auto f = GetFont();
if ( f == NULL ) f = (HFONT) GetStockObject(DEFAULT_GUI_FONT);
return f;
}
static void MakeBoldFont(LOGFONT & lf ) {
lf.lfWeight += 300;
if (lf.lfWeight > 1000 ) lf.lfWeight = 1000;
}
virtual void DrawBackground( CDCHandle dc, CRect rcClient ) {
HBRUSH brush = NULL;
if ( IsPressed() ) {
CTheme theme;
if (theme.OpenThemeData(*this, L"BUTTON" )) {
DrawThemeBackground(theme, dc, BP_PUSHBUTTON, PBS_PRESSED, rcClient, rcClient );
} else {
DrawFrameControl( dc, rcClient, DFC_BUTTON, DFCS_PUSHED );
}
} else if (m_hot) {
CTheme theme;
if (theme.OpenThemeData(*this, L"BUTTON")) {
DrawThemeBackground(theme, dc, BP_PUSHBUTTON, PBS_HOT, rcClient, rcClient);
} else {
DrawFrameControl(dc, rcClient, DFC_BUTTON, DFCS_HOT);
}
} else if ( CtlColorHandler ) {
brush = CtlColorHandler( dc );
} else {
CWindow target = WndCtlColorTarget;
if ( target == NULL ) target = GetParent();
brush = (HBRUSH) target.SendMessage(WM_CTLCOLORBTN, (WPARAM) dc.m_hDC, (LPARAM) this->m_hWnd );
}
if ( brush != NULL ) {
dc.FillRect( rcClient, brush );
dc.SetBkMode( TRANSPARENT );
}
}
virtual void OnPaint(CDCHandle) {
CPaintDC pdc(*this);
CRect rcClient;
if (! GetClientRect( &rcClient ) ) return;
auto font = myGetFont();
/*
CFont fontOverride;
if ( IsPressed() ) {
LOGFONT lf;
font.GetLogFont( lf );
MakeBoldFont( lf );
fontOverride.CreateFontIndirect( & lf );
font = fontOverride;
}
*/
HFONT oldFont = pdc.SelectFont( font );
DrawBackground( pdc.m_hDC, rcClient );
pdc.SetBkMode( TRANSPARENT );
if ( !IsWindowEnabled() ) {
pdc.SetTextColor( ::GetSysColor(COLOR_GRAYTEXT) );
} else if ( m_focused ) {
pdc.SetTextColor( ::GetSysColor(COLOR_HIGHLIGHT) );
}
pdc.DrawText( m_textDrawMe, m_textDrawMe.GetLength(), &rcClient, DT_VCENTER | DT_CENTER | DT_SINGLELINE | DT_NOPREFIX );
pdc.SelectFont( oldFont );
}
virtual void OnClicked() {
if ( ClickHandler ) {
ClickHandler();
} else {
GetParent().PostMessage( WM_COMMAND, MAKEWPARAM( this->GetDlgCtrlID(), BN_CLICKED ), (LPARAM) m_hWnd );
}
}
bool IsPressed() {return m_pressed; }
private:
int OnCreate(LPCREATESTRUCT lpCreateStruct) {
if ( lpCreateStruct->lpszName != nullptr ) this->m_textDrawMe = lpCreateStruct->lpszName;
SetMsgHandled(FALSE); return 0;
}
void OnEnable(BOOL bEnable) {
Invalidate(); SetMsgHandled(FALSE);
}
void ToggleHot( bool bHot ) {
if ( bHot != m_hot ) {
m_hot = bHot; Invalidate();
}
}
void OnMouseMove(UINT nFlags, CPoint point) {
const DWORD maskButtons = MK_LBUTTON | MK_RBUTTON | MK_MBUTTON | MK_XBUTTON1 | MK_XBUTTON2;
if ((nFlags & maskButtons) != 0) return;
CRect rcClient;
if (!GetClientRect(rcClient)) return;
if (!rcClient.PtInRect( point ) ) return;
ToggleHot( true );
SetCaptureEx([=](UINT cMsg, DWORD cFlags, CPoint cPoint) {
CRect rcClient;
if (!GetClientRect(rcClient)) return false;
if ( cMsg == WM_MOUSEWHEEL || cMsg == WM_MOUSEHWHEEL || (cFlags & maskButtons) != 0 || !rcClient.PtInRect(cPoint) ) {
ToggleHot(false);
SetMsgHandled( FALSE );
return false;
}
return true;
} );
}
void OnLButtonDown(UINT nFlags, CPoint point) {
const DWORD maskButtons = MK_LBUTTON | MK_RBUTTON | MK_MBUTTON | MK_XBUTTON1 | MK_XBUTTON2;
if ( ( nFlags & maskButtons ) != MK_LBUTTON ) return;
TogglePressed( true );
SetCaptureEx([=] (UINT cMsg, DWORD cFlags, CPoint cPoint) {
if (cMsg == WM_MOUSEWHEEL || cMsg == WM_MOUSEHWHEEL) {
TogglePressed(false);
SetMsgHandled(FALSE);
return false;
}
if ( cMsg == WM_LBUTTONUP ) {
if ( m_pressed ) OnClicked();
TogglePressed(false);
return false;
}
CRect rcClient;
if (!GetClientRect( rcClient )) return false;
if ( (cFlags & maskButtons) != (nFlags & maskButtons ) || ! rcClient.PtInRect( cPoint ) ) {
TogglePressed(false);
SetMsgHandled( FALSE ); return false;
}
return true;
} );
}
void TogglePressed( bool bPressed ) {
if ( bPressed != m_pressed ) {
m_pressed = bPressed; Invalidate();
}
}
int OnSetText(LPCTSTR lpstrText) {
m_textDrawMe = lpstrText;
Invalidate(); SetMsgHandled(FALSE);
return 0;
}
typedef std::function< bool(UINT, DWORD, CPoint) > CaptureProc_t;
void SetCaptureEx( CaptureProc_t proc ) {
m_captureProc = proc; SetCapture();
}
LRESULT MousePassThru(UINT msg, WPARAM wp, LPARAM lp) {
auto p = m_captureProc; // create local ref in case something in mid-captureproc clears it
if (p) {
CPoint pt(lp);
if (!p(msg, (DWORD)wp, pt)) {
::ReleaseCapture();
m_captureProc = nullptr;
}
return 0;
}
SetMsgHandled(FALSE);
return 0;
}
CaptureProc_t m_captureProc;
bool m_pressed = false, m_focused = false, m_hot = false;
CString m_textDrawMe;
};