From 4465d37d2e47fb53c585944133be741f37e3516a Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 15 Jun 2016 13:21:07 -0400 Subject: [PATCH 01/10] Started uiWindow positioning stuff. --- test/CMakeLists.txt | 1 + test/main.c | 4 ++++ test/page15.c | 52 +++++++++++++++++++++++++++++++++++++++++++++ test/test.h | 4 ++++ ui.h | 2 ++ 5 files changed, 63 insertions(+) create mode 100644 test/page15.c 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..ff3a5a26 --- /dev/null +++ b/test/page15.c @@ -0,0 +1,52 @@ +// 15 june 2016 +#include "test.h" + +void moveX(uiSpinbox *s, void *data) +{ + uiWindow *w = uiWindow(data); + int x, y; + + uiWindowPosition(w, &x, &y); + x = uiSpinboxValue(s); + uiWindowSetPosition(w, x, y); +} + +void moveX(uiSpinbox *s, void *data) +{ + uiWindow *w = uiWindow(data); + int x, y; + + uiWindowPosition(w, &x, &y); + y = uiSpinboxValue(s); + uiWindowSetPosition(w, x, y); +} + +// TODO onMove + +uiBox *makePage15(uiWindow *w) +{ + uiBox *page15; + uiBox *hbox; + uiSpinbox *x; + uiSpinbox *y; + int curx, cury; + + page15 = newVerticalBox(); + + hbox = newHorizontalBox(); + uiBoxAppend(page15, uiControl(hbox), 1); + + 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); + + uiSpinboxOnChanged(x, moveX, w); + uiSpinboxOnChanged(y, moveY, w); + uiWindowPosition(w, &curX, &curY); + uiSpinboxSetValue(x, curX); + uiSpinboxSetValue(y, curY); + + 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..ee2b3357 100644 --- a/ui.h +++ b/ui.h @@ -99,6 +99,8 @@ 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 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); From 53bec81925079f2651e350a95d5fcf7b6d864ec1 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 15 Jun 2016 14:57:52 -0400 Subject: [PATCH 02/10] More uiWindow positioning refinement and implementation on OS X. --- darwin/window.m | 61 +++++++++++++++++++++++++++++++++++++++++++++++++ test/page15.c | 60 +++++++++++++++++++++++++++++++++--------------- ui.h | 2 ++ 3 files changed, 105 insertions(+), 18 deletions(-) diff --git a/darwin/window.m b/darwin/window.m index ffb6b30d..7cab10b4 100644 --- a/darwin/window.m +++ b/darwin/window.m @@ -9,12 +9,16 @@ struct uiWindow { int (*onClosing)(uiWindow *, void *); void *onClosingData; struct singleChildConstraints constraints; + void (*onPositionChanged)(uiWindow *, void *); + void *onPositionChangedData; + BOOL suppressPositionChanged; }; @interface windowDelegateClass : NSObject { struct mapTable *windows; } - (BOOL)windowShouldClose:(id)sender; +- (void)windowDidMove:(NSNotification *)note; - (void)registerWindow:(uiWindow *)w; - (void)unregisterWindow:(uiWindow *)w; - (uiWindow *)lookupWindow:(NSWindow *)w; @@ -47,6 +51,16 @@ 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)registerWindow:(uiWindow *)w { mapSet(self->windows, w->window, w); @@ -204,6 +218,47 @@ 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->window center]; +} + +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; @@ -245,6 +300,11 @@ static int defaultOnClosing(uiWindow *w, void *data) return 0; } +static void defaultOnPositionChanged(uiWindow *w, void *data) +{ + // do nothing +} + uiWindow *uiNewWindow(const char *title, int width, int height, int hasMenubar) { uiWindow *w; @@ -269,6 +329,7 @@ uiWindow *uiNewWindow(const char *title, int width, int height, int hasMenubar) } [windowDelegate registerWindow:w]; uiWindowOnClosing(w, defaultOnClosing, NULL); + uiWindowOnPositionChanged(w, defaultOnPositionChanged, NULL); return w; } diff --git a/test/page15.c b/test/page15.c index ff3a5a26..7ab534fd 100644 --- a/test/page15.c +++ b/test/page15.c @@ -1,52 +1,76 @@ // 15 june 2016 #include "test.h" -void moveX(uiSpinbox *s, void *data) +static uiSpinbox *x, *y; + +static void moveX(uiSpinbox *s, void *data) { uiWindow *w = uiWindow(data); - int x, y; + int xp, yp; - uiWindowPosition(w, &x, &y); - x = uiSpinboxValue(s); - uiWindowSetPosition(w, x, y); + uiWindowPosition(w, &xp, &yp); + xp = uiSpinboxValue(x); + uiWindowSetPosition(w, xp, yp); } -void moveX(uiSpinbox *s, void *data) +static void moveY(uiSpinbox *s, void *data) { uiWindow *w = uiWindow(data); - int x, y; + int xp, yp; - uiWindowPosition(w, &x, &y); - y = uiSpinboxValue(s); - uiWindowSetPosition(w, x, y); + uiWindowPosition(w, &xp, &yp); + yp = uiSpinboxValue(y); + uiWindowSetPosition(w, xp, yp); } -// TODO onMove +static void update(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); + update(w); +} + +void onMove(uiWindow *w, void *data) +{ + printf("move\n"); + update(w); +} uiBox *makePage15(uiWindow *w) { uiBox *page15; uiBox *hbox; - uiSpinbox *x; - uiSpinbox *y; - int curx, cury; + uiButton *button; page15 = newVerticalBox(); hbox = newHorizontalBox(); - uiBoxAppend(page15, uiControl(hbox), 1); + // TODO if I make this 1 and not add anything else, 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); - uiWindowPosition(w, &curX, &curY); - uiSpinboxSetValue(x, curX); - uiSpinboxSetValue(y, curY); + uiButtonOnClicked(button, center, w); + uiWindowOnPositionChanged(w, onMove, NULL); + update(w); return page15; } diff --git a/ui.h b/ui.h index ee2b3357..37821208 100644 --- a/ui.h +++ b/ui.h @@ -101,6 +101,8 @@ _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 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); From f98318fb3af36d6a6ece2321c7e56541957159ed Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 15 Jun 2016 16:45:49 -0400 Subject: [PATCH 03/10] Started implementing the new uiWindows stuff on GTK+. --- test/page15.c | 2 +- unix/window.c | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 54 insertions(+), 2 deletions(-) diff --git a/test/page15.c b/test/page15.c index 7ab534fd..4fdadf67 100644 --- a/test/page15.c +++ b/test/page15.c @@ -55,7 +55,7 @@ uiBox *makePage15(uiWindow *w) page15 = newVerticalBox(); hbox = newHorizontalBox(); - // TODO if I make this 1 and not add anything else, on OS X the box won't be able to grow vertically + // 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); diff --git a/unix/window.c b/unix/window.c index cca767e8..702c50f4 100644 --- a/unix/window.c +++ b/unix/window.c @@ -19,6 +19,8 @@ struct uiWindow { int (*onClosing)(uiWindow *, void *); void *onClosingData; + void (*onPositionChanged)(uiWindow *, void *); + void *onPositionChangedData; }; static gboolean onClosing(GtkWidget *win, GdkEvent *e, gpointer data) @@ -37,6 +39,11 @@ static int defaultOnClosing(uiWindow *w, void *data) return 0; } +static void defaultOnPositionChanged(uiWindow *w, void *data) +{ + // do nothing +} + static void uiWindowDestroy(uiControl *c) { uiWindow *w = uiWindow(c); @@ -101,6 +108,50 @@ 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) +{ + gtk_window_move(w->window, x, y); +} + +// TODO after calling this I have to call get_position() a few times before it actually works +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 does? + gtk_window_move(w->window, x, y); +} + +// TODO find a signal to connect to +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; @@ -160,9 +211,10 @@ uiWindow *uiNewWindow(const char *title, int width, int height, int hasMenubar) // 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); uiWindowOnClosing(w, defaultOnClosing, NULL); + uiWindowOnPositionChanged(w, defaultOnPositionChanged, 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 From 48c13c738aef8fd24e8d0b477a20ed1408415a76 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 15 Jun 2016 20:45:10 -0400 Subject: [PATCH 04/10] Fixed the GTK+ window code. --- unix/window.c | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/unix/window.c b/unix/window.c index 702c50f4..cdb3ab84 100644 --- a/unix/window.c +++ b/unix/window.c @@ -21,6 +21,7 @@ struct uiWindow { void *onClosingData; void (*onPositionChanged)(uiWindow *, void *); void *onPositionChangedData; + gboolean changingPosition; }; static gboolean onClosing(GtkWidget *win, GdkEvent *e, gpointer data) @@ -34,6 +35,19 @@ 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 int defaultOnClosing(uiWindow *w, void *data) { return 0; @@ -120,10 +134,16 @@ void uiWindowPosition(uiWindow *w, int *x, int *y) 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 } -// TODO after calling this I have to call get_position() a few times before it actually works void uiWindowCenter(uiWindow *w) { gint x, y; @@ -141,11 +161,10 @@ void uiWindowCenter(uiWindow *w) x = (workarea.width - winalloc.width) / 2; y = (workarea.height - winalloc.height) / 2; - // TODO move up slightly? see what Mutter or GNOME Shell does? - gtk_window_move(w->window, x, y); + // TODO move up slightly? see what Mutter or GNOME Shell or GNOME Terminal do(es)? + uiWindowSetPosition(w, x, y); } -// TODO find a signal to connect to void uiWindowOnPositionChanged(uiWindow *w, void (*f)(uiWindow *, void *), void *data) { w->onPositionChanged = f; @@ -213,6 +232,7 @@ uiWindow *uiNewWindow(const char *title, int width, int height, int hasMenubar) // 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); uiWindowOnClosing(w, defaultOnClosing, NULL); uiWindowOnPositionChanged(w, defaultOnPositionChanged, NULL); From c3777da0f45c9557a8b514a90ab85198cdd7c4a2 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 15 Jun 2016 21:55:42 -0400 Subject: [PATCH 05/10] And added the new uiWindow methods on Windows. --- README.md | 1 + windows/window.cpp | 81 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 82 insertions(+) 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; From 5fbe85c21a887aa1b9c80c165da815726b1894b8 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 15 Jun 2016 22:28:44 -0400 Subject: [PATCH 06/10] Started adding the uiWindow size code. --- test/page15.c | 61 +++++++++++++++++++++++++++++++++++++++++++++++---- ui.h | 3 +++ 2 files changed, 60 insertions(+), 4 deletions(-) diff --git a/test/page15.c b/test/page15.c index 4fdadf67..64b23a5c 100644 --- a/test/page15.c +++ b/test/page15.c @@ -2,6 +2,7 @@ #include "test.h" static uiSpinbox *x, *y; +static uiSpinbox *width, *height; static void moveX(uiSpinbox *s, void *data) { @@ -23,7 +24,7 @@ static void moveY(uiSpinbox *s, void *data) uiWindowSetPosition(w, xp, yp); } -static void update(uiWindow *w) +static void updatepos(uiWindow *w) { int xp, yp; @@ -37,13 +38,48 @@ static void center(uiButton *b, void *data) uiWindow *w = uiWindow(data); uiWindowCenter(w); - update(w); + updatepos(w); } void onMove(uiWindow *w, void *data) { printf("move\n"); - update(w); + updatepos(w); +} + +static void sizeWidth(uiSpinbox *s, void *data) +{ + uiWindow *w = uiWindow(data); + int xp, yp; + + uiWindowsContentSize(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) @@ -70,7 +106,24 @@ uiBox *makePage15(uiWindow *w) uiSpinboxOnChanged(y, moveY, w); uiButtonOnClicked(button, center, w); uiWindowOnPositionChanged(w, onMove, NULL); - update(w); + 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/ui.h b/ui.h index 37821208..1ab01e91 100644 --- a/ui.h +++ b/ui.h @@ -103,6 +103,9 @@ _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); From 6c56f1e1ce41f3e56dde0664fec0c9c2f2c5a1dd Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 15 Jun 2016 22:52:35 -0400 Subject: [PATCH 07/10] Implemented the window size stuff on Windows. --- test/page15.c | 2 +- windows/window.cpp | 40 +++++++++++++++++++++++++++++++++++----- 2 files changed, 36 insertions(+), 6 deletions(-) diff --git a/test/page15.c b/test/page15.c index 64b23a5c..618ac709 100644 --- a/test/page15.c +++ b/test/page15.c @@ -52,7 +52,7 @@ static void sizeWidth(uiSpinbox *s, void *data) uiWindow *w = uiWindow(data); int xp, yp; - uiWindowsContentSize(w, &xp, &yp); + uiWindowContentSize(w, &xp, &yp); xp = uiSpinboxValue(width); uiWindowSetContentSize(w, xp, yp); } diff --git a/windows/window.cpp b/windows/window.cpp index f753d93c..64a1853f 100644 --- a/windows/window.cpp +++ b/windows/window.cpp @@ -16,7 +16,10 @@ struct uiWindow { BOOL hasMenubar; void (*onPositionChanged)(uiWindow *, void *); void *onPositionChangedData; - BOOL changingPosition; + 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 @@ -38,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; @@ -96,6 +98,9 @@ static LRESULT CALLBACK windowWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARA // 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: @@ -145,7 +150,7 @@ static int defaultOnClosing(uiWindow *w, void *data) return 0; } -static void defaultOnPositionChanged(uiWindow *w, void *data) +static void defaultOnPositionContentSizeChanged(uiWindow *w, void *data) { // do nothing } @@ -236,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; @@ -351,6 +355,31 @@ void uiWindowCenter(uiWindow *w) 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; @@ -453,7 +482,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, defaultOnPositionChanged, NULL); + uiWindowOnPositionChanged(w, defaultOnPositionContentSizeChanged, NULL); + uiWindowOnContentSizeChanged(w, defaultOnPositionContentSizeChanged, NULL); windows[w] = true; return w; From e5064de86ba9586e31d69cba3911e3b7287d6f80 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 15 Jun 2016 23:00:26 -0400 Subject: [PATCH 08/10] More TODOs. --- windows/window.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/windows/window.cpp b/windows/window.cpp index 64a1853f..140f1c87 100644 --- a/windows/window.cpp +++ b/windows/window.cpp @@ -350,7 +350,8 @@ void uiWindowCenter(uiWindow *w) 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? + // TODO should this be on the work area? + // TODO is this calculation correct? y -= y / 20; uiWindowSetPosition(w, x, y); } From 88bb697bbdb5d6215e3be138550d9bfedced849a Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 16 Jun 2016 00:45:23 -0400 Subject: [PATCH 09/10] Implemented the new uiWindow stuff on GTK+. --- unix/window.c | 85 ++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 71 insertions(+), 14 deletions(-) diff --git a/unix/window.c b/unix/window.c index cdb3ab84..f0d88be4 100644 --- a/unix/window.c +++ b/unix/window.c @@ -12,9 +12,12 @@ struct uiWindow { GtkContainer *vboxContainer; GtkBox *vbox; + GtkWidget *childHolderWidget; + GtkContainer *childHolderContainer; + GtkWidget *menubar; - struct child *child; + uiControl *child; int margined; int (*onClosing)(uiWindow *, void *); @@ -22,6 +25,9 @@ struct uiWindow { 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) @@ -48,12 +54,22 @@ static gboolean onConfigure(GtkWidget *win, GdkEvent *e, gpointer data) 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 defaultOnPositionChanged(uiWindow *w, void *data) +static void defaultOnPositionContentSizeChanged(uiWindow *w, void *data) { // do nothing } @@ -65,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); @@ -171,22 +191,50 @@ void uiWindowOnPositionChanged(uiWindow *w, void (*f)(uiWindow *, void *), void 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); } } @@ -198,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) @@ -227,14 +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 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, defaultOnPositionChanged, 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 From caec39281bcaac4cd26b6d81f1418b086d0476c0 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 16 Jun 2016 00:58:40 -0400 Subject: [PATCH 10/10] And implemented the new sizing stuff on OS X. --- README.md | 5 ++++- darwin/window.m | 42 ++++++++++++++++++++++++++++++++++++++++-- doc/windowmovesize | 3 +++ 3 files changed, 47 insertions(+), 3 deletions(-) create mode 100644 doc/windowmovesize diff --git a/README.md b/README.md index 86a83fa7..4b615c8e 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,10 @@ 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. diff --git a/darwin/window.m b/darwin/window.m index 7cab10b4..03c86ac1 100644 --- a/darwin/window.m +++ b/darwin/window.m @@ -12,6 +12,9 @@ struct uiWindow { void (*onPositionChanged)(uiWindow *, void *); void *onPositionChangedData; BOOL suppressPositionChanged; + void (*onContentSizeChanged)(uiWindow *, void *); + void *onContentSizeChangedData; + BOOL suppressSizeChanged; }; @interface windowDelegateClass : NSObject { @@ -19,6 +22,7 @@ struct uiWindow { } - (BOOL)windowShouldClose:(id)sender; - (void)windowDidMove:(NSNotification *)note; +- (void)windowDidResize:(NSNotification *)note; - (void)registerWindow:(uiWindow *)w; - (void)unregisterWindow:(uiWindow *)w; - (uiWindow *)lookupWindow:(NSWindow *)w; @@ -61,6 +65,15 @@ struct uiWindow { (*(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); @@ -250,7 +263,9 @@ void uiWindowSetPosition(uiWindow *w, int x, int y) void uiWindowCenter(uiWindow *w) { + w->suppressPositionChanged = YES; [w->window center]; + w->suppressPositionChanged = NO; } void uiWindowOnPositionChanged(uiWindow *w, void (*f)(uiWindow *, void *), void *data) @@ -259,6 +274,28 @@ void uiWindowOnPositionChanged(uiWindow *w, void (*f)(uiWindow *, void *), void 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; @@ -300,7 +337,7 @@ static int defaultOnClosing(uiWindow *w, void *data) return 0; } -static void defaultOnPositionChanged(uiWindow *w, void *data) +static void defaultOnPositionContentSizeChanged(uiWindow *w, void *data) { // do nothing } @@ -329,7 +366,8 @@ uiWindow *uiNewWindow(const char *title, int width, int height, int hasMenubar) } [windowDelegate registerWindow:w]; uiWindowOnClosing(w, defaultOnClosing, NULL); - uiWindowOnPositionChanged(w, defaultOnPositionChanged, 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