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;