comparison 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
comparison
equal deleted inserted replaced
0:e9bb126753e7 1:20d02a178406
1 #pragma once
2
3 #include <list>
4 #include <string>
5 #include <functional>
6 #include <memory>
7 #include "wtl-pp.h"
8
9 #include "CButtonLite.h"
10
11 class CEditWithButtons : public CEditPPHooks {
12 public:
13 CEditWithButtons(::ATL::CMessageMap * hookMM = nullptr, int hookMMID = 0) : CEditPPHooks(hookMM, hookMMID) {}
14
15 static constexpr UINT MSG_CHECKCONDITIONS = WM_USER + 13;
16 static constexpr WPARAM MSG_CHECKCONDITIONS_MAGIC1 = 0xaec66f0c;
17 static constexpr LPARAM MSG_CHECKCONDITIONS_MAGIC2 = 0x180c2f35;
18
19 BEGIN_MSG_MAP_EX(CEditWithButtons)
20 MSG_WM_CREATE(OnCreate)
21 MSG_WM_SETFONT(OnSetFont)
22 MSG_WM_WINDOWPOSCHANGED(OnPosChanged)
23 MSG_WM_CTLCOLORBTN(OnColorBtn)
24 MESSAGE_HANDLER_EX(WM_GETDLGCODE, OnGetDlgCode)
25 MSG_WM_KEYDOWN(OnKeyDown)
26 MSG_WM_CHAR(OnChar)
27 // MSG_WM_SETFOCUS( OnSetFocus )
28 // MSG_WM_KILLFOCUS( OnKillFocus )
29 MSG_WM_ENABLE(OnEnable)
30 MSG_WM_SETTEXT( OnSetText )
31 MESSAGE_HANDLER_EX(WM_PAINT, CheckConditionsTrigger)
32 MESSAGE_HANDLER_EX(WM_CUT, CheckConditionsTrigger)
33 MESSAGE_HANDLER_EX(WM_PASTE, CheckConditionsTrigger)
34 MESSAGE_HANDLER_EX(MSG_CHECKCONDITIONS, OnCheckConditions)
35 CHAIN_MSG_MAP(CEditPPHooks)
36 END_MSG_MAP()
37
38 BOOL SubclassWindow( HWND wnd ) {
39 if (!CEditPPHooks::SubclassWindow(wnd)) return FALSE;
40 m_initialParent = GetParent();
41 this->ModifyStyle(0, WS_CLIPCHILDREN);
42 RefreshButtons();
43 return TRUE;
44 }
45 typedef std::function<void () > handler_t;
46 typedef std::function<bool (const wchar_t*) > condition_t;
47
48 void AddMoreButton( std::function<void ()> f );
49 void AddClearButton( const wchar_t * clearVal = L"", bool bHandleEsc = false);
50 void AddButton( const wchar_t * str, handler_t handler, condition_t condition = nullptr, const wchar_t * drawAlternateText = nullptr );
51
52 void SetFixedWidth(unsigned fw) {
53 m_fixedWidth = fw; m_fixedWidthAuto = false;
54 RefreshButtons();
55 }
56 void SetFixedWidth() {
57 m_fixedWidth = 0; m_fixedWidthAuto = true;
58 RefreshButtons();
59 }
60 CRect RectOfButton( const wchar_t * text );
61
62 void Invalidate() {
63 __super::Invalidate();
64 for( auto & i : m_buttons ) {
65 if (i.wnd != NULL) i.wnd.Invalidate();
66 }
67 }
68 void SetShellFolderAutoComplete() {
69 SetShellAutoComplete(SHACF_FILESYS_DIRS);
70 }
71 void SetShellFileAutoComplete() {
72 SetShellAutoComplete(SHACF_FILESYS_ONLY);
73 }
74 void SetShellAutoComplete(DWORD flags) {
75 SHAutoComplete(*this, flags);
76 SetHasAutoComplete();
77 }
78 void SetHasAutoComplete(bool bValue = true) {
79 m_hasAutoComplete = bValue;
80 }
81 void RefreshConditions(const wchar_t * newText = nullptr);
82 private:
83 int OnCreate(LPCREATESTRUCT lpCreateStruct) {
84 m_initialParent = GetParent();
85 SetMsgHandled(FALSE);
86 return 0;
87 }
88 LRESULT OnCheckConditions( UINT msg, WPARAM wp, LPARAM lp ) {
89 if ( msg == MSG_CHECKCONDITIONS && wp == MSG_CHECKCONDITIONS_MAGIC1 && lp == MSG_CHECKCONDITIONS_MAGIC2 ) {
90 this->RefreshConditions();
91 } else {
92 SetMsgHandled(FALSE);
93 }
94 return 0;
95 }
96 bool HaveConditions() {
97 for( auto i = m_buttons.begin(); i != m_buttons.end(); ++i ) {
98 if ( i->condition ) return true;
99 }
100 return false;
101 }
102 void PostCheckConditions() {
103 if ( HaveConditions() ) {
104 PostMessage( MSG_CHECKCONDITIONS, MSG_CHECKCONDITIONS_MAGIC1, MSG_CHECKCONDITIONS_MAGIC2 );
105 }
106 }
107 LRESULT CheckConditionsTrigger( UINT, WPARAM, LPARAM ) {
108 PostCheckConditions();
109 SetMsgHandled(FALSE);
110 return 0;
111 }
112 int OnSetText(LPCTSTR) {
113 PostCheckConditions();
114 SetMsgHandled(FALSE);
115 return 0;
116 }
117 void OnEnable(BOOL bEnable) {
118 for( auto & i : m_buttons ) {
119 if ( i.wnd != NULL ) {
120 i.wnd.EnableWindow( bEnable );
121 }
122 }
123 SetMsgHandled(FALSE);
124 }
125 void OnChar(UINT nChar, UINT nRepCnt, UINT nFlags) {
126 (void)nRepCnt; (void)nFlags;
127 if (nChar == VK_TAB ) {
128 return;
129 }
130 PostCheckConditions();
131 SetMsgHandled(FALSE);
132 }
133 void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) {
134 (void)nRepCnt; (void)nFlags;
135 if ( nChar == VK_TAB ) {
136 return;
137 }
138 SetMsgHandled(FALSE);
139 }
140 bool canStealTab() {
141 if (m_hasAutoComplete) return false;
142 if (IsShiftPressed()) return false;
143 if (m_buttons.size() == 0) return false;
144 return true;
145 }
146 UINT OnGetDlgCode(UINT, WPARAM wp, LPARAM) {
147 if ( wp == VK_TAB && canStealTab() ) {
148 for (auto i = m_buttons.begin(); i != m_buttons.end(); ++ i ) {
149 if ( i->visible ) {
150 TabFocusThis(i->wnd);
151 return DLGC_WANTTAB;
152 }
153 }
154 }
155 SetMsgHandled(FALSE); return 0;
156 }
157 void OnSetFocus(HWND) {
158 this->ModifyStyleEx(0, WS_EX_CONTROLPARENT ); SetMsgHandled(FALSE);
159 }
160 void OnKillFocus(HWND) {
161 this->ModifyStyleEx(WS_EX_CONTROLPARENT, 0 ); SetMsgHandled(FALSE);
162 }
163 HBRUSH OnColorBtn(CDCHandle dc, CButton) {
164 if ( (this->GetStyle() & ES_READONLY) != 0 || !this->IsWindowEnabled() ) {
165 return (HBRUSH) GetParent().SendMessage( WM_CTLCOLORSTATIC, (WPARAM) dc.m_hDC, (LPARAM) m_hWnd );
166 } else {
167 return (HBRUSH) GetParent().SendMessage( WM_CTLCOLOREDIT, (WPARAM) dc.m_hDC, (LPARAM) m_hWnd );
168 }
169 }
170 void OnPosChanged(LPWINDOWPOS) {
171 Layout(); SetMsgHandled(FALSE);
172 }
173
174 struct Button_t {
175 std::wstring title, titleDraw;
176 handler_t handler;
177 CButtonLite wnd;
178 bool visible;
179 condition_t condition;
180 };
181
182 void OnSetFont(CFontHandle font, BOOL bRedraw);
183
184 void RefreshButtons() {
185 if ( m_hWnd != NULL && m_buttons.size() > 0 ) {
186 Layout();
187 }
188 }
189 void Layout( ) {
190 CRect rc;
191 if (GetClientRect(&rc)) {
192 Layout(rc.Size(), NULL);
193 }
194 }
195 static bool IsShiftPressed() {
196 return (GetKeyState(VK_SHIFT) & 0x8000) ? true : false;
197 }
198 CWindow FindDialog() {
199 // Return a window that we can send WM_NEXTDLGCTL to
200 // 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
201 // Assume our initial parent window to be the right thing to talk to
202 PFC_ASSERT(m_initialParent != NULL);
203 return m_initialParent;
204 }
205 void TabFocusThis(HWND wnd) {
206 FindDialog().PostMessage(WM_NEXTDLGCTL, (WPARAM) wnd, TRUE );
207 }
208 void TabFocusPrevNext(bool bPrev) {
209 FindDialog().PostMessage(WM_NEXTDLGCTL, bPrev ? TRUE : FALSE, FALSE);
210 }
211 void TabCycleButtons(HWND wnd);
212
213 bool ButtonWantTab( HWND wnd );
214 bool EvalCondition( Button_t & btn, const wchar_t * newText );
215 void Layout(CSize size, CFontHandle fontSetMe);
216 unsigned MeasureButton(Button_t const & button );
217
218 unsigned m_fixedWidth = 0;
219 bool m_fixedWidthAuto = false;
220 std::list< Button_t > m_buttons;
221 bool m_hasAutoComplete = false;
222 CWindow m_initialParent;
223 };