commit
1fa79fd3b6
|
@ -16,10 +16,14 @@ This README is being written.<br>
|
|||
|
||||
## 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.
|
||||
|
|
|
@ -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<NSWindowDelegate> {
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -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
|
|
@ -25,6 +25,7 @@ _add_exec(tester
|
|||
page12.c
|
||||
page13.c
|
||||
page14.c
|
||||
page15.c
|
||||
spaced.c
|
||||
${_TEST_RESOURCES_RC}
|
||||
)
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -5,6 +5,7 @@
|
|||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
#include <limits.h>
|
||||
#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 *);
|
||||
|
|
7
ui.h
7
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);
|
||||
|
|
155
unix/window.c
155
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
|
||||
|
|
|
@ -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<uiWindow *, bool> 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;
|
||||
|
|
Loading…
Reference in New Issue