Mercurial > foo_out_sdl
diff 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 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/foosdk/sdk/libPPUI/CMiddleDragImpl.h Mon Jan 05 02:15:46 2026 -0500 @@ -0,0 +1,275 @@ +#pragma once + +#include "CMiddleDragOverlay.h" +#include <utility> + +class CMiddleDragCommon { +public: + enum { + KTimerID = 0x389675f8, + KTimerPeriod = 15, + }; + static double myPow(double p_val, double p_exp); + static double ProcessMiddleDragDeltaInternal(double p_delta); + static double radiusHelper(double p_x, double p_y); + static int mySGN(LONG v); + static int32_t Round(double val, double & acc); + static LONG LineToPixelsHelper(LONG & p_overflow, LONG p_pixels, LONG p_dpi, LONG p_lineWidth); +}; + +template<typename TBase> +class CMiddleDragImpl : public TBase, protected CMiddleDragCommon { +private: + typedef CMiddleDragImpl<TBase> TSelf; +public: + template<typename ... arg_t> CMiddleDragImpl( arg_t && ... arg ) : TBase(std::forward<arg_t>(arg) ... ) {} + + BEGIN_MSG_MAP_EX(TSelf) + MESSAGE_HANDLER(WM_TIMER,OnTimer); + MESSAGE_HANDLER(WM_CAPTURECHANGED,OnCaptureChanged); + MESSAGE_HANDLER(WM_MBUTTONDOWN,OnMButtonDown); + MESSAGE_HANDLER(WM_MBUTTONDBLCLK,OnMButtonDown); + MESSAGE_HANDLER(WM_MBUTTONUP,OnMButtonUp); + MESSAGE_HANDLER(WM_LBUTTONDOWN,OnMouseAction); + MESSAGE_HANDLER(WM_RBUTTONDOWN,OnMouseAction); + MESSAGE_HANDLER(WM_LBUTTONDBLCLK,OnMouseAction); + MESSAGE_HANDLER(WM_RBUTTONDBLCLK,OnMouseAction); + MESSAGE_HANDLER(WM_LBUTTONUP,OnMouseAction); + MESSAGE_HANDLER(WM_RBUTTONUP,OnMouseAction); + MESSAGE_HANDLER(WM_XBUTTONDOWN,OnMouseAction); + MESSAGE_HANDLER(WM_XBUTTONDBLCLK,OnMouseAction); + MESSAGE_HANDLER(WM_XBUTTONUP,OnMouseAction); + MESSAGE_HANDLER(WM_MOUSEMOVE,OnMouseMove); + MESSAGE_HANDLER(WM_DESTROY,OnDestroyPassThru); + CHAIN_MSG_MAP(TBase); + END_MSG_MAP() +protected: + bool CanSmoothScroll() const { + return !m_active; + } +private: + bool m_active = false, m_dragged = false; + CPoint m_base; + CMiddleDragOverlay m_overlay; + double m_accX = 0, m_accY = 0; + + LRESULT OnDestroyPassThru(UINT,WPARAM,LPARAM,BOOL& bHandled) { + if (m_overlay != NULL) m_overlay.DestroyWindow(); + bHandled = FALSE; + return 0; + } + + LRESULT OnMButtonUp(UINT,WPARAM,LPARAM,BOOL&) { + if (m_active /*&& m_dragged*/) { + EndDrag(); + } + return 0; + } + + LRESULT OnMButtonDown(UINT,WPARAM,LPARAM p_lp,BOOL&) { + if (m_active) { + EndDrag(); + return 0; + } + EndDrag(); + this->SetFocus(); + CPoint pt(p_lp); + WIN32_OP_D( this->ClientToScreen(&pt) ); + StartDrag(pt); + return 0; + } + + LRESULT OnMouseMove(UINT,WPARAM,LPARAM p_lp,BOOL& bHandled) { + if (m_active) { + if (!m_dragged) { + CPoint pt(p_lp); + WIN32_OP_D( this->ClientToScreen(&pt) ); + if (pt != m_base) { + m_dragged = true; + } + } + bHandled = TRUE; + } else { + bHandled = FALSE; + } + return 0; + } + + LRESULT OnMouseAction(UINT,WPARAM,LPARAM,BOOL& bHandled) { + EndDrag(); + bHandled = FALSE; + return 0; + } + + void StartDrag(CPoint const & p_point) { + ::SetCapture(NULL); + + if (m_overlay == NULL) { + PFC_ASSERT( this->m_hWnd != NULL ); + WIN32_OP_D(m_overlay.Create(*this)); + } + + //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 + SetCursor(::LoadCursor(NULL,IDC_SIZEALL)); + m_active = true; + m_dragged = false; + m_base = p_point; + m_accX = m_accY = 0; + this->SetCapture(); + this->SetTimer(KTimerID,KTimerPeriod); + + m_overlay.ShowHere(p_point); + } + + void EndDrag() { + if (m_active) ::SetCapture(NULL); + } + + void HandleEndDrag() { + if (m_active) { + m_active = false; + this->KillTimer(KTimerID); + if (m_overlay != NULL) m_overlay.ShowWindow(SW_HIDE); + } + } + + LRESULT OnCaptureChanged(UINT,WPARAM,LPARAM,BOOL& bHandled) { + HandleEndDrag(); + bHandled = FALSE; + return 0; + } + + LRESULT OnTimer(UINT,WPARAM p_wp,LPARAM,BOOL& bHandled) { + switch(p_wp) { + case KTimerID: + { + CPoint ptCursor; + if (GetCursorPos(&ptCursor)) { + this->MoveViewOriginDelta(ProcessMiddleDragDelta(ptCursor - m_base)); + } + } + return 0; + default: + bHandled = FALSE; + return 0; + } + } + + CPoint ProcessMiddleDragDelta(CPoint p_delta) { + const CSize dpi = QueryScreenDPIEx(); + if (dpi.cx <= 0 || dpi.cy <= 0 || p_delta == CPoint(0, 0)) return CPoint(0, 0); + const double dpiMulX = 96.0 / (double)dpi.cx; + const double dpiMulY = 96.0 / (double)dpi.cy; + + const double deltaTotal = ProcessMiddleDragDeltaInternal(radiusHelper((double)p_delta.x * dpiMulX, (double)p_delta.y * dpiMulY)); + + double xVal = 0, yVal = 0; + + if (p_delta.x == 0) { + yVal = deltaTotal; + } else if (p_delta.y == 0) { + xVal = deltaTotal; + } else { + const double ratio = (double)p_delta.x / (double)p_delta.y; + yVal = sqrt((deltaTotal*deltaTotal) / (1.0 + (ratio*ratio))); + xVal = yVal * ratio; + } + + xVal = mySGN(p_delta.x) * fabs(xVal); + yVal = mySGN(p_delta.y) * fabs(yVal); + + + return CPoint(Round(xVal / dpiMulX, m_accX), Round(yVal / dpiMulY, m_accY)); + } + +}; + +template<typename TBase> +class CMiddleDragWrapper : public TBase { +private: + typedef CMiddleDragWrapper<TBase> TSelf; +public: + template<typename ... arg_t> CMiddleDragWrapper(arg_t && ... arg ) : TBase(std::forward<arg_t>(arg) ... ) { m_overflow = CPoint(0, 0); m_lineWidth = CSize(4, 16); } + + BEGIN_MSG_MAP_EX(TSelf) + MESSAGE_HANDLER(WM_CAPTURECHANGED,OnCaptureChanged); + CHAIN_MSG_MAP(TBase) + END_MSG_MAP() + + void MoveViewOriginDelta(CPoint p_delta) { + MoveViewOriginDeltaLines( MiddleDrag_PixelsToLines( m_overflow, p_delta ) ); + } + void MoveViewOriginDeltaLines(CPoint p_delta) { + FireScrollMessage(WM_HSCROLL,p_delta.x); + FireScrollMessage(WM_VSCROLL,p_delta.y); + } + void SetLineWidth(CSize p_width) {m_lineWidth = p_width;} + +private: + void FireScrollMessage(UINT p_msg,int p_delta) { + UINT count = (UINT)(p_delta<0?-p_delta:p_delta); + const UINT which = (p_msg == WM_HSCROLL ? SB_HORZ : SB_VERT); + SCROLLINFO si = {}; si.cbSize = sizeof(si); si.fMask = SIF_PAGE | SIF_RANGE; + if (!this->GetScrollInfo(which, &si) || si.nPage <= 0) return; + while(count >= si.nPage) { + const WPARAM code = p_delta < 0 ? SB_PAGEUP : SB_PAGEDOWN; + this->SendMessage(p_msg, code, 0); + count -= si.nPage; + } + const WPARAM code = p_delta < 0 ? SB_LINEUP : SB_LINEDOWN; + for(UINT walk = 0; walk < count; ++walk) this->SendMessage(p_msg, code, 0); + } + LRESULT OnCaptureChanged(UINT,WPARAM,LPARAM,BOOL& bHandled) { + m_overflow = CPoint(0,0); + bHandled = FALSE; + return 0; + } + + + CPoint MiddleDrag_PixelsToLines(CPoint & p_overflow,CPoint p_pixels) { + const CSize dpi = QueryScreenDPIEx(); + if (dpi.cx <= 0 || dpi.cy <= 0) return CPoint(0,0); + CPoint pt; + pt.x = CMiddleDragCommon::LineToPixelsHelper(p_overflow.x,p_pixels.x,dpi.cx,m_lineWidth.cx); + pt.y = CMiddleDragCommon::LineToPixelsHelper(p_overflow.y,p_pixels.y,dpi.cy,m_lineWidth.cy); + return pt; + } + + CSize m_lineWidth; + + CPoint m_overflow; +}; + +template<typename TBase = CWindow> +class CWindow_DummyMsgMap : public TBase , public CMessageMap { +public: + BOOL ProcessWindowMessage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, + LRESULT& lResult, DWORD dwMsgMapID = 0) {return FALSE;} +}; + +typedef CMiddleDragImpl<CMiddleDragWrapper<CWindow_DummyMsgMap<> > > CMiddleDragImplSimple; + +template<typename TBase = CWindow> +class CContainedWindow_MsgMap : public CContainedWindowT<CWindow_DummyMsgMap<TBase> > { +protected: + CContainedWindow_MsgMap() : CContainedWindowT<CWindow_DummyMsgMap<TBase> >(msgMapCast(this)) {} +private: + template<typename t> static CMessageMap * msgMapCast(t* arg) { return arg; } +}; + +template<typename TBase> +class CMiddleDragImplCtrlHook : public CMiddleDragImpl<CMiddleDragWrapper<CContainedWindow_MsgMap<TBase> > > { +}; + +template<typename TBase> class CMiddleDragImplCtrlHookEx : public CMiddleDragImplCtrlHook<TBase> { +public: + CMiddleDragImplCtrlHookEx(CMessageMap * hook, DWORD hookMapID = 0) : m_hook(*hook), m_hookMapID(hookMapID) {} + + BEGIN_MSG_MAP_EX(CMiddleDragImplCtrlHookEx) + CHAIN_MSG_MAP(CMiddleDragImplCtrlHook<TBase>) + CHAIN_MSG_MAP_ALT_MEMBER(m_hook,m_hookMapID); + END_MSG_MAP() +private: + DWORD const m_hookMapID; + CMessageMap & m_hook; +};
