diff foosdk/sdk/libPPUI/CEditWithButtons.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/CEditWithButtons.h	Mon Jan 05 02:15:46 2026 -0500
@@ -0,0 +1,223 @@
+#pragma once
+
+#include <list>
+#include <string>
+#include <functional>
+#include <memory>
+#include "wtl-pp.h"
+
+#include "CButtonLite.h"
+
+class CEditWithButtons : public CEditPPHooks {
+public:
+	CEditWithButtons(::ATL::CMessageMap * hookMM = nullptr, int hookMMID = 0) : CEditPPHooks(hookMM, hookMMID) {}
+
+	static constexpr UINT MSG_CHECKCONDITIONS = WM_USER + 13;
+	static constexpr WPARAM MSG_CHECKCONDITIONS_MAGIC1 = 0xaec66f0c;
+	static constexpr LPARAM MSG_CHECKCONDITIONS_MAGIC2 = 0x180c2f35;
+
+	BEGIN_MSG_MAP_EX(CEditWithButtons)
+		MSG_WM_CREATE(OnCreate)
+		MSG_WM_SETFONT(OnSetFont)
+		MSG_WM_WINDOWPOSCHANGED(OnPosChanged)
+		MSG_WM_CTLCOLORBTN(OnColorBtn)
+		MESSAGE_HANDLER_EX(WM_GETDLGCODE, OnGetDlgCode)
+		MSG_WM_KEYDOWN(OnKeyDown)
+		MSG_WM_CHAR(OnChar)
+		// MSG_WM_SETFOCUS( OnSetFocus )
+		// MSG_WM_KILLFOCUS( OnKillFocus )
+		MSG_WM_ENABLE(OnEnable)
+		MSG_WM_SETTEXT( OnSetText )
+		MESSAGE_HANDLER_EX(WM_PAINT, CheckConditionsTrigger)
+		MESSAGE_HANDLER_EX(WM_CUT, CheckConditionsTrigger)
+		MESSAGE_HANDLER_EX(WM_PASTE, CheckConditionsTrigger)
+		MESSAGE_HANDLER_EX(MSG_CHECKCONDITIONS, OnCheckConditions)
+		CHAIN_MSG_MAP(CEditPPHooks)
+	END_MSG_MAP()
+
+	BOOL SubclassWindow( HWND wnd ) {
+		if (!CEditPPHooks::SubclassWindow(wnd)) return FALSE;
+		m_initialParent = GetParent();
+		this->ModifyStyle(0, WS_CLIPCHILDREN);
+		RefreshButtons();
+		return TRUE;
+	}
+	typedef std::function<void () > handler_t;
+	typedef std::function<bool (const wchar_t*) > condition_t;
+
+	void AddMoreButton( std::function<void ()> f );
+	void AddClearButton( const wchar_t * clearVal = L"", bool bHandleEsc = false);
+	void AddButton( const wchar_t * str, handler_t handler, condition_t condition = nullptr, const wchar_t * drawAlternateText = nullptr );
+
+	void SetFixedWidth(unsigned fw) {
+		m_fixedWidth = fw; m_fixedWidthAuto = false;
+		RefreshButtons();
+	}
+	void SetFixedWidth() {
+		m_fixedWidth = 0; m_fixedWidthAuto = true;
+		RefreshButtons();
+	}
+	CRect RectOfButton( const wchar_t * text );
+
+	void Invalidate() {
+		__super::Invalidate();
+		for( auto & i : m_buttons ) {
+			if (i.wnd != NULL) i.wnd.Invalidate();
+		}
+	}
+	void SetShellFolderAutoComplete() {
+		SetShellAutoComplete(SHACF_FILESYS_DIRS);
+	}
+	void SetShellFileAutoComplete() {
+		SetShellAutoComplete(SHACF_FILESYS_ONLY);
+	}
+	void SetShellAutoComplete(DWORD flags) {
+		SHAutoComplete(*this, flags);
+		SetHasAutoComplete();
+	}
+	void SetHasAutoComplete(bool bValue = true) {
+		m_hasAutoComplete = bValue;
+	}
+	void RefreshConditions(const wchar_t * newText = nullptr);
+private:
+	int OnCreate(LPCREATESTRUCT lpCreateStruct) {
+		m_initialParent = GetParent();
+		SetMsgHandled(FALSE);
+		return 0;
+	}
+	LRESULT OnCheckConditions( UINT msg, WPARAM wp, LPARAM lp ) {
+		if ( msg == MSG_CHECKCONDITIONS && wp == MSG_CHECKCONDITIONS_MAGIC1 && lp == MSG_CHECKCONDITIONS_MAGIC2 ) {
+			this->RefreshConditions();
+		} else {
+			SetMsgHandled(FALSE);
+		}		
+		return 0;
+	}
+	bool HaveConditions() {
+		for( auto i = m_buttons.begin(); i != m_buttons.end(); ++i ) {
+			if ( i->condition ) return true;
+		}
+		return false;
+	}
+	void PostCheckConditions() {
+		if ( HaveConditions() ) {
+			PostMessage( MSG_CHECKCONDITIONS, MSG_CHECKCONDITIONS_MAGIC1, MSG_CHECKCONDITIONS_MAGIC2 );
+		}
+	}
+	LRESULT CheckConditionsTrigger( UINT, WPARAM, LPARAM ) {
+		PostCheckConditions();
+		SetMsgHandled(FALSE);
+		return 0;
+	}
+	int OnSetText(LPCTSTR) {
+		PostCheckConditions();
+		SetMsgHandled(FALSE);
+		return 0;
+	}
+	void OnEnable(BOOL bEnable) {
+		for( auto & i : m_buttons ) {
+			if ( i.wnd != NULL ) {
+				i.wnd.EnableWindow( bEnable );
+			}
+		}
+		SetMsgHandled(FALSE); 
+	}
+	void OnChar(UINT nChar, UINT nRepCnt, UINT nFlags) {
+		(void)nRepCnt; (void)nFlags;
+		if (nChar == VK_TAB ) {
+			return;
+		}
+		PostCheckConditions();
+		SetMsgHandled(FALSE);
+	}
+	void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) {
+		(void)nRepCnt; (void)nFlags;
+		if ( nChar == VK_TAB ) {
+			return;
+		}
+		SetMsgHandled(FALSE);
+	}
+	bool canStealTab() {
+		if (m_hasAutoComplete) return false;
+		if (IsShiftPressed()) return false;
+		if (m_buttons.size() == 0) return false;
+		return true;
+	}
+	UINT OnGetDlgCode(UINT, WPARAM wp, LPARAM) {
+		if ( wp == VK_TAB && canStealTab() ) {
+			for (auto i = m_buttons.begin(); i != m_buttons.end(); ++ i ) {
+				if ( i->visible ) {
+					TabFocusThis(i->wnd);
+					return DLGC_WANTTAB;
+				}
+			}
+		}
+		SetMsgHandled(FALSE); return 0;
+	}
+	void OnSetFocus(HWND) {
+		this->ModifyStyleEx(0, WS_EX_CONTROLPARENT ); SetMsgHandled(FALSE);
+	}
+	void OnKillFocus(HWND) {
+		this->ModifyStyleEx(WS_EX_CONTROLPARENT, 0 ); SetMsgHandled(FALSE);
+	}
+	HBRUSH OnColorBtn(CDCHandle dc, CButton) {
+		if ( (this->GetStyle() & ES_READONLY) != 0 || !this->IsWindowEnabled() ) {
+			return (HBRUSH) GetParent().SendMessage( WM_CTLCOLORSTATIC, (WPARAM) dc.m_hDC, (LPARAM) m_hWnd );
+		} else {
+			return (HBRUSH) GetParent().SendMessage( WM_CTLCOLOREDIT, (WPARAM) dc.m_hDC, (LPARAM) m_hWnd );
+		}
+	}
+	void OnPosChanged(LPWINDOWPOS) {
+		Layout(); SetMsgHandled(FALSE);
+	}
+	
+	struct Button_t {
+		std::wstring title, titleDraw;
+		handler_t handler;
+		CButtonLite wnd;
+		bool visible;
+		condition_t condition;
+	};
+	
+	void OnSetFont(CFontHandle font, BOOL bRedraw);
+
+	void RefreshButtons() {
+		if ( m_hWnd != NULL && m_buttons.size() > 0 ) {
+			Layout();
+		}
+	}
+	void Layout( ) {
+		CRect rc;
+		if (GetClientRect(&rc)) {
+			Layout(rc.Size(), NULL);
+		}
+	}
+	static bool IsShiftPressed() {
+		return (GetKeyState(VK_SHIFT) & 0x8000) ? true : false;
+	}
+	CWindow FindDialog() {
+		// Return a window that we can send WM_NEXTDLGCTL to
+		// PROBLEM: There is no clear way of obtaining one - something in our GetParent() hierarchy is usually a dialog, but we don't know which one
+		// Assume our initial parent window to be the right thing to talk to
+		PFC_ASSERT(m_initialParent != NULL);
+		return m_initialParent;
+	}
+	void TabFocusThis(HWND wnd) {
+		FindDialog().PostMessage(WM_NEXTDLGCTL, (WPARAM) wnd, TRUE );
+	}
+	void TabFocusPrevNext(bool bPrev) {
+		FindDialog().PostMessage(WM_NEXTDLGCTL, bPrev ? TRUE : FALSE, FALSE);
+	}
+	void TabCycleButtons(HWND wnd);
+
+	bool ButtonWantTab( HWND wnd );
+	bool EvalCondition( Button_t & btn, const wchar_t * newText );
+	void Layout(CSize size, CFontHandle fontSetMe);
+	unsigned MeasureButton(Button_t const & button );
+
+	unsigned m_fixedWidth = 0;
+	bool m_fixedWidthAuto = false;
+	std::list< Button_t > m_buttons;
+	bool m_hasAutoComplete = false;
+	CWindow m_initialParent;
+};