diff foosdk/sdk/libPPUI/CButtonLite.h @ 1:20d02a178406 default tip

*: check in everything else yay
author Paper <paper@tflc.us>
date Mon, 05 Jan 2026 02:15:46 -0500
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/foosdk/sdk/libPPUI/CButtonLite.h	Mon Jan 05 02:15:46 2026 -0500
@@ -0,0 +1,259 @@
+#pragma once
+
+#include <functional>
+#include <vsstyle.h>
+#include "wtl-pp.h"
+#include "win32_op.h"
+#include "DarkMode.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_MOUSELEAVE(OnMouseLeave)
+		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() const {
+		auto font = myGetFont();
+		LOGFONT lf;
+		WIN32_OP_D(font.GetLogFont(lf));
+		MakeBoldFont( lf );
+		CFont bold;
+		WIN32_OP_D(bold.CreateFontIndirect(&lf));
+		CWindowDC dc(*this);
+		auto oldFont = dc.SelectFont( bold );
+		CSize size (0,0);
+
+		{
+			CString measure;
+			measure = L"#";
+			measure += m_textDrawMe;
+			WIN32_OP_D(dc.GetTextExtent(measure, measure.GetLength(), &size));
+		}
+
+		dc.SelectFont( oldFont );
+
+		return size.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:
+	LRESULT MousePassThru(UINT cMsg, WPARAM cFlags, LPARAM lParam) {
+		SetMsgHandled(FALSE);
+		CPoint cPoint(lParam);
+		const DWORD maskButtons = MK_LBUTTON | MK_RBUTTON | MK_MBUTTON | MK_XBUTTON1 | MK_XBUTTON2;
+		if (cMsg == WM_MOUSEWHEEL || cMsg == WM_MOUSEHWHEEL || (cFlags & maskButtons) != 0) {
+			ToggleHot(false);
+		}
+		if (cMsg == WM_MOUSEWHEEL || cMsg == WM_MOUSEHWHEEL) {
+			TogglePressed(false);
+		}
+		if (cMsg == WM_LBUTTONUP) {
+			bool wasPressed = m_pressed;
+			TogglePressed(false);
+			if (wasPressed) OnClicked();
+			SetMsgHandled(TRUE);
+		}
+		return 0;
+	}
+	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) {
+		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, UINT, UINT) {
+	}
+	void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) {
+		(void)nRepCnt; (void)nFlags;
+		switch(nChar) {
+		case VK_SPACE:
+		case VK_RETURN:
+			TogglePressed(true); break;
+		}
+	}
+	void OnKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags) {
+		(void)nRepCnt; (void)nFlags;
+		switch(nChar) {
+		case VK_SPACE:
+		case VK_RETURN:
+			TogglePressed(false);
+			OnClicked(); 
+			break;
+		}
+	}
+	void OnSetFocus(CWindow) {
+		m_focused = true; Invalidate();
+	}
+	void OnKillFocus(CWindow) {
+		m_focused = false; Invalidate();
+	}
+	CFontHandle myGetFont() const {
+		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;
+	}
+
+	CWindow GetCtlColorTarget() {
+		CWindow target = WndCtlColorTarget;
+		if (target == NULL) target = GetParent();
+		return target;
+	}
+	virtual void DrawBackground( CDCHandle dc, CRect rcClient ) {
+		{
+			HBRUSH brush = NULL;
+			if (CtlColorHandler) {
+				brush = CtlColorHandler(dc);
+			} else {
+				brush = (HBRUSH)GetCtlColorTarget().SendMessage(WM_CTLCOLORBTN, (WPARAM)dc.m_hDC, (LPARAM)this->m_hWnd);
+			}
+			if (brush != NULL) {
+				dc.FillRect(rcClient, brush);
+				dc.SetBkMode(TRANSPARENT);
+			}
+		}
+		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);
+			}
+		}
+	}
+
+	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( DarkMode::GetSysColor(COLOR_GRAYTEXT) );
+		} else if ( m_focused ) {
+			pdc.SetTextColor( DarkMode::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) {
+		DarkMode::ApplyDarkThemeCtrl(*this, DarkMode::IsDialogDark(GetCtlColorTarget(), WM_CTLCOLORBTN));
+		if ( lpCreateStruct->lpszName != nullptr ) this->m_textDrawMe = lpCreateStruct->lpszName;
+		SetMsgHandled(FALSE); return 0;
+	}
+	void OnEnable(BOOL) {
+		Invalidate(); SetMsgHandled(FALSE);
+	}
+	void ToggleHot( bool bHot ) {
+		if ( bHot != m_hot ) {
+			m_hot = bHot; Invalidate();
+		}
+	}
+	void OnMouseMove(UINT nFlags, CPoint) {
+		const DWORD maskButtons = MK_LBUTTON | MK_RBUTTON | MK_MBUTTON | MK_XBUTTON1 | MK_XBUTTON2;
+		if ((nFlags & maskButtons) != 0) return;
+		ToggleHot(true);
+		TrackMouseLeave();
+	}
+	void OnLButtonDown(UINT nFlags, CPoint) {
+		const DWORD maskButtons = MK_LBUTTON | MK_RBUTTON | MK_MBUTTON | MK_XBUTTON1 | MK_XBUTTON2;
+		if ( ( nFlags & maskButtons ) != MK_LBUTTON ) return;
+		TogglePressed( true );
+		TrackMouseLeave();
+	}
+	void TogglePressed( bool bPressed ) {
+		if ( bPressed != m_pressed ) {
+			m_pressed = bPressed; Invalidate();
+		}
+	}
+	int OnSetText(LPCTSTR lpstrText) {
+		m_textDrawMe = lpstrText;
+		Invalidate(); SetMsgHandled(FALSE);
+		return 0;
+	}
+	void TrackMouseLeave() {
+		TRACKMOUSEEVENT tme = { sizeof(tme) };
+		tme.dwFlags = TME_LEAVE;
+		tme.hwndTrack = m_hWnd;
+		TrackMouseEvent(&tme);
+	}
+	void OnMouseLeave() {
+		ToggleHot(false); TogglePressed(false);
+	}
+
+	bool m_pressed = false, m_focused = false, m_hot = false;
+	CString m_textDrawMe;
+};