diff --git a/redo/basicctrls_unix.go b/redo/basicctrls_unix.go index 67d844b..1271b88 100644 --- a/redo/basicctrls_unix.go +++ b/redo/basicctrls_unix.go @@ -16,38 +16,8 @@ import "C" // TODOs: // - standalone label on its own: should it be centered or not? -type widgetbase struct { - widget *C.GtkWidget -} - -func newWidget(w *C.GtkWidget) *widgetbase { - return &widgetbase{ - widget: w, - } -} - -// these few methods are embedded by all the various Controls since they all will do the same thing - -type controlParent struct { - c *C.GtkContainer -} - -func (w *widgetbase) setParent(c *controlParent) { - C.gtk_container_add(c.c, w.widget) - // make sure the new widget is shown - C.gtk_widget_show_all(w.widget) -} - -func (w *widgetbase) containerShow() { - C.gtk_widget_show_all(w.widget) -} - -func (w *widgetbase) containerHide() { - C.gtk_widget_hide(w.widget) -} - type button struct { - *widgetbase + *controlbase button *C.GtkButton clicked *event } @@ -55,7 +25,7 @@ type button struct { // shared code for setting up buttons, check boxes, etc. func finishNewButton(widget *C.GtkWidget, event string, handler unsafe.Pointer) *button { b := &button{ - widgetbase: newWidget(widget), + controlbase: newControl(widget), button: (*C.GtkButton)(unsafe.Pointer(widget)), clicked: newEvent(), } @@ -129,14 +99,14 @@ func (c *checkbox) SetChecked(checked bool) { } type textField struct { - *widgetbase + *controlbase entry *C.GtkEntry } func startNewTextField() *textField { w := C.gtk_entry_new() return &textField{ - widgetbase: newWidget(w), + controlbase: newControl(w), entry: (*C.GtkEntry)(unsafe.Pointer(w)), } } @@ -162,22 +132,26 @@ func (t *textField) SetText(text string) { } type label struct { - *widgetbase - misc *C.GtkMisc - label *C.GtkLabel - standalone bool + *controlbase + misc *C.GtkMisc + label *C.GtkLabel + standalone bool + supercommitResize func(c *allocation, d *sizing) } func finishNewLabel(text string, standalone bool) *label { ctext := togstr(text) defer freegstr(ctext) widget := C.gtk_label_new(ctext) - return &label{ - widgetbase: newWidget(widget), + l := &label{ + controlbase: newControl(widget), misc: (*C.GtkMisc)(unsafe.Pointer(widget)), label: (*C.GtkLabel)(unsafe.Pointer(widget)), standalone: standalone, } + l.supercommitResize = l.fcommitResize + l.fcommitResize = l.labelcommitResize + return l } func newLabel(text string) Label { @@ -198,7 +172,7 @@ func (l *label) SetText(text string) { C.gtk_label_set_text(l.label, ctext) } -func (l *label) commitResize(c *allocation, d *sizing) { +func (l *label) labelcommitResize(c *allocation, d *sizing) { if !l.standalone && c.neighbor != nil { c.neighbor.getAuxResizeInfo(d) if d.shouldVAlignTop { @@ -208,5 +182,5 @@ func (l *label) commitResize(c *allocation, d *sizing) { C.gtk_misc_set_alignment(l.misc, 0, 0.5) } } - l.widgetbase.commitResize(c, d) + l.supercommitResize(c, d) } diff --git a/redo/containerctrls_unix.go b/redo/containerctrls_unix.go index 36118fe..2f2c1f2 100644 --- a/redo/containerctrls_unix.go +++ b/redo/containerctrls_unix.go @@ -13,7 +13,7 @@ import ( import "C" type tab struct { - *widgetbase + *controlbase notebook *C.GtkNotebook containers []*container @@ -25,7 +25,7 @@ type tab struct { func newTab() Tab { widget := C.gtk_notebook_new() t := &tab{ - widgetbase: newWidget(widget), + controlbase: newControl(widget), notebook: (*C.GtkNotebook)(unsafe.Pointer(widget)), } // there are no scrolling arrows by default; add them in case there are too many tabs @@ -58,10 +58,7 @@ func (t *tab) Append(name string, control Control) { } } -func (t *tab) allocate(x int, y int, width int, height int, d *sizing) []*allocation { - // only prepared the tabbed control; its children will be reallocated when that one is resized - return t.widgetbase.allocate(x, y, width, height, d) -} +// no need to override Control.allocate() as only prepared the tabbed control; its children will be reallocated when that one is resized //export layoutResizing func layoutResizing(wid *C.GtkWidget, r *C.GdkRectangle, data C.gpointer) { diff --git a/redo/control_unix.go b/redo/control_unix.go new file mode 100644 index 0000000..a5746e7 --- /dev/null +++ b/redo/control_unix.go @@ -0,0 +1,112 @@ +// +build !windows,!darwin + +// 30 july 2014 + +package ui + +import ( + "unsafe" +) + +// #include "gtk_unix.h" +import "C" + +type controlbase struct { + *controldefs + widget *C.GtkWidget +} + +type controlParent struct { + c *C.GtkContainer +} + +func newControl(widget *C.GtkWidget) *controlbase { + c := new(controlbase) + c.widget = widget + c.controldefs = new(controldefs) + c.fsetParent = func(p *controlParent) { + C.gtk_container_add(p.c, c.widget) + // make sure the new widget is shown if not explicitly hidden + c.containerShow() + } + c.fcontainerShow = func() { + C.gtk_widget_show_all(c.widget) + } + c.fcontainerHide = func() { + C.gtk_widget_hide(c.widget) + } + c.fallocate = func(x int, y int, width int, height int, d *sizing) []*allocation { + // TODO split into its own function + return []*allocation{&allocation{ + x: x, + y: y, + width: width, + height: height, + this: c, + }} + } + c.fpreferredSize = func(d *sizing) (int, int) { + // GTK+ 3 makes this easy: controls can tell us what their preferred size is! + // ...actually, it tells us two things: the "minimum size" and the "natural size". + // The "minimum size" is the smallest size we /can/ display /anything/. The "natural size" is the smallest size we would /prefer/ to display. + // The difference? Minimum size takes into account things like truncation with ellipses: the minimum size of a label can allot just the ellipses! + // So we use the natural size instead. + // There is a warning about height-for-width controls, but in my tests this isn't an issue. + // For Areas, we manually save the Area size and use that, just to be safe. + +//TODO +/* + if s.ctype == c_area { + return s.areawidth, s.areaheight + } +*/ + + var r C.GtkRequisition + + C.gtk_widget_get_preferred_size(c.widget, nil, &r) + return int(r.width), int(r.height) + } + c.fcommitResize = func(a *allocation, d *sizing) { + // as we resize on size-allocate, we have to also use size-allocate on our children + // this is fine anyway; in fact, this allows us to move without knowing what the container is! + // this is what GtkBox does anyway + // thanks to tristan in irc.gimp.net/#gtk+ + + var r C.GtkAllocation + + r.x = C.int(a.x) + r.y = C.int(a.y) + r.width = C.int(a.width) + r.height = C.int(a.height) + C.gtk_widget_size_allocate(c.widget, &r) + } + c.fgetAuxResizeInfo = func(d *sizing) { +//TODO +// d.shouldVAlignTop = (s.ctype == c_listbox) || (s.ctype == c_area) + d.shouldVAlignTop = false + } + return c +} + +type scrolledcontrol struct { + *controlbase + scroller *controlbase + scrollcontainer *C.GtkContainer + scrollwindow *C.GtkScrolledWindow +} + +func newScrolledControl(widget *C.GtkWidget, native bool) *scrolledcontrol { + scroller := C.gtk_scrolled_window_new(nil, nil) + s := &scrolledcontrol{ + controlbase: newControl(widget), + scroller: newControl(scroller), + scrollcontainer: (*C.GtkContainer)(unsafe.Pointer(scroller)), + scrollwindow: (*C.GtkScrolledWindow)(unsafe.Pointer(scroller)), + } + // give the scrolled window a border (thanks to jlindgren in irc.gimp.net/#gtk+) + C.gtk_scrolled_window_set_shadow_type(s.scrollwindow, C.GTK_SHADOW_IN) + C.gtk_container_add(s.scrollcontainer, s.widget) + s.fsetParent = s.scroller.fsetParent + s.fcommitResize = s.scroller.fcommitResize + return s +} diff --git a/redo/sizing_unix.go b/redo/sizing_unix.go index abca80d..5fa7fac 100644 --- a/redo/sizing_unix.go +++ b/redo/sizing_unix.go @@ -38,58 +38,3 @@ func (c *container) beginResize() (d *sizing) { func (c *container) translateAllocationCoords(allocations []*allocation, winwidth, winheight int) { // no need for coordinate conversion with gtk+ } - -func (w *widgetbase) allocate(x int, y int, width int, height int, d *sizing) []*allocation { - return []*allocation{&allocation{ - x: x, - y: y, - width: width, - height: height, - this: w, - }} -} - -func (w *widgetbase) commitResize(c *allocation, d *sizing) { - // as we resize on size-allocate, we have to also use size-allocate on our children - // this is fine anyway; in fact, this allows us to move without knowing what the container is! - // this is what GtkBox does anyway - // thanks to tristan in irc.gimp.net/#gtk+ - - var r C.GtkAllocation - - r.x = C.int(c.x) - r.y = C.int(c.y) - r.width = C.int(c.width) - r.height = C.int(c.height) - C.gtk_widget_size_allocate(w.widget, &r) -} - -func (w *widgetbase) getAuxResizeInfo(d *sizing) { -//TODO -// d.shouldVAlignTop = (s.ctype == c_listbox) || (s.ctype == c_area) - d.shouldVAlignTop = false -} - -// GTK+ 3 makes this easy: controls can tell us what their preferred size is! -// ...actually, it tells us two things: the "minimum size" and the "natural size". -// The "minimum size" is the smallest size we /can/ display /anything/. The "natural size" is the smallest size we would /prefer/ to display. -// The difference? Minimum size takes into account things like truncation with ellipses: the minimum size of a label can allot just the ellipses! -// So we use the natural size instead. -// There is a warning about height-for-width controls, but in my tests this isn't an issue. -// For Areas, we manually save the Area size and use that, just to be safe. - -// We don't need to worry about y-offset because label alignment is "vertically center", which GtkLabel does for us. - -func (w *widgetbase) preferredSize(d *sizing) (width int, height int) { -//TODO -/* - if s.ctype == c_area { - return s.areawidth, s.areaheight - } -*/ - - var r C.GtkRequisition - - C.gtk_widget_get_preferred_size(w.widget, nil, &r) - return int(r.width), int(r.height) -} diff --git a/redo/table_unix.go b/redo/table_unix.go index 4d47405..23366f4 100644 --- a/redo/table_unix.go +++ b/redo/table_unix.go @@ -14,15 +14,11 @@ import ( import "C" type table struct { - *widgetbase + *scrolledcontrol *tablebase - treewidget *C.GtkWidget treeview *C.GtkTreeView - scrollc *C.GtkContainer - scrollwindow *C.GtkScrolledWindow - model *C.goTableModel modelgtk *C.GtkTreeModel @@ -33,19 +29,11 @@ type table struct { func finishNewTable(b *tablebase, ty reflect.Type) Table { widget := C.gtk_tree_view_new() - scroller := C.gtk_scrolled_window_new(nil, nil) t := &table{ - // TODO kludge - widgetbase: newWidget(scroller), + scrolledcontrol: newScrolledControl(widget, true), tablebase: b, - treewidget: widget, treeview: (*C.GtkTreeView)(unsafe.Pointer(widget)), - scrollc: (*C.GtkContainer)(unsafe.Pointer(scroller)), - scrollwindow: (*C.GtkScrolledWindow)(unsafe.Pointer(scroller)), } - // give the scrolled window a border (thanks to jlindgren in irc.gimp.net/#gtk+) - C.gtk_scrolled_window_set_shadow_type(t.scrollwindow, C.GTK_SHADOW_IN) - C.gtk_container_add(t.scrollc, t.treewidget) model := C.newTableModel(unsafe.Pointer(t)) t.model = model t.modelgtk = (*C.GtkTreeModel)(unsafe.Pointer(model)) @@ -60,13 +48,6 @@ func finishNewTable(b *tablebase, ty reflect.Type) Table { return t } -func (t *table) preferredSize(d *sizing) (width int, height int) { - var r C.GtkRequisition - - C.gtk_widget_get_preferred_size(t.treewidget, nil, &r) - return int(r.width), int(r.height) -} - func (t *table) Lock() { t.tablebase.Lock() d := reflect.Indirect(reflect.ValueOf(t.data))