diff options
| -rw-r--r-- | redo/button_unix.go | 65 | ||||
| -rw-r--r-- | redo/checkbox_unix.go | 72 | ||||
| -rw-r--r-- | redo/control_unix.go | 138 | ||||
| -rwxr-xr-x | redo/controlbase.sh | 36 | ||||
| -rw-r--r-- | redo/label_unix.go | 45 | ||||
| -rw-r--r-- | redo/tab_unix.go | 35 | ||||
| -rw-r--r-- | redo/table_unix.go | 45 | ||||
| -rw-r--r-- | redo/textfield_unix.go | 40 |
8 files changed, 360 insertions, 116 deletions
diff --git a/redo/button_unix.go b/redo/button_unix.go index 9811bda..73514b8 100644 --- a/redo/button_unix.go +++ b/redo/button_unix.go @@ -10,41 +10,46 @@ import ( // #include "gtk_unix.h" // extern void buttonClicked(GtkButton *, gpointer); -// extern void checkboxToggled(GtkToggleButton *, gpointer); import "C" type button struct { - *controlbase + _widget *C.GtkWidget button *C.GtkButton clicked *event } // shared code for setting up buttons, check boxes, etc. -func finishNewButton(widget *C.GtkWidget, event string, handler unsafe.Pointer) *button { +func newButton(text string) *button { + ctext := togstr(text) + defer freegstr(ctext) + widget := C.gtk_button_new_with_label(ctext) b := &button{ - controlbase: newControl(widget), + _widget: widget, button: (*C.GtkButton)(unsafe.Pointer(widget)), clicked: newEvent(), } g_signal_connect( C.gpointer(unsafe.Pointer(b.button)), - event, - C.GCallback(handler), + "clicked", + C.GCallback(C.buttonClicked), C.gpointer(unsafe.Pointer(b))) return b } -func newButton(text string) *button { - ctext := togstr(text) - defer freegstr(ctext) - widget := C.gtk_button_new_with_label(ctext) - return finishNewButton(widget, "clicked", C.buttonClicked) -} - func (b *button) OnClicked(e func()) { b.clicked.set(e) } +func (b *button) Text() string { + return fromgstr(C.gtk_button_get_label(b.button)) +} + +func (b *button) SetText(text string) { + ctext := togstr(text) + defer freegstr(ctext) + C.gtk_button_set_label(b.button, ctext) +} + //export buttonClicked func buttonClicked(bwid *C.GtkButton, data C.gpointer) { b := (*button)(unsafe.Pointer(data)) @@ -52,12 +57,34 @@ func buttonClicked(bwid *C.GtkButton, data C.gpointer) { println("button clicked") } -func (b *button) Text() string { - return fromgstr(C.gtk_button_get_label(b.button)) +func (b *button) widget() *C.GtkWidget { + return b._widget } -func (b *button) SetText(text string) { - ctext := togstr(text) - defer freegstr(ctext) - C.gtk_button_set_label(b.button, ctext) +func (b *button) setParent(p *controlParent) { + basesetParent(b, p) +} + +func (b *button) containerShow() { + basecontainerShow(b) +} + +func (b *button) containerHide() { + basecontainerHide(b) +} + +func (b *button) allocate(x int, y int, width int, height int, d *sizing) []*allocation { + return baseallocate(b, x, y, width, height, d) +} + +func (b *button) preferredSize(d *sizing) (width, height int) { + return basepreferredSize(b, d) +} + +func (b *button) commitResize(a *allocation, d *sizing) { + basecommitResize(b, a, d) +} + +func (b *button) getAuxResizeInfo(d *sizing) { + basegetAuxResizeInfo(d) } diff --git a/redo/checkbox_unix.go b/redo/checkbox_unix.go index 45048d1..070b84f 100644 --- a/redo/checkbox_unix.go +++ b/redo/checkbox_unix.go @@ -9,33 +9,48 @@ import ( ) // #include "gtk_unix.h" -// extern void buttonClicked(GtkButton *, gpointer); // extern void checkboxToggled(GtkToggleButton *, gpointer); import "C" type checkbox struct { - // embed button so its methods and events carry over - *button + _widget *C.GtkWidget + button *C.GtkButton toggle *C.GtkToggleButton checkbox *C.GtkCheckButton + toggled *event } func newCheckbox(text string) *checkbox { ctext := togstr(text) defer freegstr(ctext) widget := C.gtk_check_button_new_with_label(ctext) - return &checkbox{ - button: finishNewButton(widget, "toggled", C.checkboxToggled), + c := &checkbox{ + _widget: widget, + button: (*C.GtkButton)(unsafe.Pointer(widget)), toggle: (*C.GtkToggleButton)(unsafe.Pointer(widget)), checkbox: (*C.GtkCheckButton)(unsafe.Pointer(widget)), + toggled: newEvent(), } + g_signal_connect( + C.gpointer(unsafe.Pointer(c.checkbox)), + "toggled", + C.GCallback(C.checkboxToggled), + C.gpointer(unsafe.Pointer(c))) + return c } -//export checkboxToggled -func checkboxToggled(bwid *C.GtkToggleButton, data C.gpointer) { - // note that the finishNewButton() call uses the embedded *button as data - // this is fine because we're only deferring to buttonClicked() anyway - buttonClicked(nil, data) +func (c *checkbox) OnToggled(e func()) { + c.toggled.set(e) +} + +func (c *checkbox) Text() string { + return fromgstr(C.gtk_button_get_label(c.button)) +} + +func (c *checkbox) SetText(text string) { + ctext := togstr(text) + defer freegstr(ctext) + C.gtk_button_set_label(c.button, ctext) } func (c *checkbox) Checked() bool { @@ -46,3 +61,40 @@ func (c *checkbox) SetChecked(checked bool) { C.gtk_toggle_button_set_active(c.toggle, togbool(checked)) } +//export checkboxToggled +func checkboxToggled(bwid *C.GtkToggleButton, data C.gpointer) { + c := (*checkbox)(unsafe.Pointer(data)) + c.toggled.fire() +} + +func (c *checkbox) widget() *C.GtkWidget { + return c._widget +} + +func (c *checkbox) setParent(p *controlParent) { + basesetParent(c, p) +} + +func (c *checkbox) containerShow() { + basecontainerShow(c) +} + +func (c *checkbox) containerHide() { + basecontainerHide(c) +} + +func (c *checkbox) allocate(x int, y int, width int, height int, d *sizing) []*allocation { + return baseallocate(c, x, y, width, height, d) +} + +func (c *checkbox) preferredSize(d *sizing) (width, height int) { + return basepreferredSize(c, d) +} + +func (c *checkbox) commitResize(a *allocation, d *sizing) { + basecommitResize(c, a, d) +} + +func (c *checkbox) getAuxResizeInfo(d *sizing) { + basegetAuxResizeInfo(d) +} diff --git a/redo/control_unix.go b/redo/control_unix.go index d0a4c18..490d5a2 100644 --- a/redo/control_unix.go +++ b/redo/control_unix.go @@ -11,92 +11,102 @@ import ( // #include "gtk_unix.h" import "C" -type controlbase struct { - *controldefs - widget *C.GtkWidget +// all Controls that call base methods must be this +type controlPrivate interface { + widget() *C.GtkWidget + Control } 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 = baseallocate(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. +func basesetParent(c controlPrivate, p *controlParent) { + C.gtk_container_add(p.c, c.widget()) + // make sure the new widget is shown if not explicitly hidden + c.containerShow() +} + +func basecontainerShow(c controlPrivate) { + C.gtk_widget_show_all(c.widget()) +} + +func basecontainerHide(c controlPrivate) { + C.gtk_widget_hide(c.widget()) +} + +func basepreferredSize(c controlPrivate, 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 - } + if s.ctype == c_area { + return s.areawidth, s.areaheight + } */ - var r C.GtkRequisition + 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+ + C.gtk_widget_get_preferred_size(c.widget(), nil, &r) + return int(r.width), int(r.height) +} - var r C.GtkAllocation +func basecommitResize(c controlPrivate, a *allocation, d *sizing) { + dobasecommitResize(c.widget(), a, d) +} - 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) { - // controls set this to true if a Label to its left should be vertically aligned to the control's top - d.shouldVAlignTop = false - } - return c +func dobasecommitResize(w *C.GtkWidget, 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, &r) } -type scrolledcontrol struct { - *controlbase - scroller *controlbase +func basegetAuxResizeInfo(d *sizing) { + // controls set this to true if a Label to its left should be vertically aligned to the control's top + d.shouldVAlignTop = false +} + +type scroller struct { + scrollwidget *C.GtkWidget 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)), +func newScroller(widget *C.GtkWidget, native bool) *scroller { + scrollwidget := C.gtk_scrolled_window_new(nil, nil) + s := &scroller{ + scrollwidget: scrollwidget, + scrollcontainer: (*C.GtkContainer)(unsafe.Pointer(scrollwidget)), + scrollwindow: (*C.GtkScrolledWindow)(unsafe.Pointer(scrollwidget)), } // 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 + // TODO use native here + C.gtk_container_add(s.scrollcontainer, widget) return s } + +func (s *scroller) setParent(p *controlParent) { + C.gtk_container_add(p.c, s.scrollwidget) + // TODO for when hiding/showing is implemented + C.gtk_widget_show_all(s.scrollwidget) +} + +func (s *scroller) commitResize(c *allocation, d *sizing) { + dobasecommitResize(s.scrollwidget, c, d) +} diff --git a/redo/controlbase.sh b/redo/controlbase.sh new file mode 100755 index 0000000..24930c9 --- /dev/null +++ b/redo/controlbase.sh @@ -0,0 +1,36 @@ +sed "s/AAA/$1/g +s/BBB/$2/g +s/CCC/$3/g +s/DDD/$4/g" <<\END +func (AAA *BBB) CCC() DDD { + return AAA._CCC +} + +func (AAA *BBB) setParent(p *controlParent) { + basesetParent(AAA, p) +} + +func (AAA *BBB) containerShow() { + basecontainerShow(AAA) +} + +func (AAA *BBB) containerHide() { + basecontainerHide(AAA) +} + +func (AAA *BBB) allocate(x int, y int, width int, height int, d *sizing) []*allocation { + return baseallocate(AAA, x, y, width, height, d) +} + +func (AAA *BBB) preferredSize(d *sizing) (width, height int) { + return basepreferredSize(AAA, d) +} + +func (AAA *BBB) commitResize(a *allocation, d *sizing) { + basecommitResize(AAA, a, d) +} + +func (AAA *BBB) getAuxResizeInfo(d *sizing) { + basegetAuxResizeInfo(d) +} +END diff --git a/redo/label_unix.go b/redo/label_unix.go index 1776b73..7ff6c1d 100644 --- a/redo/label_unix.go +++ b/redo/label_unix.go @@ -17,11 +17,10 @@ import "C" // - standalone label on its own: should it be centered or not? type label struct { - *controlbase - misc *C.GtkMisc - label *C.GtkLabel - standalone bool - supercommitResize func(c *allocation, d *sizing) + _widget *C.GtkWidget + misc *C.GtkMisc + label *C.GtkLabel + standalone bool } func finishNewLabel(text string, standalone bool) *label { @@ -29,13 +28,11 @@ func finishNewLabel(text string, standalone bool) *label { defer freegstr(ctext) widget := C.gtk_label_new(ctext) l := &label{ - controlbase: newControl(widget), + _widget: 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 } @@ -57,7 +54,31 @@ func (l *label) SetText(text string) { C.gtk_label_set_text(l.label, ctext) } -func (l *label) labelcommitResize(c *allocation, d *sizing) { +func (l *label) widget() *C.GtkWidget { + return l._widget +} + +func (l *label) setParent(p *controlParent) { + basesetParent(l, p) +} + +func (l *label) containerShow() { + basecontainerShow(l) +} + +func (l *label) containerHide() { + basecontainerHide(l) +} + +func (l *label) allocate(x int, y int, width int, height int, d *sizing) []*allocation { + return baseallocate(l, x, y, width, height, d) +} + +func (l *label) preferredSize(d *sizing) (width, height int) { + return basepreferredSize(l, d) +} + +func (l *label) commitResize(c *allocation, d *sizing) { if !l.standalone && c.neighbor != nil { c.neighbor.getAuxResizeInfo(d) if d.shouldVAlignTop { @@ -67,5 +88,9 @@ func (l *label) labelcommitResize(c *allocation, d *sizing) { C.gtk_misc_set_alignment(l.misc, 0, 0.5) } } - l.supercommitResize(c, d) + basecommitResize(l, c, d) +} + +func (l *label) getAuxResizeInfo(d *sizing) { + basegetAuxResizeInfo(d) } diff --git a/redo/tab_unix.go b/redo/tab_unix.go index 60978ea..ba7d106 100644 --- a/redo/tab_unix.go +++ b/redo/tab_unix.go @@ -12,7 +12,7 @@ import ( import "C" type tab struct { - *controlbase + _widget *C.GtkWidget notebook *C.GtkNotebook tabs []*layout @@ -21,7 +21,7 @@ type tab struct { func newTab() Tab { widget := C.gtk_notebook_new() t := &tab{ - controlbase: newControl(widget), + _widget: widget, notebook: (*C.GtkNotebook)(unsafe.Pointer(widget)), } // there are no scrolling arrows by default; add them in case there are too many tabs @@ -42,4 +42,35 @@ func (t *tab) Append(name string, control Control) { } } +func (t *tab) widget() *C.GtkWidget { + return t._widget +} + +func (t *tab) setParent(p *controlParent) { + basesetParent(t, p) +} + +func (t *tab) containerShow() { + basecontainerShow(t) +} + +func (t *tab) containerHide() { + basecontainerHide(t) +} + +func (t *tab) allocate(x int, y int, width int, height int, d *sizing) []*allocation { + return baseallocate(t, x, y, width, height, d) +} + +func (t *tab) preferredSize(d *sizing) (width, height int) { + return basepreferredSize(t, d) +} + // no need to override Control.commitResize() as only prepared the tabbed control; its children will be reallocated when that one is resized +func (t *tab) commitResize(a *allocation, d *sizing) { + basecommitResize(t, a, d) +} + +func (t *tab) getAuxResizeInfo(d *sizing) { + basegetAuxResizeInfo(d) +} diff --git a/redo/table_unix.go b/redo/table_unix.go index 2fd2418..05baa57 100644 --- a/redo/table_unix.go +++ b/redo/table_unix.go @@ -14,10 +14,11 @@ import ( import "C" type table struct { - *scrolledcontrol *tablebase + _widget *C.GtkWidget treeview *C.GtkTreeView + scroller *scroller model *C.goTableModel modelgtk *C.GtkTreeModel @@ -30,15 +31,11 @@ type table struct { func finishNewTable(b *tablebase, ty reflect.Type) Table { widget := C.gtk_tree_view_new() t := &table{ - scrolledcontrol: newScrolledControl(widget, true), + scroller: newScroller(widget, true), tablebase: b, + _widget: widget, treeview: (*C.GtkTreeView)(unsafe.Pointer(widget)), } - t.fgetAuxResizeInfo = func(d *sizing) { - // a Label to the left of a Table should be vertically aligned to the top - // TODO do the same with Area - d.shouldVAlignTop = true - } model := C.newTableModel(unsafe.Pointer(t)) t.model = model t.modelgtk = (*C.GtkTreeModel)(unsafe.Pointer(model)) @@ -96,3 +93,37 @@ func goTableModel_getRowCount(data unsafe.Pointer) C.gint { d := reflect.Indirect(reflect.ValueOf(t.data)) return C.gint(d.Len()) } + +func (t *table) widget() *C.GtkWidget { + return t._widget +} + +func (t *table) setParent(p *controlParent) { + t.scroller.setParent(p) +} + +func (t *table) containerShow() { + basecontainerShow(t) +} + +func (t *table) containerHide() { + basecontainerHide(t) +} + +func (t *table) allocate(x int, y int, width int, height int, d *sizing) []*allocation { + return baseallocate(t, x, y, width, height, d) +} + +func (t *table) preferredSize(d *sizing) (width, height int) { + return basepreferredSize(t, d) +} + +func (t *table) commitResize(c *allocation, d *sizing) { + t.scroller.commitResize(c, d) +} + +func (t *table) getAuxResizeInfo(d *sizing) { + // a Label to the left of a Table should be vertically aligned to the top + // TODO do the same with Area + d.shouldVAlignTop = true +} diff --git a/redo/textfield_unix.go b/redo/textfield_unix.go index 4d710b1..fa79347 100644 --- a/redo/textfield_unix.go +++ b/redo/textfield_unix.go @@ -14,15 +14,15 @@ import ( import "C" type textField struct { - *controlbase + _widget *C.GtkWidget entry *C.GtkEntry } func startNewTextField() *textField { - w := C.gtk_entry_new() + widget := C.gtk_entry_new() return &textField{ - controlbase: newControl(w), - entry: (*C.GtkEntry)(unsafe.Pointer(w)), + _widget: widget, + entry: (*C.GtkEntry)(unsafe.Pointer(widget)), } } @@ -45,3 +45,35 @@ func (t *textField) SetText(text string) { defer freegstr(ctext) C.gtk_entry_set_text(t.entry, ctext) } + +func (t *textField) widget() *C.GtkWidget { + return t._widget +} + +func (t *textField) setParent(p *controlParent) { + basesetParent(t, p) +} + +func (t *textField) containerShow() { + basecontainerShow(t) +} + +func (t *textField) containerHide() { + basecontainerHide(t) +} + +func (t *textField) allocate(x int, y int, width int, height int, d *sizing) []*allocation { + return baseallocate(t, x, y, width, height, d) +} + +func (t *textField) preferredSize(d *sizing) (width, height int) { + return basepreferredSize(t, d) +} + +func (t *textField) commitResize(a *allocation, d *sizing) { + basecommitResize(t, a, d) +} + +func (t *textField) getAuxResizeInfo(d *sizing) { + basegetAuxResizeInfo(d) +} |
