|
1
|
1 #pragma once
|
|
|
2
|
|
|
3 #include <functional>
|
|
|
4 #include <vsstyle.h>
|
|
|
5 #include "wtl-pp.h"
|
|
|
6 #include "win32_op.h"
|
|
|
7 #include "DarkMode.h"
|
|
|
8
|
|
|
9 typedef CWinTraits<WS_CHILD|WS_TABSTOP,0> CButtonLiteTraits;
|
|
|
10
|
|
|
11 class CButtonLite : public CWindowImpl<CButtonLite, CWindow, CButtonLiteTraits > {
|
|
|
12 public:
|
|
|
13 BEGIN_MSG_MAP_EX(CButtonLite)
|
|
|
14 MESSAGE_RANGE_HANDLER_EX(WM_MOUSEFIRST, WM_MOUSELAST, MousePassThru)
|
|
|
15 MSG_WM_MOUSELEAVE(OnMouseLeave)
|
|
|
16 MSG_WM_SETTEXT(OnSetText)
|
|
|
17 MSG_WM_PAINT( OnPaint )
|
|
|
18 MSG_WM_MOUSEMOVE(OnMouseMove)
|
|
|
19 MSG_WM_LBUTTONDOWN(OnLButtonDown)
|
|
|
20 MSG_WM_SETFOCUS(OnSetFocus)
|
|
|
21 MSG_WM_KILLFOCUS(OnKillFocus)
|
|
|
22 MSG_WM_KEYDOWN(OnKeyDown)
|
|
|
23 MSG_WM_KEYUP(OnKeyUp)
|
|
|
24 MSG_WM_CHAR(OnChar)
|
|
|
25 MSG_WM_ENABLE(OnEnable)
|
|
|
26 MESSAGE_HANDLER_EX(WM_GETDLGCODE, OnGetDlgCode)
|
|
|
27 MSG_WM_SETFONT(OnSetFont)
|
|
|
28 MSG_WM_GETFONT(OnGetFont)
|
|
|
29 MSG_WM_CREATE(OnCreate)
|
|
|
30 END_MSG_MAP()
|
|
|
31 std::function<void () > ClickHandler;
|
|
|
32
|
|
|
33 unsigned Measure() const {
|
|
|
34 auto font = myGetFont();
|
|
|
35 LOGFONT lf;
|
|
|
36 WIN32_OP_D(font.GetLogFont(lf));
|
|
|
37 MakeBoldFont( lf );
|
|
|
38 CFont bold;
|
|
|
39 WIN32_OP_D(bold.CreateFontIndirect(&lf));
|
|
|
40 CWindowDC dc(*this);
|
|
|
41 auto oldFont = dc.SelectFont( bold );
|
|
|
42 CSize size (0,0);
|
|
|
43
|
|
|
44 {
|
|
|
45 CString measure;
|
|
|
46 measure = L"#";
|
|
|
47 measure += m_textDrawMe;
|
|
|
48 WIN32_OP_D(dc.GetTextExtent(measure, measure.GetLength(), &size));
|
|
|
49 }
|
|
|
50
|
|
|
51 dc.SelectFont( oldFont );
|
|
|
52
|
|
|
53 return size.cx;
|
|
|
54 }
|
|
|
55 std::function< void (HWND) > TabCycleHandler;
|
|
|
56 std::function< HBRUSH (CDCHandle) > CtlColorHandler;
|
|
|
57 std::function< bool (HWND) > WantTabCheck;
|
|
|
58 CWindow WndCtlColorTarget;
|
|
|
59
|
|
|
60 // Rationale: sometimes you want a different text to be presented to accessibility APIs than actually drawn
|
|
|
61 // For an example, a clear button looks best with a multiplication sign, but the narrator should say "clear" or so when focused
|
|
|
62 void DrawAlternateText( const wchar_t * textDrawMe ) {
|
|
|
63 m_textDrawMe = textDrawMe;
|
|
|
64 }
|
|
|
65
|
|
|
66 protected:
|
|
|
67 LRESULT MousePassThru(UINT cMsg, WPARAM cFlags, LPARAM lParam) {
|
|
|
68 SetMsgHandled(FALSE);
|
|
|
69 CPoint cPoint(lParam);
|
|
|
70 const DWORD maskButtons = MK_LBUTTON | MK_RBUTTON | MK_MBUTTON | MK_XBUTTON1 | MK_XBUTTON2;
|
|
|
71 if (cMsg == WM_MOUSEWHEEL || cMsg == WM_MOUSEHWHEEL || (cFlags & maskButtons) != 0) {
|
|
|
72 ToggleHot(false);
|
|
|
73 }
|
|
|
74 if (cMsg == WM_MOUSEWHEEL || cMsg == WM_MOUSEHWHEEL) {
|
|
|
75 TogglePressed(false);
|
|
|
76 }
|
|
|
77 if (cMsg == WM_LBUTTONUP) {
|
|
|
78 bool wasPressed = m_pressed;
|
|
|
79 TogglePressed(false);
|
|
|
80 if (wasPressed) OnClicked();
|
|
|
81 SetMsgHandled(TRUE);
|
|
|
82 }
|
|
|
83 return 0;
|
|
|
84 }
|
|
|
85 CFontHandle m_font;
|
|
|
86 void OnSetFont(HFONT font, BOOL bRedraw) {
|
|
|
87 m_font = font; if (bRedraw) Invalidate();
|
|
|
88 }
|
|
|
89 HFONT OnGetFont() {
|
|
|
90 return m_font;
|
|
|
91 }
|
|
|
92 LRESULT OnGetDlgCode(UINT, WPARAM wp, LPARAM) {
|
|
|
93 if ( wp == VK_TAB && TabCycleHandler != NULL) {
|
|
|
94 if ( WantTabCheck == NULL || WantTabCheck(m_hWnd) ) {
|
|
|
95 TabCycleHandler( m_hWnd );
|
|
|
96 return DLGC_WANTTAB;
|
|
|
97 }
|
|
|
98 }
|
|
|
99 SetMsgHandled(FALSE); return 0;
|
|
|
100 }
|
|
|
101 void OnChar(UINT, UINT, UINT) {
|
|
|
102 }
|
|
|
103 void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) {
|
|
|
104 (void)nRepCnt; (void)nFlags;
|
|
|
105 switch(nChar) {
|
|
|
106 case VK_SPACE:
|
|
|
107 case VK_RETURN:
|
|
|
108 TogglePressed(true); break;
|
|
|
109 }
|
|
|
110 }
|
|
|
111 void OnKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags) {
|
|
|
112 (void)nRepCnt; (void)nFlags;
|
|
|
113 switch(nChar) {
|
|
|
114 case VK_SPACE:
|
|
|
115 case VK_RETURN:
|
|
|
116 TogglePressed(false);
|
|
|
117 OnClicked();
|
|
|
118 break;
|
|
|
119 }
|
|
|
120 }
|
|
|
121 void OnSetFocus(CWindow) {
|
|
|
122 m_focused = true; Invalidate();
|
|
|
123 }
|
|
|
124 void OnKillFocus(CWindow) {
|
|
|
125 m_focused = false; Invalidate();
|
|
|
126 }
|
|
|
127 CFontHandle myGetFont() const {
|
|
|
128 auto f = GetFont();
|
|
|
129 if ( f == NULL ) f = (HFONT) GetStockObject(DEFAULT_GUI_FONT);
|
|
|
130 return f;
|
|
|
131 }
|
|
|
132 static void MakeBoldFont(LOGFONT & lf ) {
|
|
|
133 lf.lfWeight += 300;
|
|
|
134 if (lf.lfWeight > 1000 ) lf.lfWeight = 1000;
|
|
|
135 }
|
|
|
136
|
|
|
137 CWindow GetCtlColorTarget() {
|
|
|
138 CWindow target = WndCtlColorTarget;
|
|
|
139 if (target == NULL) target = GetParent();
|
|
|
140 return target;
|
|
|
141 }
|
|
|
142 virtual void DrawBackground( CDCHandle dc, CRect rcClient ) {
|
|
|
143 {
|
|
|
144 HBRUSH brush = NULL;
|
|
|
145 if (CtlColorHandler) {
|
|
|
146 brush = CtlColorHandler(dc);
|
|
|
147 } else {
|
|
|
148 brush = (HBRUSH)GetCtlColorTarget().SendMessage(WM_CTLCOLORBTN, (WPARAM)dc.m_hDC, (LPARAM)this->m_hWnd);
|
|
|
149 }
|
|
|
150 if (brush != NULL) {
|
|
|
151 dc.FillRect(rcClient, brush);
|
|
|
152 dc.SetBkMode(TRANSPARENT);
|
|
|
153 }
|
|
|
154 }
|
|
|
155 if ( IsPressed() ) {
|
|
|
156 CTheme theme;
|
|
|
157 if (theme.OpenThemeData(*this, L"BUTTON" )) {
|
|
|
158 DrawThemeBackground(theme, dc, BP_PUSHBUTTON, PBS_PRESSED, rcClient, rcClient );
|
|
|
159 } else {
|
|
|
160 DrawFrameControl( dc, rcClient, DFC_BUTTON, DFCS_PUSHED );
|
|
|
161 }
|
|
|
162 } else if (m_hot) {
|
|
|
163 CTheme theme;
|
|
|
164 if (theme.OpenThemeData(*this, L"BUTTON")) {
|
|
|
165 DrawThemeBackground(theme, dc, BP_PUSHBUTTON, PBS_HOT, rcClient, rcClient);
|
|
|
166 } else {
|
|
|
167 DrawFrameControl(dc, rcClient, DFC_BUTTON, DFCS_HOT);
|
|
|
168 }
|
|
|
169 }
|
|
|
170 }
|
|
|
171
|
|
|
172 virtual void OnPaint(CDCHandle) {
|
|
|
173 CPaintDC pdc(*this);
|
|
|
174
|
|
|
175 CRect rcClient;
|
|
|
176 if (! GetClientRect( &rcClient ) ) return;
|
|
|
177
|
|
|
178 auto font = myGetFont();
|
|
|
179 /*
|
|
|
180 CFont fontOverride;
|
|
|
181 if ( IsPressed() ) {
|
|
|
182 LOGFONT lf;
|
|
|
183 font.GetLogFont( lf );
|
|
|
184 MakeBoldFont( lf );
|
|
|
185 fontOverride.CreateFontIndirect( & lf );
|
|
|
186 font = fontOverride;
|
|
|
187 }
|
|
|
188 */
|
|
|
189 HFONT oldFont = pdc.SelectFont( font );
|
|
|
190
|
|
|
191 DrawBackground( pdc.m_hDC, rcClient );
|
|
|
192
|
|
|
193 pdc.SetBkMode( TRANSPARENT );
|
|
|
194 if ( !IsWindowEnabled() ) {
|
|
|
195 pdc.SetTextColor( DarkMode::GetSysColor(COLOR_GRAYTEXT) );
|
|
|
196 } else if ( m_focused ) {
|
|
|
197 pdc.SetTextColor( DarkMode::GetSysColor(COLOR_HIGHLIGHT) );
|
|
|
198 }
|
|
|
199 pdc.DrawText( m_textDrawMe, m_textDrawMe.GetLength(), &rcClient, DT_VCENTER | DT_CENTER | DT_SINGLELINE | DT_NOPREFIX );
|
|
|
200
|
|
|
201 pdc.SelectFont( oldFont );
|
|
|
202 }
|
|
|
203 virtual void OnClicked() {
|
|
|
204 if ( ClickHandler ) {
|
|
|
205 ClickHandler();
|
|
|
206 } else {
|
|
|
207 GetParent().PostMessage( WM_COMMAND, MAKEWPARAM( this->GetDlgCtrlID(), BN_CLICKED ), (LPARAM) m_hWnd );
|
|
|
208 }
|
|
|
209 }
|
|
|
210 bool IsPressed() {return m_pressed; }
|
|
|
211 private:
|
|
|
212 int OnCreate(LPCREATESTRUCT lpCreateStruct) {
|
|
|
213 DarkMode::ApplyDarkThemeCtrl(*this, DarkMode::IsDialogDark(GetCtlColorTarget(), WM_CTLCOLORBTN));
|
|
|
214 if ( lpCreateStruct->lpszName != nullptr ) this->m_textDrawMe = lpCreateStruct->lpszName;
|
|
|
215 SetMsgHandled(FALSE); return 0;
|
|
|
216 }
|
|
|
217 void OnEnable(BOOL) {
|
|
|
218 Invalidate(); SetMsgHandled(FALSE);
|
|
|
219 }
|
|
|
220 void ToggleHot( bool bHot ) {
|
|
|
221 if ( bHot != m_hot ) {
|
|
|
222 m_hot = bHot; Invalidate();
|
|
|
223 }
|
|
|
224 }
|
|
|
225 void OnMouseMove(UINT nFlags, CPoint) {
|
|
|
226 const DWORD maskButtons = MK_LBUTTON | MK_RBUTTON | MK_MBUTTON | MK_XBUTTON1 | MK_XBUTTON2;
|
|
|
227 if ((nFlags & maskButtons) != 0) return;
|
|
|
228 ToggleHot(true);
|
|
|
229 TrackMouseLeave();
|
|
|
230 }
|
|
|
231 void OnLButtonDown(UINT nFlags, CPoint) {
|
|
|
232 const DWORD maskButtons = MK_LBUTTON | MK_RBUTTON | MK_MBUTTON | MK_XBUTTON1 | MK_XBUTTON2;
|
|
|
233 if ( ( nFlags & maskButtons ) != MK_LBUTTON ) return;
|
|
|
234 TogglePressed( true );
|
|
|
235 TrackMouseLeave();
|
|
|
236 }
|
|
|
237 void TogglePressed( bool bPressed ) {
|
|
|
238 if ( bPressed != m_pressed ) {
|
|
|
239 m_pressed = bPressed; Invalidate();
|
|
|
240 }
|
|
|
241 }
|
|
|
242 int OnSetText(LPCTSTR lpstrText) {
|
|
|
243 m_textDrawMe = lpstrText;
|
|
|
244 Invalidate(); SetMsgHandled(FALSE);
|
|
|
245 return 0;
|
|
|
246 }
|
|
|
247 void TrackMouseLeave() {
|
|
|
248 TRACKMOUSEEVENT tme = { sizeof(tme) };
|
|
|
249 tme.dwFlags = TME_LEAVE;
|
|
|
250 tme.hwndTrack = m_hWnd;
|
|
|
251 TrackMouseEvent(&tme);
|
|
|
252 }
|
|
|
253 void OnMouseLeave() {
|
|
|
254 ToggleHot(false); TogglePressed(false);
|
|
|
255 }
|
|
|
256
|
|
|
257 bool m_pressed = false, m_focused = false, m_hot = false;
|
|
|
258 CString m_textDrawMe;
|
|
|
259 };
|