diff --git a/README.md b/README.md
index 1aea4549..86a83fa7 100644
--- a/README.md
+++ b/README.md
@@ -20,6 +20,7 @@ This README is being written.
* **15 June 2016**
* Added `uiFormDelete()`; thanks to @emersion.
+ * Added `uiWindowPosition()`, `uiWindowSetPosition()`, `uiWindowCenter()`, and `uiWindowOnPositionChanged()`, methods for manipulating uiWindow position.
* **14 June 2016**
* uiDarwinControl now has a `ChildVisibilityChanged()` method and a corresponding `NotifyVisibilityChanged()` function that is called by the default show/hide handlers. This is used to make visibility changes work on OS X; uiBox, uiForm, and uiGrid all respect these now.
diff --git a/windows/window.cpp b/windows/window.cpp
index 411cb86e..f753d93c 100644
--- a/windows/window.cpp
+++ b/windows/window.cpp
@@ -14,6 +14,9 @@ struct uiWindow {
void *onClosingData;
int margined;
BOOL hasMenubar;
+ void (*onPositionChanged)(uiWindow *, void *);
+ void *onPositionChangedData;
+ BOOL changingPosition;
};
// from https://msdn.microsoft.com/en-us/library/windows/desktop/dn742486.aspx#sizingandspacing
@@ -87,6 +90,10 @@ static LRESULT CALLBACK windowWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARA
runMenuEvent(LOWORD(wParam), uiWindow(w));
return 0;
case WM_WINDOWPOSCHANGED:
+ if ((wp->flags & SWP_NOMOVE) == 0)
+ if (!w->changingPosition)
+ (*(w->onPositionChanged))(w, w->onPositionChangedData);
+ // and continue anyway
if ((wp->flags & SWP_NOSIZE) != 0)
break;
windowRelayout(w);
@@ -138,6 +145,11 @@ static int defaultOnClosing(uiWindow *w, void *data)
return 0;
}
+static void defaultOnPositionChanged(uiWindow *w, void *data)
+{
+ // do nothing
+}
+
static std::map windows;
static void uiWindowDestroy(uiControl *c)
@@ -277,6 +289,74 @@ void uiWindowSetTitle(uiWindow *w, const char *title)
// don't queue resize; the caption isn't part of what affects layout and sizing of the client area (it'll be ellipsized if too long)
}
+void uiWindowPosition(uiWindow *w, int *x, int *y)
+{
+ RECT r;
+
+ uiWindowsEnsureGetWindowRect(w->hwnd, &r);
+ *x = r.left;
+ *y = r.top;
+}
+
+void uiWindowSetPosition(uiWindow *w, int x, int y)
+{
+ w->changingPosition = TRUE;
+ if (SetWindowPos(w->hwnd, NULL, x, y, 0, 0, SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOSIZE | SWP_NOZORDER) == 0)
+ logLastError(L"error moving window");
+ w->changingPosition = FALSE;
+}
+
+// this is used for both fullscreening and centering
+// see also https://blogs.msdn.microsoft.com/oldnewthing/20100412-00/?p=14353 and https://blogs.msdn.microsoft.com/oldnewthing/20050505-04/?p=35703
+static void windowMonitorRect(HWND hwnd, RECT *r)
+{
+ HMONITOR monitor;
+ MONITORINFO mi;
+
+ monitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTOPRIMARY);
+ ZeroMemory(&mi, sizeof (MONITORINFO));
+ mi.cbSize = sizeof (MONITORINFO);
+ if (GetMonitorInfoW(monitor, &mi) == 0) {
+ logLastError(L"error getting window monitor rect");
+ // default to SM_CXSCREEN x SM_CYSCREEN to be safe
+ r->left = 0;
+ r->top = 0;
+ r->right = GetSystemMetrics(SM_CXSCREEN);
+ r->bottom = GetSystemMetrics(SM_CYSCREEN);
+ return;
+ }
+ *r = mi.rcMonitor;
+}
+
+// TODO use the work rect instead?
+void uiWindowCenter(uiWindow *w)
+{
+ RECT wr, mr;
+ int x, y;
+ LONG wwid, mwid;
+ LONG wht, mht;
+
+ uiWindowsEnsureGetWindowRect(w->hwnd, &wr);
+ windowMonitorRect(w->hwnd, &mr);
+ wwid = wr.right - wr.left;
+ mwid = mr.right - mr.left;
+ x = (mwid - wwid) / 2;
+ wht = wr.bottom - wr.top;
+ mht = mr.bottom - mr.top;
+ y = (mht - wht) / 2;
+ // y is now evenly divided, however https://msdn.microsoft.com/en-us/library/windows/desktop/dn742502(v=vs.85).aspx says that 45% should go above and 55% should go below
+ // so just move 5% of the way up
+ // TODO is this correct?
+ y -= y / 20;
+ uiWindowSetPosition(w, x, y);
+}
+
+void uiWindowOnPositionChanged(uiWindow *w, void (*f)(uiWindow *, void *), void *data)
+{
+ w->onPositionChanged = f;
+ w->onPositionChangedData = data;
+}
+
void uiWindowOnClosing(uiWindow *w, int (*f)(uiWindow *, void *), void *data)
{
w->onClosing = f;
@@ -373,6 +453,7 @@ uiWindow *uiNewWindow(const char *title, int width, int height, int hasMenubar)
setClientSize(w, width, height, hasMenubarBOOL, style, exstyle);
uiWindowOnClosing(w, defaultOnClosing, NULL);
+ uiWindowOnPositionChanged(w, defaultOnPositionChanged, NULL);
windows[w] = true;
return w;