Mercurial > foo_out_sdl
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 }; |
