comparison foosdk/sdk/libPPUI/CMiddleDragImpl.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 "CMiddleDragOverlay.h"
4 #include <utility>
5
6 class CMiddleDragCommon {
7 public:
8 enum {
9 KTimerID = 0x389675f8,
10 KTimerPeriod = 15,
11 };
12 static double myPow(double p_val, double p_exp);
13 static double ProcessMiddleDragDeltaInternal(double p_delta);
14 static double radiusHelper(double p_x, double p_y);
15 static int mySGN(LONG v);
16 static int32_t Round(double val, double & acc);
17 static LONG LineToPixelsHelper(LONG & p_overflow, LONG p_pixels, LONG p_dpi, LONG p_lineWidth);
18 };
19
20 template<typename TBase>
21 class CMiddleDragImpl : public TBase, protected CMiddleDragCommon {
22 private:
23 typedef CMiddleDragImpl<TBase> TSelf;
24 public:
25 template<typename ... arg_t> CMiddleDragImpl( arg_t && ... arg ) : TBase(std::forward<arg_t>(arg) ... ) {}
26
27 BEGIN_MSG_MAP_EX(TSelf)
28 MESSAGE_HANDLER(WM_TIMER,OnTimer);
29 MESSAGE_HANDLER(WM_CAPTURECHANGED,OnCaptureChanged);
30 MESSAGE_HANDLER(WM_MBUTTONDOWN,OnMButtonDown);
31 MESSAGE_HANDLER(WM_MBUTTONDBLCLK,OnMButtonDown);
32 MESSAGE_HANDLER(WM_MBUTTONUP,OnMButtonUp);
33 MESSAGE_HANDLER(WM_LBUTTONDOWN,OnMouseAction);
34 MESSAGE_HANDLER(WM_RBUTTONDOWN,OnMouseAction);
35 MESSAGE_HANDLER(WM_LBUTTONDBLCLK,OnMouseAction);
36 MESSAGE_HANDLER(WM_RBUTTONDBLCLK,OnMouseAction);
37 MESSAGE_HANDLER(WM_LBUTTONUP,OnMouseAction);
38 MESSAGE_HANDLER(WM_RBUTTONUP,OnMouseAction);
39 MESSAGE_HANDLER(WM_XBUTTONDOWN,OnMouseAction);
40 MESSAGE_HANDLER(WM_XBUTTONDBLCLK,OnMouseAction);
41 MESSAGE_HANDLER(WM_XBUTTONUP,OnMouseAction);
42 MESSAGE_HANDLER(WM_MOUSEMOVE,OnMouseMove);
43 MESSAGE_HANDLER(WM_DESTROY,OnDestroyPassThru);
44 CHAIN_MSG_MAP(TBase);
45 END_MSG_MAP()
46 protected:
47 bool CanSmoothScroll() const {
48 return !m_active;
49 }
50 private:
51 bool m_active = false, m_dragged = false;
52 CPoint m_base;
53 CMiddleDragOverlay m_overlay;
54 double m_accX = 0, m_accY = 0;
55
56 LRESULT OnDestroyPassThru(UINT,WPARAM,LPARAM,BOOL& bHandled) {
57 if (m_overlay != NULL) m_overlay.DestroyWindow();
58 bHandled = FALSE;
59 return 0;
60 }
61
62 LRESULT OnMButtonUp(UINT,WPARAM,LPARAM,BOOL&) {
63 if (m_active /*&& m_dragged*/) {
64 EndDrag();
65 }
66 return 0;
67 }
68
69 LRESULT OnMButtonDown(UINT,WPARAM,LPARAM p_lp,BOOL&) {
70 if (m_active) {
71 EndDrag();
72 return 0;
73 }
74 EndDrag();
75 this->SetFocus();
76 CPoint pt(p_lp);
77 WIN32_OP_D( this->ClientToScreen(&pt) );
78 StartDrag(pt);
79 return 0;
80 }
81
82 LRESULT OnMouseMove(UINT,WPARAM,LPARAM p_lp,BOOL& bHandled) {
83 if (m_active) {
84 if (!m_dragged) {
85 CPoint pt(p_lp);
86 WIN32_OP_D( this->ClientToScreen(&pt) );
87 if (pt != m_base) {
88 m_dragged = true;
89 }
90 }
91 bHandled = TRUE;
92 } else {
93 bHandled = FALSE;
94 }
95 return 0;
96 }
97
98 LRESULT OnMouseAction(UINT,WPARAM,LPARAM,BOOL& bHandled) {
99 EndDrag();
100 bHandled = FALSE;
101 return 0;
102 }
103
104 void StartDrag(CPoint const & p_point) {
105 ::SetCapture(NULL);
106
107 if (m_overlay == NULL) {
108 PFC_ASSERT( this->m_hWnd != NULL );
109 WIN32_OP_D(m_overlay.Create(*this));
110 }
111
112 //todo sanity checks - don't drag when the entire content is visible, perhaps use a different icon when only vertical or horizontal drag is possible
113 SetCursor(::LoadCursor(NULL,IDC_SIZEALL));
114 m_active = true;
115 m_dragged = false;
116 m_base = p_point;
117 m_accX = m_accY = 0;
118 this->SetCapture();
119 this->SetTimer(KTimerID,KTimerPeriod);
120
121 m_overlay.ShowHere(p_point);
122 }
123
124 void EndDrag() {
125 if (m_active) ::SetCapture(NULL);
126 }
127
128 void HandleEndDrag() {
129 if (m_active) {
130 m_active = false;
131 this->KillTimer(KTimerID);
132 if (m_overlay != NULL) m_overlay.ShowWindow(SW_HIDE);
133 }
134 }
135
136 LRESULT OnCaptureChanged(UINT,WPARAM,LPARAM,BOOL& bHandled) {
137 HandleEndDrag();
138 bHandled = FALSE;
139 return 0;
140 }
141
142 LRESULT OnTimer(UINT,WPARAM p_wp,LPARAM,BOOL& bHandled) {
143 switch(p_wp) {
144 case KTimerID:
145 {
146 CPoint ptCursor;
147 if (GetCursorPos(&ptCursor)) {
148 this->MoveViewOriginDelta(ProcessMiddleDragDelta(ptCursor - m_base));
149 }
150 }
151 return 0;
152 default:
153 bHandled = FALSE;
154 return 0;
155 }
156 }
157
158 CPoint ProcessMiddleDragDelta(CPoint p_delta) {
159 const CSize dpi = QueryScreenDPIEx();
160 if (dpi.cx <= 0 || dpi.cy <= 0 || p_delta == CPoint(0, 0)) return CPoint(0, 0);
161 const double dpiMulX = 96.0 / (double)dpi.cx;
162 const double dpiMulY = 96.0 / (double)dpi.cy;
163
164 const double deltaTotal = ProcessMiddleDragDeltaInternal(radiusHelper((double)p_delta.x * dpiMulX, (double)p_delta.y * dpiMulY));
165
166 double xVal = 0, yVal = 0;
167
168 if (p_delta.x == 0) {
169 yVal = deltaTotal;
170 } else if (p_delta.y == 0) {
171 xVal = deltaTotal;
172 } else {
173 const double ratio = (double)p_delta.x / (double)p_delta.y;
174 yVal = sqrt((deltaTotal*deltaTotal) / (1.0 + (ratio*ratio)));
175 xVal = yVal * ratio;
176 }
177
178 xVal = mySGN(p_delta.x) * fabs(xVal);
179 yVal = mySGN(p_delta.y) * fabs(yVal);
180
181
182 return CPoint(Round(xVal / dpiMulX, m_accX), Round(yVal / dpiMulY, m_accY));
183 }
184
185 };
186
187 template<typename TBase>
188 class CMiddleDragWrapper : public TBase {
189 private:
190 typedef CMiddleDragWrapper<TBase> TSelf;
191 public:
192 template<typename ... arg_t> CMiddleDragWrapper(arg_t && ... arg ) : TBase(std::forward<arg_t>(arg) ... ) { m_overflow = CPoint(0, 0); m_lineWidth = CSize(4, 16); }
193
194 BEGIN_MSG_MAP_EX(TSelf)
195 MESSAGE_HANDLER(WM_CAPTURECHANGED,OnCaptureChanged);
196 CHAIN_MSG_MAP(TBase)
197 END_MSG_MAP()
198
199 void MoveViewOriginDelta(CPoint p_delta) {
200 MoveViewOriginDeltaLines( MiddleDrag_PixelsToLines( m_overflow, p_delta ) );
201 }
202 void MoveViewOriginDeltaLines(CPoint p_delta) {
203 FireScrollMessage(WM_HSCROLL,p_delta.x);
204 FireScrollMessage(WM_VSCROLL,p_delta.y);
205 }
206 void SetLineWidth(CSize p_width) {m_lineWidth = p_width;}
207
208 private:
209 void FireScrollMessage(UINT p_msg,int p_delta) {
210 UINT count = (UINT)(p_delta<0?-p_delta:p_delta);
211 const UINT which = (p_msg == WM_HSCROLL ? SB_HORZ : SB_VERT);
212 SCROLLINFO si = {}; si.cbSize = sizeof(si); si.fMask = SIF_PAGE | SIF_RANGE;
213 if (!this->GetScrollInfo(which, &si) || si.nPage <= 0) return;
214 while(count >= si.nPage) {
215 const WPARAM code = p_delta < 0 ? SB_PAGEUP : SB_PAGEDOWN;
216 this->SendMessage(p_msg, code, 0);
217 count -= si.nPage;
218 }
219 const WPARAM code = p_delta < 0 ? SB_LINEUP : SB_LINEDOWN;
220 for(UINT walk = 0; walk < count; ++walk) this->SendMessage(p_msg, code, 0);
221 }
222 LRESULT OnCaptureChanged(UINT,WPARAM,LPARAM,BOOL& bHandled) {
223 m_overflow = CPoint(0,0);
224 bHandled = FALSE;
225 return 0;
226 }
227
228
229 CPoint MiddleDrag_PixelsToLines(CPoint & p_overflow,CPoint p_pixels) {
230 const CSize dpi = QueryScreenDPIEx();
231 if (dpi.cx <= 0 || dpi.cy <= 0) return CPoint(0,0);
232 CPoint pt;
233 pt.x = CMiddleDragCommon::LineToPixelsHelper(p_overflow.x,p_pixels.x,dpi.cx,m_lineWidth.cx);
234 pt.y = CMiddleDragCommon::LineToPixelsHelper(p_overflow.y,p_pixels.y,dpi.cy,m_lineWidth.cy);
235 return pt;
236 }
237
238 CSize m_lineWidth;
239
240 CPoint m_overflow;
241 };
242
243 template<typename TBase = CWindow>
244 class CWindow_DummyMsgMap : public TBase , public CMessageMap {
245 public:
246 BOOL ProcessWindowMessage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam,
247 LRESULT& lResult, DWORD dwMsgMapID = 0) {return FALSE;}
248 };
249
250 typedef CMiddleDragImpl<CMiddleDragWrapper<CWindow_DummyMsgMap<> > > CMiddleDragImplSimple;
251
252 template<typename TBase = CWindow>
253 class CContainedWindow_MsgMap : public CContainedWindowT<CWindow_DummyMsgMap<TBase> > {
254 protected:
255 CContainedWindow_MsgMap() : CContainedWindowT<CWindow_DummyMsgMap<TBase> >(msgMapCast(this)) {}
256 private:
257 template<typename t> static CMessageMap * msgMapCast(t* arg) { return arg; }
258 };
259
260 template<typename TBase>
261 class CMiddleDragImplCtrlHook : public CMiddleDragImpl<CMiddleDragWrapper<CContainedWindow_MsgMap<TBase> > > {
262 };
263
264 template<typename TBase> class CMiddleDragImplCtrlHookEx : public CMiddleDragImplCtrlHook<TBase> {
265 public:
266 CMiddleDragImplCtrlHookEx(CMessageMap * hook, DWORD hookMapID = 0) : m_hook(*hook), m_hookMapID(hookMapID) {}
267
268 BEGIN_MSG_MAP_EX(CMiddleDragImplCtrlHookEx)
269 CHAIN_MSG_MAP(CMiddleDragImplCtrlHook<TBase>)
270 CHAIN_MSG_MAP_ALT_MEMBER(m_hook,m_hookMapID);
271 END_MSG_MAP()
272 private:
273 DWORD const m_hookMapID;
274 CMessageMap & m_hook;
275 };