diff foosdk/sdk/foobar2000/helpers/WindowPositionUtils.cpp @ 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/foobar2000/helpers/WindowPositionUtils.cpp	Mon Jan 05 02:15:46 2026 -0500
@@ -0,0 +1,228 @@
+#include "StdAfx.h"
+#include "WindowPositionUtils.h"
+
+#define FB2K_WPU_DEBUG 0
+namespace {
+	static BOOL GetParentWndRect(CWindow wndParent, CRect& rc) {
+		if (!wndParent.IsIconic()) {
+			return wndParent.GetWindowRect(rc);
+		}
+		WINDOWPLACEMENT pl = { sizeof(pl) };
+		if (!wndParent.GetWindowPlacement(&pl)) return FALSE;
+		rc = pl.rcNormalPosition;
+		return TRUE;
+	}
+
+	struct DeOverlapState {
+		CWindow m_thisWnd;
+		CPoint m_topLeft;
+		bool m_match;
+	};
+	static BOOL CALLBACK MyEnumChildProc(HWND wnd, LPARAM param) {
+		DeOverlapState* state = reinterpret_cast<DeOverlapState*>(param);
+		if (wnd != state->m_thisWnd && IsWindowVisible(wnd)) {
+			CRect rc;
+			if (GetWindowRect(wnd, rc)) {
+				if (rc.TopLeft() == state->m_topLeft) {
+					state->m_match = true; return FALSE;
+				}
+			}
+		}
+		return TRUE;
+	}
+	static bool DeOverlapTest(CWindow wnd, CPoint topLeft) {
+		DeOverlapState state = {};
+		state.m_thisWnd = wnd; state.m_topLeft = topLeft; state.m_match = false;
+		EnumThreadWindows(GetCurrentThreadId(), MyEnumChildProc, reinterpret_cast<LPARAM>(&state));
+		return state.m_match;
+	}
+	static int DeOverlapDelta() {
+		return pfc::max_t<int>(GetSystemMetrics(SM_CYCAPTION), 1);
+	}
+	static void DeOverlap(CWindow wnd, CRect& rc) {
+		const int delta = DeOverlapDelta();
+		for (;;) {
+			if (!DeOverlapTest(wnd, rc.TopLeft())) break;
+			rc.OffsetRect(delta, delta);
+		}
+	}
+}
+
+bool cfgDialogPositionData::grabFrom(CWindow wnd) {
+	CRect rc;
+	if (!GetClientRectAsSC(wnd, rc)) {
+		return false;
+	}
+	const CSize DPI = QueryScreenDPIEx(wnd);
+	m_dpiX = DPI.cx; m_dpiY = DPI.cy;
+	m_width = rc.Width(); m_height = rc.Height();
+	m_posX = m_posY = posInvalid;
+	CWindow parent = wnd.GetParent();
+	if (parent != NULL) {
+		CRect rcParent;
+		if (GetParentWndRect(parent, rcParent)) {
+			m_posX = rc.left - rcParent.left;
+			m_posY = rc.top - rcParent.top;
+		}
+	} else {
+		m_posX = rc.left; m_posY = rc.top;
+	}
+	return true;
+}
+
+pfc::string8 cfgDialogPositionData::debug() const {
+	pfc::string_formatter ret;
+	if (m_width != sizeInvalid) ret << "W: " << m_width << "\n";
+	if (m_height != sizeInvalid) ret << "H: " << m_height << "\n";
+	if (m_posX != posInvalid) ret << "X: " << m_posX << "\n";
+	if (m_posY != posInvalid) ret << "Y: " << m_posY << "\n";
+	if (m_dpiX != dpiInvalid) ret << "DPI-X: " << m_dpiX << "\n";
+	if (m_dpiY != dpiInvalid) ret << "DPI-Y: " << m_dpiY << "\n";	
+	return ret;
+}
+
+cfgDialogPositionData cfgDialogPositionData::reDPI( CSize screenDPI ) const {
+	cfgDialogPositionData v = *this;
+	if (screenDPI.cx == 0 || screenDPI.cy == 0) {
+		PFC_ASSERT(!"Should not get here - something seriously wrong with the OS");
+		return v;
+	}
+	if (v.m_dpiX != dpiInvalid && v.m_dpiX != screenDPI.cx) {
+		if (v.m_width != sizeInvalid) v.m_width = MulDiv(v.m_width, screenDPI.cx, v.m_dpiX);
+		if (v.m_posX != posInvalid) v.m_posX = MulDiv(v.m_posX, screenDPI.cx, v.m_dpiX);
+	}
+	if (v.m_dpiY != dpiInvalid && v.m_dpiY != screenDPI.cy) {
+		if (v.m_height != sizeInvalid) v.m_height = MulDiv(v.m_height, screenDPI.cy, v.m_dpiY);
+		if (v.m_posY != posInvalid) v.m_posY = MulDiv(v.m_posY, screenDPI.cy, v.m_dpiY);
+	}
+	v.m_dpiX = screenDPI.cx;
+	v.m_dpiY = screenDPI.cy;
+	return v;
+}
+
+
+bool cfgDialogPositionData::overrideDefaultSize(t_uint32 width, t_uint32 height) {
+	bool rv = false;
+	if (m_width == sizeInvalid && m_height == sizeInvalid) {
+		m_width = width; m_height = height; m_posX = m_posY = posInvalid;
+		m_dpiX = m_dpiY = 96;
+		rv = true;
+	}
+	return rv;
+}
+
+bool cfgDialogPositionData::applyTo(CWindow wnd) const {
+#if FB2K_WPU_DEBUG
+	FB2K_console_formatter() << "cfgDialogPositionData::applyTo(0x" << pfc::format_window( wnd ) << ")";
+	FB2K_console_formatter() << "data:\n" << this->debug();
+#endif
+	const auto v = reDPI(QueryScreenDPIEx(wnd));
+#if FB2K_WPU_DEBUG
+	FB2K_console_formatter() << "after reDPI:\n" << v.debug();
+#endif
+	CWindow wndParent = wnd.GetParent();
+	UINT flags = SWP_NOACTIVATE | SWP_NOZORDER;
+	CRect rc;
+	if (!GetClientRectAsSC(wnd, rc)) return false;
+	if (v.m_width != v.sizeInvalid && v.m_height != v.sizeInvalid && (wnd.GetWindowLong(GWL_STYLE) & WS_SIZEBOX) != 0) {
+		rc.right = rc.left + v.m_width;
+		rc.bottom = rc.top + v.m_height;
+	} else {
+		flags |= SWP_NOSIZE;
+	}
+	if (wndParent != NULL) {
+		CRect rcParent;
+		if (GetParentWndRect(wndParent, rcParent)) {
+			if (v.m_posX != v.posInvalid && v.m_posY != v.posInvalid) {
+				rc.MoveToXY(rcParent.TopLeft() + CPoint(v.m_posX, v.m_posY));
+			} else {
+				CPoint center = rcParent.CenterPoint();
+				rc.MoveToXY(center.x - rc.Width() / 2, center.y - rc.Height() / 2);
+			}
+		}
+	} else {
+		if (v.m_posX != v.posInvalid && v.m_posY != v.posInvalid ) {
+			rc.MoveToXY( v.m_posX, v.m_posY );
+		}
+	}
+	if (!AdjustWindowRectHelper(wnd, rc)) return FALSE;
+
+	DeOverlap(wnd, rc);
+
+	{
+		CRect rcAdjust(0, 0, 1, 1);
+		if (wndParent != NULL) {
+			CRect temp;
+			if (wndParent.GetWindowRect(temp)) rcAdjust = temp;
+		}
+		AdjustRectToScreenArea(rc, rcAdjust);
+	}
+
+
+	return wnd.SetWindowPos(NULL, rc, flags);
+}
+
+void cfgDialogPosition::read_from_window(HWND wnd) {
+	cfgDialogPositionData data;
+	if (data.grabFrom(wnd)) this->set(data);
+}
+
+bool cfgDialogPosition::apply_to_window(HWND wnd) {
+	auto data = this->get();
+	return data.applyTo(wnd);
+}
+
+bool cfgWindowPositionData::grabFrom(CWindow wnd) {
+	WINDOWPLACEMENT wp = {sizeof(wp)};
+	bool rv = !! wnd.GetWindowPlacement(&wp);
+	if (rv) {
+		if ( !wnd.IsWindowVisible() ) wp.showCmd = SW_HIDE;
+		this->m_wp = wp;
+		m_dpi = QueryScreenDPIEx(wnd);
+		PFC_ASSERT( m_dpi.cx > 0 && m_dpi.cy > 0 );
+	}
+	return rv;
+}
+
+static void reDPI(WINDOWPLACEMENT& wp, SIZE from, SIZE to) {
+	wp.rcNormalPosition.left = MulDiv(wp.rcNormalPosition.left, to.cx, from.cx);
+	wp.rcNormalPosition.right = MulDiv(wp.rcNormalPosition.right, to.cx, from.cx);
+	wp.rcNormalPosition.top = MulDiv(wp.rcNormalPosition.top, to.cy, from.cy);
+	wp.rcNormalPosition.bottom = MulDiv(wp.rcNormalPosition.bottom, to.cy, from.cy);
+}
+
+bool applyWindowPlacement(HWND window, WINDOWPLACEMENT const& data, bool allowHidden); // window_placement_helper.cpp
+
+bool cfgWindowPositionData::applyTo(CWindow wnd, bool allowHidden) const {
+	WINDOWPLACEMENT wp = m_wp;
+	if ( wp.length == 0 ) return false;
+	auto dpi = QueryScreenDPIEx(wnd);
+	if (dpi.cx != m_dpi.cx || dpi.cy != m_dpi.cy) {
+		reDPI( wp, m_dpi, dpi );
+	}
+	return applyWindowPlacement(wnd, wp, allowHidden);
+}
+
+void cfgWindowPosition::read_from_window(HWND wnd) {
+	// grabFrom might work partially, fail to obtain size due to window being hidden, use last values
+	cfgWindowPositionData data = get();
+	if ( data.grabFrom( wnd ) ) set(data);
+}
+
+bool cfgWindowPosition::apply_to_window(HWND wnd, bool allowHidden) {
+	auto data = get();
+	return data.applyTo( wnd, allowHidden );
+}
+
+void cfgWindowPosition::windowCreated(HWND wnd, bool allowHidden, DWORD showHow) {
+	auto data = get();
+	switch (showHow) {
+	case SW_HIDE:
+	case SW_MINIMIZE:
+		data.m_wp.showCmd = showHow;
+		break;
+	}
+	if (!data.applyTo(wnd, allowHidden)) {
+		::ShowWindow( wnd, showHow);
+	}
+}
\ No newline at end of file