diff options
Diffstat (limited to 'src/libui_sdl/libui/unix/window.c')
-rw-r--r-- | src/libui_sdl/libui/unix/window.c | 279 |
1 files changed, 279 insertions, 0 deletions
diff --git a/src/libui_sdl/libui/unix/window.c b/src/libui_sdl/libui/unix/window.c new file mode 100644 index 0000000..ea9ba37 --- /dev/null +++ b/src/libui_sdl/libui/unix/window.c @@ -0,0 +1,279 @@ +// 11 june 2015 +#include "uipriv_unix.h" + +struct uiWindow { + uiUnixControl c; + + GtkWidget *widget; + GtkContainer *container; + GtkWindow *window; + + GtkWidget *vboxWidget; + GtkContainer *vboxContainer; + GtkBox *vbox; + + GtkWidget *childHolderWidget; + GtkContainer *childHolderContainer; + + GtkWidget *menubar; + + uiControl *child; + int margined; + + int (*onClosing)(uiWindow *, void *); + void *onClosingData; + void (*onContentSizeChanged)(uiWindow *, void *); + void *onContentSizeChangedData; + gboolean fullscreen; +}; + +static gboolean onClosing(GtkWidget *win, GdkEvent *e, gpointer data) +{ + uiWindow *w = uiWindow(data); + + // manually destroy the window ourselves; don't let the delete-event handler do it + if ((*(w->onClosing))(w, w->onClosingData)) + uiControlDestroy(uiControl(w)); + // don't continue to the default delete-event handler; we destroyed the window by now + return TRUE; +} + +static void onSizeAllocate(GtkWidget *widget, GdkRectangle *allocation, gpointer data) +{ + uiWindow *w = uiWindow(data); + + // TODO deal with spurious size-allocates + (*(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); + + // first hide ourselves + gtk_widget_hide(w->widget); + // now destroy the 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 + // use gtk_widget_destroy() instead of g_object_unref() because GTK+ has internal references (see #165) + gtk_widget_destroy(w->widget); + uiFreeControl(uiControl(w)); +} + +uiUnixControlDefaultHandle(uiWindow) + +uiControl *uiWindowParent(uiControl *c) +{ + return NULL; +} + +void uiWindowSetParent(uiControl *c, uiControl *parent) +{ + uiUserBugCannotSetParentOnToplevel("uiWindow"); +} + +static int uiWindowToplevel(uiControl *c) +{ + return 1; +} + +uiUnixControlDefaultVisible(uiWindow) + +static void uiWindowShow(uiControl *c) +{ + uiWindow *w = uiWindow(c); + + // don't use gtk_widget_show_all() as that will show all children, regardless of user settings + // don't use gtk_widget_show(); that doesn't bring to front or give keyboard focus + // (gtk_window_present() does call gtk_widget_show() though) + gtk_window_present(w->window); +} + +uiUnixControlDefaultHide(uiWindow) +uiUnixControlDefaultEnabled(uiWindow) +uiUnixControlDefaultEnable(uiWindow) +uiUnixControlDefaultDisable(uiWindow) +// TODO? +uiUnixControlDefaultSetContainer(uiWindow) + +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; + + gtk_widget_get_allocation(w->childHolderWidget, &allocation); + *width = allocation.width; + *height = allocation.height; +} + +void uiWindowSetContentSize(uiWindow *w, int width, int height) +{ + GtkAllocation childAlloc; + gint winWidth, winHeight; + + // we need to resize the child holder widget to the given size + // we can't resize that without running the event loop + // but we can do gtk_window_set_size() + // so how do we deal with the differences in sizes? + // simple arithmetic, of course! + + // from what I can tell, the return from gtk_widget_get_allocation(w->window) and gtk_window_get_size(w->window) will be the same + // this is not affected by Wayland and not affected by GTK+ builtin CSD + // so we can safely juse use them to get the real window size! + // since we're using gtk_window_resize(), use the latter + gtk_window_get_size(w->window, &winWidth, &winHeight); + + // now get the child holder widget's current allocation + gtk_widget_get_allocation(w->childHolderWidget, &childAlloc); + // and punch that out of the window size + winWidth -= childAlloc.width; + winHeight -= childAlloc.height; + + // now we just need to add the new size back in + winWidth += width; + winHeight += height; + // and set it + // this will not move the window in my tests, so we're good + gtk_window_resize(w->window, winWidth, winHeight); +} + +int uiWindowFullscreen(uiWindow *w) +{ + return w->fullscreen; +} + +// TODO use window-state-event to track +// TODO does this send an extra size changed? +// TODO what behavior do we want? +void uiWindowSetFullscreen(uiWindow *w, int fullscreen) +{ + w->fullscreen = fullscreen; + if (w->fullscreen) + gtk_window_fullscreen(w->window); + else + gtk_window_unfullscreen(w->window); +} + +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; +} + +int uiWindowBorderless(uiWindow *w) +{ + return gtk_window_get_decorated(w->window) == FALSE; +} + +void uiWindowSetBorderless(uiWindow *w, int borderless) +{ + gtk_window_set_decorated(w->window, borderless == 0); +} + +// TODO save and restore expands and aligns +void uiWindowSetChild(uiWindow *w, uiControl *child) +{ + if (w->child != NULL) { + 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); + } +} + +int uiWindowMargined(uiWindow *w) +{ + return w->margined; +} + +void uiWindowSetMargined(uiWindow *w, int margined) +{ + w->margined = margined; + setMargined(w->childHolderContainer, w->margined); +} + +uiWindow *uiNewWindow(const char *title, int width, int height, int hasMenubar) +{ + uiWindow *w; + + uiUnixNewControl(uiWindow, w); + + 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); + + w->vboxWidget = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); + w->vboxContainer = GTK_CONTAINER(w->vboxWidget); + w->vbox = GTK_BOX(w->vboxWidget); + + // set the vbox as the GtkWindow child + gtk_container_add(w->container, w->vboxWidget); + + if (hasMenubar) { + w->menubar = makeMenubar(uiWindow(w)); + 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->childHolderWidget, "size-allocate", G_CALLBACK(onSizeAllocate), w); + uiWindowOnClosing(w, defaultOnClosing, 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, especially since see uiWindowDestroy() above + g_object_ref(w->widget); + + return w; +} |