|
1
|
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 };
|