diff --git a/redo/controls.go b/redo/controls.go index 0500ac6..e8610a5 100644 --- a/redo/controls.go +++ b/redo/controls.go @@ -9,7 +9,7 @@ type Control interface { parent(*window) // TODO enable/disable (public) // TODO show/hide (public) - // TODO sizing (likely private) + controlSizing } // Button is a clickable button that performs some task. diff --git a/redo/sizing.go b/redo/sizing.go new file mode 100644 index 0000000..a3f8c0e --- /dev/null +++ b/redo/sizing.go @@ -0,0 +1,48 @@ +// 25 june 2014 + +package ui + +type allocation struct { + x int + y int + width int + height int + this Control + neighbor Control +} + +type sizingbase struct { + xmargin int + ymargin int + xpadding int + ypadding int +} + +// this ensures that all *windows across all platforms contain the necessary functions +// if this fails to compile, we have a problem +var windowSizeEnsure interface { + beginResize() *sizing + endResize(*sizing) + translateAllocationCoords([]*allocation, int, int) +} = &window{} + +type controlSizing interface { + allocate(x int, y int, width int, height int, d *sizing) []*allocation + preferredSize(*sizing) (int, int) + commitResize(*allocation, *sizing) + getAuxResizeInfo(*sizing) +} + +func (w *window) doresize(width, height int) { + if w.child == nil { // no children; nothing to do + return + } + d := w.beginResize() + allocations := w.child.allocate(0, 0, width, height, d) + w.translateAllocationCoords(allocations, width, height) + // move in reverse so as to approximate right->left order so neighbors make sense + for i := len(allocations) - 1; i >= 0; i-- { + allocations[i].this.commitResize(allocations[i], d) + } + w.endResize(d) +} diff --git a/redo/sizing_unix.go b/redo/sizing_unix.go new file mode 100644 index 0000000..da3791e --- /dev/null +++ b/redo/sizing_unix.go @@ -0,0 +1,102 @@ +// +build !darwin + +// 23 february 2014 + +package ui + +// #include "gtk_unix.h" +import "C" + +type sizing struct { + sizingbase + + // for size calculations + // gtk+ needs nothing + + // for the actual resizing + shouldVAlignTop bool +} + +const ( + gtkXMargin = 12 + gtkYMargin = 12 + gtkXPadding = 12 + gtkYPadding = 6 +) + +func (w *window) beginResize() (d *sizing) { + d = new(sizing) + if w.spaced { + d.xmargin = gtkXMargin + d.ymargin = gtkYMargin + d.xpadding = gtkXPadding + d.ypadding = gtkYPadding + } + return d +} + +func (w *window) endResize(d *sizing) { + C.gtk_widget_queue_draw(w.widget) +} + +func (w *window) 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) { +// TODO +/* + if s.ctype == c_label && !s.alternate && c.neighbor != nil { + c.neighbor.getAuxResizeInfo(d) + if d.shouldVAlignTop { + // TODO should it be center-aligned to the first line or not + gtk_misc_set_alignment(s.widget, 0, 0) + } else { + gtk_misc_set_alignment(s.widget, 0, 0.5) + } + } +*/ + // TODO this uses w.parentw directly; change? + C.gtk_layout_move(w.parentw.layout, w.widget, C.gint(c.x), C.gint(c.y)) + C.gtk_widget_set_size_request(w.widget, C.gint(c.width), C.gint(c.height)) +} + +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/window_unix.go b/redo/window_unix.go index f110e5f..d7f670c 100644 --- a/redo/window_unix.go +++ b/redo/window_unix.go @@ -26,6 +26,8 @@ type window struct { child Control closing *event + + spaced bool } func newWindow(title string, width int, height int) *Request { @@ -162,7 +164,7 @@ func windowClosing(wid *C.GtkWidget, e *C.GdkEvent, data C.gpointer) C.gboolean func windowResizing(wid *C.GtkWidget, event *C.GdkEvent, data C.gpointer) C.gboolean { w := (*window)(unsafe.Pointer(data)) e := (*C.GdkEventConfigure)(unsafe.Pointer(event)) - _ = w // TODO + w.doresize(int(e.width), int(e.height)) // TODO this does not take CSD into account; my attempts at doing so so far have failed to work correctly in the face of rapid live resizing // TODO triggered twice on each resize or maximize for some reason??? fmt.Printf("new size %d x %d\n", e.width, e.height)