aboutsummaryrefslogtreecommitdiff
path: root/src/libui_sdl/libui/unix/window.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/libui_sdl/libui/unix/window.c')
-rw-r--r--src/libui_sdl/libui/unix/window.c279
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;
+}