commit
1fa79fd3b6
|
@ -16,10 +16,14 @@ This README is being written.<br>
|
||||||
|
|
||||||
## Updates
|
## 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**
|
* **15 June 2016**
|
||||||
* Added `uiFormDelete()`; thanks to @emersion.
|
* Added `uiFormDelete()`; thanks to @emersion.
|
||||||
|
* Added `uiWindowPosition()`, `uiWindowSetPosition()`, `uiWindowCenter()`, and `uiWindowOnPositionChanged()`, methods for manipulating uiWindow position.
|
||||||
|
|
||||||
* **14 June 2016**
|
* **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.
|
* 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 *);
|
int (*onClosing)(uiWindow *, void *);
|
||||||
void *onClosingData;
|
void *onClosingData;
|
||||||
struct singleChildConstraints constraints;
|
struct singleChildConstraints constraints;
|
||||||
|
void (*onPositionChanged)(uiWindow *, void *);
|
||||||
|
void *onPositionChangedData;
|
||||||
|
BOOL suppressPositionChanged;
|
||||||
|
void (*onContentSizeChanged)(uiWindow *, void *);
|
||||||
|
void *onContentSizeChangedData;
|
||||||
|
BOOL suppressSizeChanged;
|
||||||
};
|
};
|
||||||
|
|
||||||
@interface windowDelegateClass : NSObject<NSWindowDelegate> {
|
@interface windowDelegateClass : NSObject<NSWindowDelegate> {
|
||||||
struct mapTable *windows;
|
struct mapTable *windows;
|
||||||
}
|
}
|
||||||
- (BOOL)windowShouldClose:(id)sender;
|
- (BOOL)windowShouldClose:(id)sender;
|
||||||
|
- (void)windowDidMove:(NSNotification *)note;
|
||||||
|
- (void)windowDidResize:(NSNotification *)note;
|
||||||
- (void)registerWindow:(uiWindow *)w;
|
- (void)registerWindow:(uiWindow *)w;
|
||||||
- (void)unregisterWindow:(uiWindow *)w;
|
- (void)unregisterWindow:(uiWindow *)w;
|
||||||
- (uiWindow *)lookupWindow:(NSWindow *)w;
|
- (uiWindow *)lookupWindow:(NSWindow *)w;
|
||||||
|
@ -47,6 +55,25 @@ struct uiWindow {
|
||||||
return NO;
|
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
|
- (void)registerWindow:(uiWindow *)w
|
||||||
{
|
{
|
||||||
mapSet(self->windows, w->window, w);
|
mapSet(self->windows, w->window, w);
|
||||||
|
@ -204,6 +231,71 @@ void uiWindowSetTitle(uiWindow *w, const char *title)
|
||||||
[w->window setTitle:toNSString(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)
|
void uiWindowOnClosing(uiWindow *w, int (*f)(uiWindow *, void *), void *data)
|
||||||
{
|
{
|
||||||
w->onClosing = f;
|
w->onClosing = f;
|
||||||
|
@ -245,6 +337,11 @@ static int defaultOnClosing(uiWindow *w, void *data)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void defaultOnPositionContentSizeChanged(uiWindow *w, void *data)
|
||||||
|
{
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
uiWindow *uiNewWindow(const char *title, int width, int height, int hasMenubar)
|
uiWindow *uiNewWindow(const char *title, int width, int height, int hasMenubar)
|
||||||
{
|
{
|
||||||
uiWindow *w;
|
uiWindow *w;
|
||||||
|
@ -269,6 +366,8 @@ uiWindow *uiNewWindow(const char *title, int width, int height, int hasMenubar)
|
||||||
}
|
}
|
||||||
[windowDelegate registerWindow:w];
|
[windowDelegate registerWindow:w];
|
||||||
uiWindowOnClosing(w, defaultOnClosing, NULL);
|
uiWindowOnClosing(w, defaultOnClosing, NULL);
|
||||||
|
uiWindowOnPositionChanged(w, defaultOnPositionContentSizeChanged, NULL);
|
||||||
|
uiWindowOnContentSizeChanged(w, defaultOnPositionContentSizeChanged, NULL);
|
||||||
|
|
||||||
return w;
|
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
|
page12.c
|
||||||
page13.c
|
page13.c
|
||||||
page14.c
|
page14.c
|
||||||
|
page15.c
|
||||||
spaced.c
|
spaced.c
|
||||||
${_TEST_RESOURCES_RC}
|
${_TEST_RESOURCES_RC}
|
||||||
)
|
)
|
||||||
|
|
|
@ -49,6 +49,7 @@ int main(int argc, char *argv[])
|
||||||
uiBox *page6, *page7, *page8, *page9, *page10;
|
uiBox *page6, *page7, *page8, *page9, *page10;
|
||||||
uiBox *page11, *page12, *page13;
|
uiBox *page11, *page12, *page13;
|
||||||
uiTab *page14;
|
uiTab *page14;
|
||||||
|
uiBox *page15;
|
||||||
uiTab *outerTab;
|
uiTab *outerTab;
|
||||||
uiTab *innerTab;
|
uiTab *innerTab;
|
||||||
int nomenus = 0;
|
int nomenus = 0;
|
||||||
|
@ -148,6 +149,9 @@ int main(int argc, char *argv[])
|
||||||
page14 = makePage14();
|
page14 = makePage14();
|
||||||
uiTabAppend(innerTab, "Page 14", uiControl(page14));
|
uiTabAppend(innerTab, "Page 14", uiControl(page14));
|
||||||
|
|
||||||
|
page15 = makePage15(w);
|
||||||
|
uiTabAppend(innerTab, "Page 15", uiControl(page15));
|
||||||
|
|
||||||
if (startspaced)
|
if (startspaced)
|
||||||
setSpaced(1);
|
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 <stdarg.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
#include <limits.h>
|
||||||
#include "../ui.h"
|
#include "../ui.h"
|
||||||
|
|
||||||
// main.c
|
// main.c
|
||||||
|
@ -85,3 +86,6 @@ extern uiBox *makePage13(void);
|
||||||
|
|
||||||
// page14.c
|
// page14.c
|
||||||
extern uiTab *makePage14(void);
|
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))
|
#define uiWindow(this) ((uiWindow *) (this))
|
||||||
_UI_EXTERN char *uiWindowTitle(uiWindow *w);
|
_UI_EXTERN char *uiWindowTitle(uiWindow *w);
|
||||||
_UI_EXTERN void uiWindowSetTitle(uiWindow *w, const char *title);
|
_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 uiWindowOnClosing(uiWindow *w, int (*f)(uiWindow *w, void *data), void *data);
|
||||||
_UI_EXTERN void uiWindowSetChild(uiWindow *w, uiControl *child);
|
_UI_EXTERN void uiWindowSetChild(uiWindow *w, uiControl *child);
|
||||||
_UI_EXTERN int uiWindowMargined(uiWindow *w);
|
_UI_EXTERN int uiWindowMargined(uiWindow *w);
|
||||||
|
|
155
unix/window.c
155
unix/window.c
|
@ -12,13 +12,22 @@ struct uiWindow {
|
||||||
GtkContainer *vboxContainer;
|
GtkContainer *vboxContainer;
|
||||||
GtkBox *vbox;
|
GtkBox *vbox;
|
||||||
|
|
||||||
|
GtkWidget *childHolderWidget;
|
||||||
|
GtkContainer *childHolderContainer;
|
||||||
|
|
||||||
GtkWidget *menubar;
|
GtkWidget *menubar;
|
||||||
|
|
||||||
struct child *child;
|
uiControl *child;
|
||||||
int margined;
|
int margined;
|
||||||
|
|
||||||
int (*onClosing)(uiWindow *, void *);
|
int (*onClosing)(uiWindow *, void *);
|
||||||
void *onClosingData;
|
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)
|
static gboolean onClosing(GtkWidget *win, GdkEvent *e, gpointer data)
|
||||||
|
@ -32,11 +41,39 @@ static gboolean onClosing(GtkWidget *win, GdkEvent *e, gpointer data)
|
||||||
return TRUE;
|
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)
|
static int defaultOnClosing(uiWindow *w, void *data)
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void defaultOnPositionContentSizeChanged(uiWindow *w, void *data)
|
||||||
|
{
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
static void uiWindowDestroy(uiControl *c)
|
static void uiWindowDestroy(uiControl *c)
|
||||||
{
|
{
|
||||||
uiWindow *w = uiWindow(c);
|
uiWindow *w = uiWindow(c);
|
||||||
|
@ -44,11 +81,15 @@ static void uiWindowDestroy(uiControl *c)
|
||||||
// first hide ourselves
|
// first hide ourselves
|
||||||
gtk_widget_hide(w->widget);
|
gtk_widget_hide(w->widget);
|
||||||
// now destroy the child
|
// now destroy the child
|
||||||
if (w->child != NULL)
|
if (w->child != NULL) {
|
||||||
childDestroy(w->child);
|
uiControlSetParent(w->child, NULL);
|
||||||
|
uiUnixControlSetContainer(uiUnixControl(w->child), w->childHolderContainer, TRUE);
|
||||||
|
uiControlDestroy(w->child);
|
||||||
|
}
|
||||||
// now destroy the menus, if any
|
// now destroy the menus, if any
|
||||||
if (w->menubar != NULL)
|
if (w->menubar != NULL)
|
||||||
freeMenubar(w->menubar);
|
freeMenubar(w->menubar);
|
||||||
|
gtk_widget_destroy(w->childHolderWidget);
|
||||||
gtk_widget_destroy(w->vboxWidget);
|
gtk_widget_destroy(w->vboxWidget);
|
||||||
// and finally free ourselves
|
// and finally free ourselves
|
||||||
g_object_unref(w->widget);
|
g_object_unref(w->widget);
|
||||||
|
@ -101,22 +142,99 @@ void uiWindowSetTitle(uiWindow *w, const char *title)
|
||||||
gtk_window_set_title(w->window, 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)
|
void uiWindowOnClosing(uiWindow *w, int (*f)(uiWindow *, void *), void *data)
|
||||||
{
|
{
|
||||||
w->onClosing = f;
|
w->onClosing = f;
|
||||||
w->onClosingData = data;
|
w->onClosingData = data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO save and restore expands and aligns
|
||||||
void uiWindowSetChild(uiWindow *w, uiControl *child)
|
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) {
|
if (w->child != NULL) {
|
||||||
gtk_widget_set_hexpand(childBox(w->child), TRUE);
|
uiControlSetParent(w->child, NULL);
|
||||||
gtk_widget_set_halign(childBox(w->child), GTK_ALIGN_FILL);
|
uiUnixControlSetContainer(uiUnixControl(w->child), w->childHolderContainer, TRUE);
|
||||||
gtk_widget_set_vexpand(childBox(w->child), TRUE);
|
}
|
||||||
gtk_widget_set_valign(childBox(w->child), GTK_ALIGN_FILL);
|
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)
|
void uiWindowSetMargined(uiWindow *w, int margined)
|
||||||
{
|
{
|
||||||
w->margined = margined;
|
w->margined = margined;
|
||||||
if (w->child != NULL)
|
setMargined(w->childHolderContainer, w->margined);
|
||||||
childSetMargined(w->child, w->margined);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uiWindow *uiNewWindow(const char *title, int width, int height, int hasMenubar)
|
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);
|
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
|
// show everything in the vbox, but not the GtkWindow itself
|
||||||
gtk_widget_show_all(w->vboxWidget);
|
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, "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);
|
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
|
// normally it's SetParent() that does this, but we can't call SetParent() on a uiWindow
|
||||||
// TODO we really need to clean this up
|
// TODO we really need to clean this up
|
||||||
|
|
|
@ -14,6 +14,12 @@ struct uiWindow {
|
||||||
void *onClosingData;
|
void *onClosingData;
|
||||||
int margined;
|
int margined;
|
||||||
BOOL hasMenubar;
|
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
|
// 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)
|
static void windowRelayout(uiWindow *w)
|
||||||
{
|
{
|
||||||
uiWindowsSizing sizing;
|
|
||||||
int x, y, width, height;
|
int x, y, width, height;
|
||||||
RECT r;
|
RECT r;
|
||||||
int mx, my;
|
int mx, my;
|
||||||
|
@ -87,8 +92,15 @@ static LRESULT CALLBACK windowWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARA
|
||||||
runMenuEvent(LOWORD(wParam), uiWindow(w));
|
runMenuEvent(LOWORD(wParam), uiWindow(w));
|
||||||
return 0;
|
return 0;
|
||||||
case WM_WINDOWPOSCHANGED:
|
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)
|
if ((wp->flags & SWP_NOSIZE) != 0)
|
||||||
break;
|
break;
|
||||||
|
if (w->onContentSizeChanged != NULL) // TODO figure out why this is happening too early
|
||||||
|
if (!w->changingSize)
|
||||||
|
(*(w->onContentSizeChanged))(w, w->onContentSizeChangedData);
|
||||||
windowRelayout(w);
|
windowRelayout(w);
|
||||||
return 0;
|
return 0;
|
||||||
case WM_GETMINMAXINFO:
|
case WM_GETMINMAXINFO:
|
||||||
|
@ -138,6 +150,11 @@ static int defaultOnClosing(uiWindow *w, void *data)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void defaultOnPositionContentSizeChanged(uiWindow *w, void *data)
|
||||||
|
{
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
static std::map<uiWindow *, bool> windows;
|
static std::map<uiWindow *, bool> windows;
|
||||||
|
|
||||||
static void uiWindowDestroy(uiControl *c)
|
static void uiWindowDestroy(uiControl *c)
|
||||||
|
@ -224,7 +241,6 @@ uiWindowsControlDefaultSetParentHWND(uiWindow)
|
||||||
static void uiWindowMinimumSize(uiWindowsControl *c, int *width, int *height)
|
static void uiWindowMinimumSize(uiWindowsControl *c, int *width, int *height)
|
||||||
{
|
{
|
||||||
uiWindow *w = uiWindow(c);
|
uiWindow *w = uiWindow(c);
|
||||||
uiWindowsSizing sizing;
|
|
||||||
int mx, my;
|
int mx, my;
|
||||||
|
|
||||||
*width = 0;
|
*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)
|
// 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)
|
void uiWindowOnClosing(uiWindow *w, int (*f)(uiWindow *, void *), void *data)
|
||||||
{
|
{
|
||||||
w->onClosing = f;
|
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);
|
setClientSize(w, width, height, hasMenubarBOOL, style, exstyle);
|
||||||
|
|
||||||
uiWindowOnClosing(w, defaultOnClosing, NULL);
|
uiWindowOnClosing(w, defaultOnClosing, NULL);
|
||||||
|
uiWindowOnPositionChanged(w, defaultOnPositionContentSizeChanged, NULL);
|
||||||
|
uiWindowOnContentSizeChanged(w, defaultOnPositionContentSizeChanged, NULL);
|
||||||
|
|
||||||
windows[w] = true;
|
windows[w] = true;
|
||||||
return w;
|
return w;
|
||||||
|
|
Loading…
Reference in New Issue