Did all the uiWindow stuff on GTK+.
This commit is contained in:
parent
eb1862afe1
commit
218439c215
|
@ -29,12 +29,12 @@ Each method is named for the `uiDarwinControl` function that it implements. As s
|
|||
### `uiDarwinControlHandle()`
|
||||
|
||||
```objective-c
|
||||
uiprivExtern NSView *uiDarwinControlHandle(uiControl *c);
|
||||
uiprivExtern id uiDarwinControlHandle(uiControl *c);
|
||||
```
|
||||
|
||||
`uiDarwinControlHandle()` returns the Objective-C object that underpins `c`, or `nil` if `c` does not have any underlying object associated with it when called.
|
||||
|
||||
The object returned by `uiDarwinControlHandle()` is owned by `c`; you do not receive a reference to it at all. The object is valid until either `c` is destroyed or until `c` decides to destroy the object; you can handle the latter by catching TODO. In general, you should not store the returned string pointer directly for later use, nor should you attempt to retain the returned object. Instead, use the returned handle immediately if you have to, or follow TODO if you need to adjust properties of the handle that should persist across handle destruction/creation.
|
||||
The object returned by `uiDarwinControlHandle()` is owned by `c`; you do not receive a reference to it at all. The object is valid until either `c` is destroyed or until `c` decides to destroy the object; you can handle the latter by catching TODO. In general, you should not store the returned object pointer directly for later use, nor should you attempt to retain the returned object. Instead, use the returned handle immediately if you have to, or follow TODO if you need to adjust properties of the handle that should persist across handle destruction/creation.
|
||||
|
||||
`uiWindow`s have a single handle of type `NSWindow` that is created when the `uiWindow` is created and destroyed when the `uiWindow` is destroyed. Despite this, you should still follow the best practices described above.
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@ TODO
|
|||
typedef struct uiControlOSVtable uiControlOSVtable;
|
||||
struct uiControlOSVtable {
|
||||
size_t Size;
|
||||
GtkWidget *(*Handle)(uiControl *c, void *implData);
|
||||
};
|
||||
```
|
||||
|
||||
|
@ -23,4 +24,22 @@ You are responsible for allocating and initializing this struct. To do so, you s
|
|||
|
||||
Each method takes at least two parameters. The first, `c`, is the `uiControl` itself. The second, `implData`, is the implementation data pointer; it is the same as the pointer returned by `uiControlImplData(c)`, and is provided here as a convenience.
|
||||
|
||||
Each method is named for the `uiUnixControl` function that it implements. As such, details on how to implement these methods are documented alongside those functions. For instance, instructions on implementing `TODO()` are given under the documentation for `TODO()`.
|
||||
Each method is named for the `uiUnixControl` function that it implements. As such, details on how to implement these methods are documented alongside those functions. For instance, instructions on implementing `Handle()` are given under the documentation for `uiUnixControlHandle()`.
|
||||
|
||||
### `uiUnixControlHandle()`
|
||||
|
||||
```objective-c
|
||||
uiprivExtern GtkWidget *uiUnixControlHandle(uiControl *c);
|
||||
```
|
||||
|
||||
`uiUnixControlHandle()` returns the GtkWidget that underpins `c`, or `NULL` if `c` does not have any underlying widget associated with it when called.
|
||||
|
||||
The widget returned by `uiUnixControlHandle()` is owned by `c`; you do not receive a reference to it at all. The widget is valid until either `c` is destroyed or until `c` decides to destroy the widget; you can handle the latter by catching TODO. In general, you should not store the returned widget pointer directly for later use, nor should you attempt to acquire a reference to the returned object. Instead, use the returned handle immediately if you have to, or follow TODO if you need to adjust properties of the handle that should persist across handle destruction/creation.
|
||||
|
||||
`uiWindow`s have a single handle of type `GtkWindow` that is created when the `uiWindow` is created and destroyed when the `uiWindow` is destroyed. Despite this, you should still follow the best practices described above.
|
||||
|
||||
For all other `uiControl`s defined by libui, the returned object is of the appropriate `GtkWidget` subclass:
|
||||
|
||||
* TODO
|
||||
|
||||
It is a programmer error to pass `NULL` for `c`. TODO a non-`uiControl`?
|
||||
|
|
|
@ -1,8 +1,14 @@
|
|||
// 10 june 2019
|
||||
#include "test_unix.h"
|
||||
|
||||
static GtkWidget *osVtableNopHandle(uiControl *c, void *implData)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const uiControlOSVtable osVtable = {
|
||||
.Size = sizeof (uiControlOSVtable),
|
||||
.Handle = osVtableNopHandle,
|
||||
};
|
||||
|
||||
const uiControlOSVtable *testOSVtable(void)
|
||||
|
@ -23,3 +29,26 @@ Test(WrongControlOSVtableSizeIsProgrammerError)
|
|||
uiRegisterControlType("name", &vtable, &osvt, 0);
|
||||
endCheckProgrammerError(ctx);
|
||||
}
|
||||
|
||||
Test(ControlOSVtableWithMissingHandleMethodIsProgrammerError)
|
||||
{
|
||||
uiControlVtable vtable;
|
||||
uiControlOSVtable osvt;
|
||||
void *ctx;
|
||||
|
||||
testControlLoadNopVtable(&vtable);
|
||||
ctx = beginCheckProgrammerError("uiRegisterControlType(): required uiControlOSVtable method Handle() missing for uiControl type name");
|
||||
osvt = osVtable;
|
||||
osvt.Handle = NULL;
|
||||
uiRegisterControlType("name", &vtable, &osvt, 0);
|
||||
endCheckProgrammerError(ctx);
|
||||
}
|
||||
|
||||
Test(GettingUnixHandleOfNullControlIsProgrammerError)
|
||||
{
|
||||
void *ctx;
|
||||
|
||||
ctx = beginCheckProgrammerError("uiUnixControlHandle(): invalid null pointer for uiControl");
|
||||
uiUnixControlHandle(NULL);
|
||||
endCheckProgrammerError(ctx);
|
||||
}
|
||||
|
|
|
@ -30,6 +30,7 @@ elif libui_OS == 'haiku'
|
|||
else
|
||||
libui_test_sources += files([
|
||||
'controls_unix.c',
|
||||
'window_unix.c',
|
||||
])
|
||||
endif
|
||||
|
||||
|
@ -77,6 +78,18 @@ elif libui_OS == 'darwin'
|
|||
required: true),
|
||||
]
|
||||
endif
|
||||
elif libui_OS == 'haiku'
|
||||
# TODO
|
||||
else
|
||||
# static mode already gives us these dependencies
|
||||
if libui_mode != 'static'
|
||||
libui_test_deps += [
|
||||
dependency('gtk+-3.0',
|
||||
version: '>=3.10.0',
|
||||
method: 'pkg-config',
|
||||
required: true),
|
||||
]
|
||||
endif
|
||||
endif
|
||||
|
||||
pymod = import('python')
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
// 10 june 2019
|
||||
// TODO proper macros and headers
|
||||
#include <gtk/gtk.h>
|
||||
#define libuiOSHeader "../ui_unix.h"
|
||||
#include "test.h"
|
||||
|
|
|
@ -36,7 +36,7 @@ static void testSetWindowTitleImplFull(const char *file, long line, const char *
|
|||
nsw = (NSWindow *) uiDarwinControlHandle(uiControl(w));
|
||||
got = [[nsw title] UTF8String];
|
||||
if (!utf8equal(got, want))
|
||||
utf8diffErrorFull(file, line, "uiWindowTitle() reported wrong title after uiWindowSetTitle()", got, want);
|
||||
utf8diffErrorFull(file, line, "-[NSWindow title] reported wrong title after uiWindowSetTitle()", got, want);
|
||||
uiControlFree(uiControl(w));
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,78 @@
|
|||
// 24 may 2020
|
||||
#include "test_unix.h"
|
||||
|
||||
Test(WindowHasHandleFromStart)
|
||||
{
|
||||
uiWindow *a;
|
||||
|
||||
a = uiNewWindow();
|
||||
if (uiUnixControlHandle(uiControl(a)) == NULL)
|
||||
TestErrorf("uiUnixControlHandle(brand new uiWindow) is NULL; should not be");
|
||||
uiControlFree(uiControl(a));
|
||||
}
|
||||
|
||||
Test(InitialWindowTitleIsEmptyString_OSLevel)
|
||||
{
|
||||
uiWindow *w;
|
||||
GtkWindow *gw;
|
||||
const char *title;
|
||||
|
||||
w = uiNewWindow();
|
||||
gw = GTK_WINDOW(uiUnixControlHandle(uiControl(w)));
|
||||
title = gtk_window_get_title(gw);
|
||||
if (!utf8equal(title, testUTF8Empty))
|
||||
utf8diffError("brand new uiWindow has wrong title", title, testUTF8Empty);
|
||||
uiControlFree(uiControl(w));
|
||||
}
|
||||
|
||||
static void testSetWindowTitleImplFull(const char *file, long line, const char *title, const char *want)
|
||||
{
|
||||
uiWindow *w;
|
||||
GtkWindow *gw;
|
||||
const char *got;
|
||||
|
||||
w = uiNewWindow();
|
||||
uiWindowSetTitle(w, title);
|
||||
gw = GTK_WINDOW(uiUnixControlHandle(uiControl(w)));
|
||||
got = gtk_window_get_title(gw);
|
||||
if (!utf8equal(got, want))
|
||||
utf8diffErrorFull(file, line, "gtk_window_get_title() reported wrong title after uiWindowSetTitle()", got, want);
|
||||
uiControlFree(uiControl(w));
|
||||
}
|
||||
|
||||
#define testSetWindowTitleImpl(title, want) testSetWindowTitleImplFull(__FILE__, __LINE__, title, want)
|
||||
|
||||
Test(SetWindowTitle_OSLevel_Empty)
|
||||
{
|
||||
testSetWindowTitleImpl(testUTF8Empty, testUTF8Empty);
|
||||
}
|
||||
|
||||
Test(SetWindowTitle_OSLevel_ASCIIOnly)
|
||||
{
|
||||
testSetWindowTitleImpl(testUTF8ASCIIOnly, testUTF8ASCIIOnly);
|
||||
}
|
||||
|
||||
Test(SetWindowTitle_OSLevel_WithTwoByte)
|
||||
{
|
||||
testSetWindowTitleImpl(testUTF8WithTwoByte, testUTF8WithTwoByte);
|
||||
}
|
||||
|
||||
Test(SetWindowTitle_OSLevel_WithThreeByte)
|
||||
{
|
||||
testSetWindowTitleImpl(testUTF8WithThreeByte, testUTF8WithThreeByte);
|
||||
}
|
||||
|
||||
Test(SetWindowTitle_OSLevel_WithFourByte)
|
||||
{
|
||||
testSetWindowTitleImpl(testUTF8WithFourByte, testUTF8WithFourByte);
|
||||
}
|
||||
|
||||
Test(SetWindowTitle_OSLevel_Combined)
|
||||
{
|
||||
testSetWindowTitleImpl(testUTF8Combined, testUTF8Combined);
|
||||
}
|
||||
|
||||
Test(SetWindowTitle_OSLevel_Invalid)
|
||||
{
|
||||
testSetWindowTitleImpl(testUTF8InvalidInput, testUTF8InvalidOutput);
|
||||
}
|
|
@ -13,8 +13,11 @@ extern "C" {
|
|||
|
||||
struct uiControlOSVtable {
|
||||
size_t Size;
|
||||
GtkWidget *(*Handle)(uiControl *c, void *implData);
|
||||
};
|
||||
|
||||
uiprivExtern GtkWidget *uiUnixControlHandle(uiControl *c);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -1,12 +1,18 @@
|
|||
// 8 june 2019
|
||||
#include "uipriv_unix.h"
|
||||
|
||||
bool uiprivOSVtableValid(const uiControlOSVtable *osVtable, const char *func)
|
||||
bool uiprivOSVtableValid(const char *name, const uiControlOSVtable *osVtable, const char *func)
|
||||
{
|
||||
if (osVtable->Size != sizeof (uiControlOSVtable)) {
|
||||
uiprivProgrammerErrorWrongStructSize(osVtable->Size, "uiControlOSVtable", func);
|
||||
return false;
|
||||
}
|
||||
#define checkMethod(method) \
|
||||
if (osVtable->method == NULL) { \
|
||||
uiprivProgrammerErrorRequiredControlMethodMissing(name, "uiControlOSVtable", #method, func); \
|
||||
return 0; \
|
||||
}
|
||||
checkMethod(Handle)
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -18,3 +24,19 @@ uiControlOSVtable *uiprivCloneOSVtable(const uiControlOSVtable *osVtable)
|
|||
*v2 = *osVtable;
|
||||
return v2;
|
||||
}
|
||||
|
||||
#define callVtable(method, ...) ((*(method))(__VA_ARGS__))
|
||||
|
||||
GtkWidget *uiUnixControlHandle(uiControl *c)
|
||||
{
|
||||
uiControlOSVtable *osVtable;
|
||||
|
||||
if (!uiprivCheckInitializedAndThread())
|
||||
return NULL;
|
||||
if (c == NULL) {
|
||||
uiprivProgrammerErrorNullPointer("uiControl", uiprivFunc);
|
||||
return NULL;
|
||||
}
|
||||
osVtable = uiprivControlOSVtable(c);
|
||||
return callVtable(osVtable->Handle, c, uiControlImplData(c));
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
libui_sources += [
|
||||
'unix/controls.c',
|
||||
'unix/main.c',
|
||||
'unix/window.c',
|
||||
]
|
||||
|
||||
libui_deps += [
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
// 11 june 2015
|
||||
#include "uipriv_unix.h"
|
||||
|
||||
struct uiWindow {
|
||||
uiUnixControl c;
|
||||
|
||||
struct windowImplData {
|
||||
GtkWidget *widget;
|
||||
GtkContainer *container;
|
||||
GtkWindow *window;
|
||||
|
||||
char *title;
|
||||
#if 0
|
||||
GtkWidget *vboxWidget;
|
||||
GtkContainer *vboxContainer;
|
||||
GtkBox *vbox;
|
||||
|
@ -25,8 +25,12 @@ struct uiWindow {
|
|||
void (*onContentSizeChanged)(uiWindow *, void *);
|
||||
void *onContentSizeChangedData;
|
||||
gboolean fullscreen;
|
||||
#endif
|
||||
};
|
||||
|
||||
#if 0
|
||||
// skip {
|
||||
|
||||
static gboolean onClosing(GtkWidget *win, GdkEvent *e, gpointer data)
|
||||
{
|
||||
uiWindow *w = uiWindow(data);
|
||||
|
@ -120,11 +124,6 @@ char *uiWindowTitle(uiWindow *w)
|
|||
return uiUnixStrdupText(gtk_window_get_title(w->window));
|
||||
}
|
||||
|
||||
void uiWindowSetTitle(uiWindow *w, const char *title)
|
||||
{
|
||||
gtk_window_set_title(w->window, title);
|
||||
}
|
||||
|
||||
void uiWindowContentSize(uiWindow *w, int *width, int *height)
|
||||
{
|
||||
GtkAllocation allocation;
|
||||
|
@ -229,18 +228,20 @@ void uiWindowSetMargined(uiWindow *w, int margined)
|
|||
uiprivSetMargined(w->childHolderContainer, w->margined);
|
||||
}
|
||||
|
||||
uiWindow *uiNewWindow(const char *title, int width, int height, int hasMenubar)
|
||||
// } skip
|
||||
#endif
|
||||
|
||||
static bool windowInit(uiControl *c, void *implData, void *initData)
|
||||
{
|
||||
uiWindow *w;
|
||||
struct windowImplData *wi = (struct windowImplData *) implData;
|
||||
|
||||
uiUnixNewControl(uiWindow, w);
|
||||
wi->widget = gtk_window_new(GTK_WINDOW_TOPLEVEL);
|
||||
wi->container = GTK_CONTAINER(wi->widget);
|
||||
wi->window = GTK_WINDOW(wi->widget);
|
||||
|
||||
w->widget = gtk_window_new(GTK_WINDOW_TOPLEVEL);
|
||||
w->container = GTK_CONTAINER(w->widget);
|
||||
w->window = GTK_WINDOW(w->widget);
|
||||
|
||||
gtk_window_set_title(w->window, title);
|
||||
gtk_window_resize(w->window, width, height);
|
||||
gtk_window_set_title(wi->window, "");
|
||||
#if 0
|
||||
gtk_window_resize(wi->window, width, height);
|
||||
|
||||
w->vboxWidget = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
|
||||
w->vboxContainer = GTK_CONTAINER(w->vboxWidget);
|
||||
|
@ -270,10 +271,87 @@ uiWindow *uiNewWindow(const char *title, int width, int height, int hasMenubar)
|
|||
g_signal_connect(w->childHolderWidget, "size-allocate", G_CALLBACK(onSizeAllocate), w);
|
||||
uiWindowOnClosing(w, defaultOnClosing, NULL);
|
||||
uiWindowOnContentSizeChanged(w, defaultOnPositionContentSizeChanged, NULL);
|
||||
#endif
|
||||
|
||||
// normally it's SetParent() that does this, but we can't call SetParent() on a uiWindow
|
||||
// TODO we really need to clean this up, especially since see uiWindowDestroy() above
|
||||
g_object_ref(w->widget);
|
||||
// TODO replace this with a function that does this and sets the visibility stuff
|
||||
g_object_ref(wi->widget);
|
||||
|
||||
return w;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void windowFree(uiControl *c, void *implData)
|
||||
{
|
||||
struct windowImplData *wi = (struct windowImplData *) implData;
|
||||
|
||||
if (wi->title != NULL) {
|
||||
uiprivFreeUTF8(wi->title);
|
||||
wi->title = NULL;
|
||||
}
|
||||
// use gtk_widget_destroy() instead of g_object_unref() because GTK+ has internal references (see #165)
|
||||
// TODO just do this in general?
|
||||
gtk_widget_destroy(wi->widget);
|
||||
}
|
||||
|
||||
static void windowParentChanging(uiControl *c, void *implData, uiControl *oldParent)
|
||||
{
|
||||
uiprivProgrammerErrorCannotHaveWindowsAsChildren();
|
||||
}
|
||||
|
||||
static void windowParentChanged(uiControl *c, void *implData, uiControl *newParent)
|
||||
{
|
||||
uiprivProgrammerErrorCannotHaveWindowsAsChildren();
|
||||
}
|
||||
|
||||
static GtkWidget *windowHandle(uiControl *c, void *implData)
|
||||
{
|
||||
struct windowImplData *wi = (struct windowImplData *) implData;
|
||||
|
||||
return wi->widget;
|
||||
}
|
||||
|
||||
static const uiControlVtable windowVtable = {
|
||||
.Size = sizeof (uiControlVtable),
|
||||
.Init = windowInit,
|
||||
.Free = windowFree,
|
||||
.ParentChanging = windowParentChanging,
|
||||
.ParentChanged = windowParentChanged,
|
||||
};
|
||||
|
||||
static const uiControlOSVtable windowOSVtable = {
|
||||
.Size = sizeof (uiControlOSVtable),
|
||||
.Handle = windowHandle,
|
||||
};
|
||||
|
||||
static uint32_t windowType = 0;
|
||||
|
||||
uint32_t uiprivSysWindowType(void)
|
||||
{
|
||||
if (windowType == 0)
|
||||
windowType = uiRegisterControlType("uiWindow", &windowVtable, &windowOSVtable, sizeof (struct windowImplData));
|
||||
return windowType;
|
||||
}
|
||||
|
||||
uiWindow *uiprivSysNewWindow(void)
|
||||
{
|
||||
return (uiWindow *) uiNewControl(uiWindowType(), NULL);
|
||||
}
|
||||
|
||||
const char *uiprivSysWindowTitle(uiWindow *w)
|
||||
{
|
||||
struct windowImplData *wi = (struct windowImplData *) uiControlImplData(uiControl(w));
|
||||
|
||||
if (wi->title == NULL)
|
||||
// TODO replace this with a dedicated UTF-8 empty string object
|
||||
return "";
|
||||
return wi->title;
|
||||
}
|
||||
|
||||
void uiprivSysWindowSetTitle(uiWindow *w, const char *title)
|
||||
{
|
||||
struct windowImplData *wi = (struct windowImplData *) uiControlImplData(uiControl(w));
|
||||
|
||||
if (wi->title != NULL)
|
||||
uiprivFreeUTF8(wi->title);
|
||||
wi->title = uiprivSanitizeUTF8(title);
|
||||
gtk_window_set_title(wi->window, wi->title);
|
||||
}
|
Loading…
Reference in New Issue