diff --git a/README.md b/README.md index 1aea4549..4b615c8e 100644 --- a/README.md +++ b/README.md @@ -16,10 +16,14 @@ This README is being written.
## Updates -*Note that today's entry may be updated later today Eastern Time.* +*Note that today's entry (Eastern Time) may be updated later today.* + +* **16 June 2016** + * Added `uiWindowContentSize()`, `uiWindowSetContentSize()`, and `uiWindowOnContentSizeChanged()` methods for manipulating uiWindow content sizes. Note the use of "content size"; the size you work with does NOT include window decorations (titlebars, menus, etc.). * **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/darwin/window.m b/darwin/window.m index ffb6b30d..03c86ac1 100644 --- a/darwin/window.m +++ b/darwin/window.m @@ -9,12 +9,20 @@ struct uiWindow { int (*onClosing)(uiWindow *, void *); void *onClosingData; struct singleChildConstraints constraints; + void (*onPositionChanged)(uiWindow *, void *); + void *onPositionChangedData; + BOOL suppressPositionChanged; + void (*onContentSizeChanged)(uiWindow *, void *); + void *onContentSizeChangedData; + BOOL suppressSizeChanged; }; @interface windowDelegateClass : NSObject { struct mapTable *windows; } - (BOOL)windowShouldClose:(id)sender; +- (void)windowDidMove:(NSNotification *)note; +- (void)windowDidResize:(NSNotification *)note; - (void)registerWindow:(uiWindow *)w; - (void)unregisterWindow:(uiWindow *)w; - (uiWindow *)lookupWindow:(NSWindow *)w; @@ -47,6 +55,25 @@ struct uiWindow { return NO; } +// TODO doesn't happen live +- (void)windowDidMove:(NSNotification *)note +{ + uiWindow *w; + + w = [self lookupWindow:((NSWindow *) [note object])]; + if (!w->suppressPositionChanged) + (*(w->onPositionChanged))(w, w->onPositionChangedData); +} + +- (void)windowDidResize:(NSNotification *)note +{ + uiWindow *w; + + w = [self lookupWindow:((NSWindow *) [note object])]; + if (!w->suppressSizeChanged) + (*(w->onContentSizeChanged))(w, w->onContentSizeChangedData); +} + - (void)registerWindow:(uiWindow *)w { mapSet(self->windows, w->window, w); @@ -204,6 +231,71 @@ void uiWindowSetTitle(uiWindow *w, const char *title) [w->window setTitle:toNSString(title)]; } +void uiWindowPosition(uiWindow *w, int *x, int *y) +{ + NSScreen *screen; + NSRect r; + + r = [w->window frame]; + *x = r.origin.x; + // this is the right screen to use; thanks mikeash in irc.freenode.net/#macdev + // -mainScreen is useless for positioning (it's just the key window's screen) + // and we use -frame, not -visibleFrame, for dealing with absolute positions + screen = (NSScreen *) [[NSScreen screens] objectAtIndex:0]; + *y = ([screen frame].size.height - r.origin.y) - r.size.height; +} + +void uiWindowSetPosition(uiWindow *w, int x, int y) +{ + // -[NSWindow setFrameTopLeftPoint:] is acting weird so... + NSRect r; + NSScreen *screen; + + // this fires windowDidMove: + w->suppressPositionChanged = YES; + r = [w->window frame]; + r.origin.x = x; + screen = (NSScreen *) [[NSScreen screens] objectAtIndex:0]; + r.origin.y = [screen frame].size.height - (y + r.size.height); + [w->window setFrameOrigin:r.origin]; + w->suppressPositionChanged = NO; +} + +void uiWindowCenter(uiWindow *w) +{ + w->suppressPositionChanged = YES; + [w->window center]; + w->suppressPositionChanged = NO; +} + +void uiWindowOnPositionChanged(uiWindow *w, void (*f)(uiWindow *, void *), void *data) +{ + w->onPositionChanged = f; + w->onPositionChangedData = data; +} + +void uiWindowContentSize(uiWindow *w, int *width, int *height) +{ + NSRect r; + + r = [w->window contentRectForFrameRect:[w->window frame]]; + *width = r.size.width; + *height = r.size.height; +} + +void uiWindowSetContentSize(uiWindow *w, int width, int height) +{ + w->suppressSizeChanged = YES; + [w->window setContentSize:NSMakeSize(width, height)]; + w->suppressSizeChanged = NO; +} + +void uiWindowOnContentSizeChanged(uiWindow *w, void (*f)(uiWindow *, void *), void *data) +{ + w->onContentSizeChanged = f; + w->onContentSizeChangedData = data; +} + void uiWindowOnClosing(uiWindow *w, int (*f)(uiWindow *, void *), void *data) { w->onClosing = f; @@ -245,6 +337,11 @@ static int defaultOnClosing(uiWindow *w, void *data) return 0; } +static void defaultOnPositionContentSizeChanged(uiWindow *w, void *data) +{ + // do nothing +} + uiWindow *uiNewWindow(const char *title, int width, int height, int hasMenubar) { uiWindow *w; @@ -269,6 +366,8 @@ uiWindow *uiNewWindow(const char *title, int width, int height, int hasMenubar) } [windowDelegate registerWindow:w]; uiWindowOnClosing(w, defaultOnClosing, NULL); + uiWindowOnPositionChanged(w, defaultOnPositionContentSizeChanged, NULL); + uiWindowOnContentSizeChanged(w, defaultOnPositionContentSizeChanged, NULL); return w; } diff --git a/doc/windowmovesize b/doc/windowmovesize new file mode 100644 index 00000000..ec8bd966 --- /dev/null +++ b/doc/windowmovesize @@ -0,0 +1,3 @@ +you should never need to use these functions +they are provided only for the cases when ABSOLUTELY NECESSARY +the operating system may ignore your requests, for instance, if you are giving it invalid numbers or a size too small to fit; this is all system-defined diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index c6511e80..e4924bb3 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -25,6 +25,7 @@ _add_exec(tester page12.c page13.c page14.c + page15.c spaced.c ${_TEST_RESOURCES_RC} ) diff --git a/test/main.c b/test/main.c index 63f21b86..ae3e8af7 100644 --- a/test/main.c +++ b/test/main.c @@ -49,6 +49,7 @@ int main(int argc, char *argv[]) uiBox *page6, *page7, *page8, *page9, *page10; uiBox *page11, *page12, *page13; uiTab *page14; + uiBox *page15; uiTab *outerTab; uiTab *innerTab; int nomenus = 0; @@ -148,6 +149,9 @@ int main(int argc, char *argv[]) page14 = makePage14(); uiTabAppend(innerTab, "Page 14", uiControl(page14)); + page15 = makePage15(w); + uiTabAppend(innerTab, "Page 15", uiControl(page15)); + if (startspaced) setSpaced(1); diff --git a/test/page15.c b/test/page15.c new file mode 100644 index 00000000..618ac709 --- /dev/null +++ b/test/page15.c @@ -0,0 +1,129 @@ +// 15 june 2016 +#include "test.h" + +static uiSpinbox *x, *y; +static uiSpinbox *width, *height; + +static void moveX(uiSpinbox *s, void *data) +{ + uiWindow *w = uiWindow(data); + int xp, yp; + + uiWindowPosition(w, &xp, &yp); + xp = uiSpinboxValue(x); + uiWindowSetPosition(w, xp, yp); +} + +static void moveY(uiSpinbox *s, void *data) +{ + uiWindow *w = uiWindow(data); + int xp, yp; + + uiWindowPosition(w, &xp, &yp); + yp = uiSpinboxValue(y); + uiWindowSetPosition(w, xp, yp); +} + +static void updatepos(uiWindow *w) +{ + int xp, yp; + + uiWindowPosition(w, &xp, &yp); + uiSpinboxSetValue(x, xp); + uiSpinboxSetValue(y, yp); +} + +static void center(uiButton *b, void *data) +{ + uiWindow *w = uiWindow(data); + + uiWindowCenter(w); + updatepos(w); +} + +void onMove(uiWindow *w, void *data) +{ + printf("move\n"); + updatepos(w); +} + +static void sizeWidth(uiSpinbox *s, void *data) +{ + uiWindow *w = uiWindow(data); + int xp, yp; + + uiWindowContentSize(w, &xp, &yp); + xp = uiSpinboxValue(width); + uiWindowSetContentSize(w, xp, yp); +} + +static void sizeHeight(uiSpinbox *s, void *data) +{ + uiWindow *w = uiWindow(data); + int xp, yp; + + uiWindowContentSize(w, &xp, &yp); + yp = uiSpinboxValue(height); + uiWindowSetContentSize(w, xp, yp); +} + +static void updatesize(uiWindow *w) +{ + int xp, yp; + + uiWindowContentSize(w, &xp, &yp); + uiSpinboxSetValue(width, xp); + uiSpinboxSetValue(height, yp); +} + +void onSize(uiWindow *w, void *data) +{ + printf("size\n"); + updatesize(w); +} + +uiBox *makePage15(uiWindow *w) +{ + uiBox *page15; + uiBox *hbox; + uiButton *button; + + page15 = newVerticalBox(); + + hbox = newHorizontalBox(); + // TODO if I make this 1 and not add anything else AND not call uiWindowOnPositionChanged(), on OS X the box won't be able to grow vertically + uiBoxAppend(page15, uiControl(hbox), 0); + + uiBoxAppend(hbox, uiControl(uiNewLabel("Position")), 0); + x = uiNewSpinbox(INT_MIN, INT_MAX); + uiBoxAppend(hbox, uiControl(x), 1); + y = uiNewSpinbox(INT_MIN, INT_MAX); + uiBoxAppend(hbox, uiControl(y), 1); + button = uiNewButton("Center"); + uiBoxAppend(hbox, uiControl(button), 0); + + uiSpinboxOnChanged(x, moveX, w); + uiSpinboxOnChanged(y, moveY, w); + uiButtonOnClicked(button, center, w); + uiWindowOnPositionChanged(w, onMove, NULL); + updatepos(w); + + hbox = newHorizontalBox(); + uiBoxAppend(page15, uiControl(hbox), 0); + + uiBoxAppend(hbox, uiControl(uiNewLabel("Size")), 0); + width = uiNewSpinbox(INT_MIN, INT_MAX); + uiBoxAppend(hbox, uiControl(width), 1); + height = uiNewSpinbox(INT_MIN, INT_MAX); + uiBoxAppend(hbox, uiControl(height), 1); +// button = uiNewButton("Center"); +// uiBoxAppend(hbox, uiControl(button), 0); + + uiSpinboxOnChanged(width, sizeWidth, w); + uiSpinboxOnChanged(height, sizeHeight, w); +// uiButtonOnClicked(button, center, w); + uiWindowOnContentSizeChanged(w, onSize, NULL); + updatesize(w); + + return page15; +} diff --git a/test/test.h b/test/test.h index e1b16299..66b1baa7 100644 --- a/test/test.h +++ b/test/test.h @@ -5,6 +5,7 @@ #include #include #include +#include #include "../ui.h" // main.c @@ -85,3 +86,6 @@ extern uiBox *makePage13(void); // page14.c extern uiTab *makePage14(void); + +// page15.c +extern uiBox *makePage15(uiWindow *); diff --git a/ui.h b/ui.h index 85c2d302..1ab01e91 100644 --- a/ui.h +++ b/ui.h @@ -99,6 +99,13 @@ typedef struct uiWindow uiWindow; #define uiWindow(this) ((uiWindow *) (this)) _UI_EXTERN char *uiWindowTitle(uiWindow *w); _UI_EXTERN void uiWindowSetTitle(uiWindow *w, const char *title); +_UI_EXTERN void uiWindowPosition(uiWindow *w, int *x, int *y); +_UI_EXTERN void uiWindowSetPosition(uiWindow *w, int x, int y); +_UI_EXTERN void uiWindowCenter(uiWindow *w); +_UI_EXTERN void uiWindowOnPositionChanged(uiWindow *w, void (*f)(uiWindow *, void *), void *data); +_UI_EXTERN void uiWindowContentSize(uiWindow *w, int *width, int *height); +_UI_EXTERN void uiWindowSetContentSize(uiWindow *w, int width, int height); +_UI_EXTERN void uiWindowOnContentSizeChanged(uiWindow *w, void (*f)(uiWindow *, void *), void *data); _UI_EXTERN void uiWindowOnClosing(uiWindow *w, int (*f)(uiWindow *w, void *data), void *data); _UI_EXTERN void uiWindowSetChild(uiWindow *w, uiControl *child); _UI_EXTERN int uiWindowMargined(uiWindow *w); diff --git a/unix/window.c b/unix/window.c index cca767e8..f0d88be4 100644 --- a/unix/window.c +++ b/unix/window.c @@ -12,13 +12,22 @@ struct uiWindow { GtkContainer *vboxContainer; GtkBox *vbox; + GtkWidget *childHolderWidget; + GtkContainer *childHolderContainer; + GtkWidget *menubar; - struct child *child; + uiControl *child; int margined; int (*onClosing)(uiWindow *, void *); void *onClosingData; + void (*onPositionChanged)(uiWindow *, void *); + void *onPositionChangedData; + gboolean changingPosition; + void (*onContentSizeChanged)(uiWindow *, void *); + void *onContentSizeChangedData; + gboolean changingSize; }; static gboolean onClosing(GtkWidget *win, GdkEvent *e, gpointer data) @@ -32,11 +41,39 @@ static gboolean onClosing(GtkWidget *win, GdkEvent *e, gpointer data) return TRUE; } +static gboolean onConfigure(GtkWidget *win, GdkEvent *e, gpointer data) +{ + uiWindow *w = uiWindow(data); + + // there doesn't seem to be a way to determine if only moving or only resizing is happening :/ + if (w->changingPosition) + w->changingPosition = FALSE; + else + (*(w->onPositionChanged))(w, w->onPositionChangedData); + // always continue handling + return FALSE; +} + +static void onSizeAllocate(GtkWidget *widget, GdkRectangle *allocation, gpointer data) +{ + uiWindow *w = uiWindow(data); + + if (w->changingSize) + w->changingSize = FALSE; + else + (*(w->onContentSizeChanged))(w, w->onContentSizeChangedData); +} + static int defaultOnClosing(uiWindow *w, void *data) { return 0; } +static void defaultOnPositionContentSizeChanged(uiWindow *w, void *data) +{ + // do nothing +} + static void uiWindowDestroy(uiControl *c) { uiWindow *w = uiWindow(c); @@ -44,11 +81,15 @@ static void uiWindowDestroy(uiControl *c) // first hide ourselves gtk_widget_hide(w->widget); // now destroy the child - if (w->child != NULL) - childDestroy(w->child); + if (w->child != NULL) { + uiControlSetParent(w->child, NULL); + uiUnixControlSetContainer(uiUnixControl(w->child), w->childHolderContainer, TRUE); + uiControlDestroy(w->child); + } // now destroy the menus, if any if (w->menubar != NULL) freeMenubar(w->menubar); + gtk_widget_destroy(w->childHolderWidget); gtk_widget_destroy(w->vboxWidget); // and finally free ourselves g_object_unref(w->widget); @@ -101,22 +142,99 @@ void uiWindowSetTitle(uiWindow *w, const char *title) gtk_window_set_title(w->window, title); } +// TODO allow specifying either as NULL on all platforms +void uiWindowPosition(uiWindow *w, int *x, int *y) +{ + gint rx, ry; + + gtk_window_get_position(w->window, &rx, &ry); + *x = rx; + *y = ry; +} + +void uiWindowSetPosition(uiWindow *w, int x, int y) +{ + w->changingPosition = TRUE; + gtk_window_move(w->window, x, y); + // gtk_window_move() is asynchronous + // we need to wait for a configure-event + // thanks to hergertme in irc.gimp.net/#gtk+ + while (w->changingPosition) + if (gtk_main_iteration() != FALSE) + break; // stop early if gtk_main_quit() called +} + +void uiWindowCenter(uiWindow *w) +{ + gint x, y; + GtkAllocation winalloc; + GdkWindow *gdkwin; + GdkScreen *screen; + GdkRectangle workarea; + + gtk_widget_get_allocation(w->widget, &winalloc); + gdkwin = gtk_widget_get_window(w->widget); + screen = gdk_window_get_screen(gdkwin); + gdk_screen_get_monitor_workarea(screen, + gdk_screen_get_monitor_at_window(screen, gdkwin), + &workarea); + + x = (workarea.width - winalloc.width) / 2; + y = (workarea.height - winalloc.height) / 2; + // TODO move up slightly? see what Mutter or GNOME Shell or GNOME Terminal do(es)? + uiWindowSetPosition(w, x, y); +} + +void uiWindowOnPositionChanged(uiWindow *w, void (*f)(uiWindow *, void *), void *data) +{ + w->onPositionChanged = f; + w->onPositionChangedData = data; +} + +void uiWindowContentSize(uiWindow *w, int *width, int *height) +{ + GtkAllocation allocation; + + gtk_widget_get_allocation(w->childHolderWidget, &allocation); + *width = allocation.width; + *height = allocation.height; +} + +// TODO what happens if the size is already the current one? +// TODO a spurious size-allocate gets sent after this function returns +void uiWindowSetContentSize(uiWindow *w, int width, int height) +{ + w->changingSize = TRUE; + gtk_widget_set_size_request(w->childHolderWidget, width, height); + while (w->changingSize) + if (gtk_main_iteration() != FALSE) + break; // stop early if gtk_main_quit() called + gtk_widget_set_size_request(w->childHolderWidget, -1, -1); +} + +void uiWindowOnContentSizeChanged(uiWindow *w, void (*f)(uiWindow *, void *), void *data) +{ + w->onContentSizeChanged = f; + w->onContentSizeChangedData = data; +} + void uiWindowOnClosing(uiWindow *w, int (*f)(uiWindow *, void *), void *data) { w->onClosing = f; w->onClosingData = data; } +// TODO save and restore expands and aligns void uiWindowSetChild(uiWindow *w, uiControl *child) { - if (w->child != NULL) - childRemove(w->child); - w->child = newChildWithBox(child, uiControl(w), w->vboxContainer, w->margined); if (w->child != NULL) { - gtk_widget_set_hexpand(childBox(w->child), TRUE); - gtk_widget_set_halign(childBox(w->child), GTK_ALIGN_FILL); - gtk_widget_set_vexpand(childBox(w->child), TRUE); - gtk_widget_set_valign(childBox(w->child), GTK_ALIGN_FILL); + uiControlSetParent(w->child, NULL); + uiUnixControlSetContainer(uiUnixControl(w->child), w->childHolderContainer, TRUE); + } + w->child = child; + if (w->child != NULL) { + uiControlSetParent(w->child, uiControl(w)); + uiUnixControlSetContainer(uiUnixControl(w->child), w->childHolderContainer, FALSE); } } @@ -128,8 +246,7 @@ int uiWindowMargined(uiWindow *w) void uiWindowSetMargined(uiWindow *w, int margined) { w->margined = margined; - if (w->child != NULL) - childSetMargined(w->child, w->margined); + setMargined(w->childHolderContainer, w->margined); } uiWindow *uiNewWindow(const char *title, int width, int height, int hasMenubar) @@ -157,12 +274,24 @@ uiWindow *uiNewWindow(const char *title, int width, int height, int hasMenubar) gtk_container_add(w->vboxContainer, w->menubar); } + w->childHolderWidget = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); + w->childHolderContainer = GTK_CONTAINER(w->childHolderWidget); + gtk_widget_set_hexpand(w->childHolderWidget, TRUE); + gtk_widget_set_halign(w->childHolderWidget, GTK_ALIGN_FILL); + gtk_widget_set_vexpand(w->childHolderWidget, TRUE); + gtk_widget_set_valign(w->childHolderWidget, GTK_ALIGN_FILL); + gtk_container_add(w->vboxContainer, w->childHolderWidget); + // show everything in the vbox, but not the GtkWindow itself gtk_widget_show_all(w->vboxWidget); - // and connect our OnClosing() event + // and connect our events g_signal_connect(w->widget, "delete-event", G_CALLBACK(onClosing), w); + g_signal_connect(w->widget, "configure-event", G_CALLBACK(onConfigure), w); + g_signal_connect(w->childHolderWidget, "size-allocate", G_CALLBACK(onSizeAllocate), w); uiWindowOnClosing(w, defaultOnClosing, NULL); + uiWindowOnPositionChanged(w, defaultOnPositionContentSizeChanged, NULL); + uiWindowOnContentSizeChanged(w, defaultOnPositionContentSizeChanged, NULL); // normally it's SetParent() that does this, but we can't call SetParent() on a uiWindow // TODO we really need to clean this up diff --git a/windows/window.cpp b/windows/window.cpp index 411cb86e..140f1c87 100644 --- a/windows/window.cpp +++ b/windows/window.cpp @@ -14,6 +14,12 @@ struct uiWindow { void *onClosingData; int margined; BOOL hasMenubar; + void (*onPositionChanged)(uiWindow *, void *); + void *onPositionChangedData; + BOOL changingPosition; // to avoid triggering the above when programmatically doing this + void (*onContentSizeChanged)(uiWindow *, void *); + void *onContentSizeChangedData; + BOOL changingSize; }; // from https://msdn.microsoft.com/en-us/library/windows/desktop/dn742486.aspx#sizingandspacing @@ -35,7 +41,6 @@ static void windowMargins(uiWindow *w, int *mx, int *my) static void windowRelayout(uiWindow *w) { - uiWindowsSizing sizing; int x, y, width, height; RECT r; int mx, my; @@ -87,8 +92,15 @@ 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; + if (w->onContentSizeChanged != NULL) // TODO figure out why this is happening too early + if (!w->changingSize) + (*(w->onContentSizeChanged))(w, w->onContentSizeChangedData); windowRelayout(w); return 0; case WM_GETMINMAXINFO: @@ -138,6 +150,11 @@ static int defaultOnClosing(uiWindow *w, void *data) return 0; } +static void defaultOnPositionContentSizeChanged(uiWindow *w, void *data) +{ + // do nothing +} + static std::map windows; static void uiWindowDestroy(uiControl *c) @@ -224,7 +241,6 @@ uiWindowsControlDefaultSetParentHWND(uiWindow) static void uiWindowMinimumSize(uiWindowsControl *c, int *width, int *height) { uiWindow *w = uiWindow(c); - uiWindowsSizing sizing; int mx, my; *width = 0; @@ -277,6 +293,100 @@ 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 should this be on the work area? + // TODO is this calculation correct? + y -= y / 20; + uiWindowSetPosition(w, x, y); +} + +void uiWindowContentSize(uiWindow *w, int *width, int *height) +{ + RECT r; + + uiWindowsEnsureGetClientRect(w->hwnd, &r); + *width = r.right - r.left; + *height = r.bottom - r.top; +} + +// TODO should this disallow too small? +void uiWindowSetContentSize(uiWindow *w, int width, int height) +{ + w->changingSize = TRUE; + clientSizeToWindowSize(w->hwnd, &width, &height, w->hasMenubar); + if (SetWindowPos(w->hwnd, NULL, 0, 0, width, height, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOZORDER) == 0) + logLastError(L"error resizing window"); + w->changingSize = FALSE; +} + +void uiWindowOnContentSizeChanged(uiWindow *w, void (*f)(uiWindow *, void *), void *data) +{ + w->onContentSizeChanged = f; + w->onContentSizeChangedData = data; +} + +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 +483,8 @@ uiWindow *uiNewWindow(const char *title, int width, int height, int hasMenubar) setClientSize(w, width, height, hasMenubarBOOL, style, exstyle); uiWindowOnClosing(w, defaultOnClosing, NULL); + uiWindowOnPositionChanged(w, defaultOnPositionContentSizeChanged, NULL); + uiWindowOnContentSizeChanged(w, defaultOnPositionContentSizeChanged, NULL); windows[w] = true; return w;