diff --git a/gtkcalls_unix.go b/gtkcalls_unix.go index f2bce9e..39effe1 100644 --- a/gtkcalls_unix.go +++ b/gtkcalls_unix.go @@ -66,16 +66,30 @@ func gtk_window_get_size(window *gtkWidget) (int, int) { return int(width), int(height) } -func gtk_fixed_new() *gtkWidget { - return fromgtkwidget(C.gtk_fixed_new()) +// this should allow us to resize the window arbitrarily +// thanks to Company in irc.gimp.net/#gtk+ +func gtkNewWindowLayout() *gtkWidget { + layout := C.gtk_layout_new(nil, nil) + scrollarea := C.gtk_scrolled_window_new((*C.GtkAdjustment)(nil), (*C.GtkAdjustment)(nil)) + C.gtk_container_add((*C.GtkContainer)(unsafe.Pointer(scrollarea)), layout) + // never show scrollbars; we're just doing this to allow arbitrary resizes + C.gtk_scrolled_window_set_policy((*C.GtkScrolledWindow)(unsafe.Pointer(scrollarea)), + C.GTK_POLICY_NEVER, C.GTK_POLICY_NEVER) + return fromgtkwidget(scrollarea) } func gtk_container_add(container *gtkWidget, widget *gtkWidget) { C.gtk_container_add(togtkcontainer(container), togtkwidget(widget)) } -func gtk_fixed_move(container *gtkWidget, widget *gtkWidget, x int, y int) { - C.gtk_fixed_move(togtkfixed(container), togtkwidget(widget), +func gtkAddWidgetToLayout(container *gtkWidget, widget *gtkWidget) { + layout := C.gtk_bin_get_child((*C.GtkBin)(unsafe.Pointer(container))) + C.gtk_container_add((*C.GtkContainer)(unsafe.Pointer(layout)), togtkwidget(widget)) +} + +func gtkMoveWidgetInLayout(container *gtkWidget, widget *gtkWidget, x int, y int) { + layout := C.gtk_bin_get_child((*C.GtkBin)(unsafe.Pointer(container))) + C.gtk_layout_move((*C.GtkLayout)(unsafe.Pointer(layout)), togtkwidget(widget), C.gint(x), C.gint(y)) } diff --git a/gtkcasts_unix.go b/gtkcasts_unix.go index 09ba9ef..b459c46 100644 --- a/gtkcasts_unix.go +++ b/gtkcasts_unix.go @@ -61,12 +61,12 @@ func togtkcontainer(what *gtkWidget) *C.GtkContainer { return (*C.GtkContainer)(unsafe.Pointer(what)) } -func fromgtkfixed(x *C.GtkFixed) *gtkWidget { +func fromgtklayout(x *C.GtkLayout) *gtkWidget { return (*gtkWidget)(unsafe.Pointer(x)) } -func togtkfixed(what *gtkWidget) *C.GtkFixed { - return (*C.GtkFixed)(unsafe.Pointer(what)) +func togtklayout(what *gtkWidget) *C.GtkLayout { + return (*C.GtkLayout)(unsafe.Pointer(what)) } func fromgtkbutton(x *C.GtkButton) *gtkWidget { diff --git a/implementation.md b/implementation.md index 2bd4232..800ce3a 100644 --- a/implementation.md +++ b/implementation.md @@ -47,7 +47,7 @@ GTK+ is strange: there are constructor functions that return `GtkWidget *`, but As the GTK+ main loop system does not quite run in a sane way (it allows recursion, and the `gtk_main_loop_iteration_do` function only onperates on the innermost call), we cannot use the `for`/`select` template for `ui()`. Fortunately, GDK provides gdk_threads_add_idle(), which allows us to run, and optionally (and importantly) run only once, a function on the `gtk_main()` thread when not processing events. We use this, combined with a goroutine for dispatching, to handle `uitask` requests. See `our_idle_callback` in callbacks_unix.go for details. -GTK+ layout managers are not used since the UI library's layout managers are coded in a portable way. (`GtkFixed` is used instead.) This isn't ideal, but it works for now. +GTK+ layout managers are not used since the UI library's layout managers are coded in a portable way. (A `GtkLayout` in a `GtkScrolledWindow` is used instead. This is done instead of just a `GtkFixed` so that a window can be resized smaller than the size requests of its contents.) This isn't ideal, but it works for now. All event handlers take the `sysData` as their user data parameter; this means all the event-handling code is stored in static functions in callbacks_unix.go. (Early versions of the package generated signal handlers for each control on the fly, but this needed to be changed to accommodoate Area, which not only needs the `sysData` but also needs to connect to a subwidget of a subwidget (specifically the subwidget of the `GtkViewport` of a `GtkScrolledWindow`); the current setup also avoids creating closures for each and every Window and Button created, and also means we can stop having to shove those callbacks in an ever-growing slice to prevent them from being garbage collected.) Should the widget actually be a child widget of a `GtkScrolledWindow`, the `child` function and `childsigs` signal list are used to assign signals as well. diff --git a/sysdata_unix.go b/sysdata_unix.go index 8d753da..e9c507a 100644 --- a/sysdata_unix.go +++ b/sysdata_unix.go @@ -117,7 +117,7 @@ func (s *sysData) make(initText string, window *sysData) error { s.widget = <-ret if window == nil { uitask <- func() { - fixed := gtk_fixed_new() + fixed := gtkNewWindowLayout() gtk_container_add(s.widget, fixed) // TODO return the container before assigning the signals? for signame, sigfunc := range ct.signals { @@ -129,7 +129,7 @@ func (s *sysData) make(initText string, window *sysData) error { } else { s.container = window.container uitask <- func() { - gtk_container_add(s.container, s.widget) + gtkAddWidgetToLayout(s.container, s.widget) for signame, sigfunc := range ct.signals { g_signal_connect(s.widget, signame, sigfunc, s) } @@ -187,7 +187,7 @@ func (s *sysData) setText(text string) { } func (s *sysData) setRect(x int, y int, width int, height int, winheight int) error { - gtk_fixed_move(s.container, s.widget, x, y) + gtkMoveWidgetInLayout(s.container, s.widget, x, y) gtk_widget_set_size_request(s.widget, width, height) return nil } diff --git a/todo.md b/todo.md index e5e40ab..3d12c2a 100644 --- a/todo.md +++ b/todo.md @@ -45,7 +45,6 @@ important things: - make gcc (Unix)/clang (Mac OS X) pedantic about warnings/errors; also -Werror - make sure scrollbars in Listbox work identically on all platforms (specifically the existence and autohiding of both horizontal and vertical scrollbars) - pin down this behavior; also note non-editability -- GTK+ windows cannot be resized smaller than their controls's current sizes in their current positions; find out how to overrule that so they can be freely resized - the size of Listboxes on Windows does not fill the requested space completely (wait, wasn't there a style that governed this?) super ultra important things: @@ -74,6 +73,7 @@ super ultra important things: - frame sizes are a bit of a hack: the preferred size of a NSScrollView is the preferred size of its document view; the frameSize method described on the above link might be better but a real solution is optimal - make sure the image drawn on an Area looks correct on all platforms (is not cropped incorrectly or blurred) - GTK+: requested clip rect seems to be larger than the size of the Area (also larger than the visible portion? TODO) +- when resizing a GTK+ window smaller than a certain size, the controls inside will start clipping in bizarre ways (progress bars/entry lines will just cut off; editable comboboxes will stretch slightly longer than noneditable ones; the horizontal scrollbar in Area will disappear smoothly; etc.) important things: - make specific wording in documentation consistent (make/create, etc.)