Migrated the GTK+ backend to the new Control system. Added controlParent to deal with interface issues; need to apply this to the Windows backend too.

This commit is contained in:
Pietro Gagliardi 2014-08-03 16:28:21 -04:00
parent fd48be68ee
commit b6d07237b4
8 changed files with 363 additions and 119 deletions

View File

@ -10,48 +10,36 @@ import (
// #include "gtk_unix.h" // #include "gtk_unix.h"
// extern void buttonClicked(GtkButton *, gpointer); // extern void buttonClicked(GtkButton *, gpointer);
// extern void checkboxToggled(GtkToggleButton *, gpointer);
import "C" import "C"
type button struct { type button struct {
*controlbase _widget *C.GtkWidget
button *C.GtkButton button *C.GtkButton
clicked *event clicked *event
} }
// shared code for setting up buttons, check boxes, etc. // 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{ b := &button{
controlbase: newControl(widget), _widget: widget,
button: (*C.GtkButton)(unsafe.Pointer(widget)), button: (*C.GtkButton)(unsafe.Pointer(widget)),
clicked: newEvent(), clicked: newEvent(),
} }
g_signal_connect( g_signal_connect(
C.gpointer(unsafe.Pointer(b.button)), C.gpointer(unsafe.Pointer(b.button)),
event, "clicked",
C.GCallback(handler), C.GCallback(C.buttonClicked),
C.gpointer(unsafe.Pointer(b))) C.gpointer(unsafe.Pointer(b)))
return 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()) { func (b *button) OnClicked(e func()) {
b.clicked.set(e) b.clicked.set(e)
} }
//export buttonClicked
func buttonClicked(bwid *C.GtkButton, data C.gpointer) {
b := (*button)(unsafe.Pointer(data))
b.clicked.fire()
println("button clicked")
}
func (b *button) Text() string { func (b *button) Text() string {
return fromgstr(C.gtk_button_get_label(b.button)) return fromgstr(C.gtk_button_get_label(b.button))
} }
@ -61,3 +49,42 @@ func (b *button) SetText(text string) {
defer freegstr(ctext) defer freegstr(ctext)
C.gtk_button_set_label(b.button, ctext) C.gtk_button_set_label(b.button, ctext)
} }
//export buttonClicked
func buttonClicked(bwid *C.GtkButton, data C.gpointer) {
b := (*button)(unsafe.Pointer(data))
b.clicked.fire()
println("button clicked")
}
func (b *button) widget() *C.GtkWidget {
return b._widget
}
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)
}

View File

@ -9,33 +9,48 @@ import (
) )
// #include "gtk_unix.h" // #include "gtk_unix.h"
// extern void buttonClicked(GtkButton *, gpointer);
// extern void checkboxToggled(GtkToggleButton *, gpointer); // extern void checkboxToggled(GtkToggleButton *, gpointer);
import "C" import "C"
type checkbox struct { type checkbox struct {
// embed button so its methods and events carry over _widget *C.GtkWidget
*button button *C.GtkButton
toggle *C.GtkToggleButton toggle *C.GtkToggleButton
checkbox *C.GtkCheckButton checkbox *C.GtkCheckButton
toggled *event
} }
func newCheckbox(text string) *checkbox { func newCheckbox(text string) *checkbox {
ctext := togstr(text) ctext := togstr(text)
defer freegstr(ctext) defer freegstr(ctext)
widget := C.gtk_check_button_new_with_label(ctext) widget := C.gtk_check_button_new_with_label(ctext)
return &checkbox{ c := &checkbox{
button: finishNewButton(widget, "toggled", C.checkboxToggled), _widget: widget,
button: (*C.GtkButton)(unsafe.Pointer(widget)),
toggle: (*C.GtkToggleButton)(unsafe.Pointer(widget)), toggle: (*C.GtkToggleButton)(unsafe.Pointer(widget)),
checkbox: (*C.GtkCheckButton)(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 (c *checkbox) OnToggled(e func()) {
func checkboxToggled(bwid *C.GtkToggleButton, data C.gpointer) { c.toggled.set(e)
// 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) 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 { 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)) 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)
}

View File

@ -11,32 +11,31 @@ import (
// #include "gtk_unix.h" // #include "gtk_unix.h"
import "C" import "C"
type controlbase struct { // all Controls that call base methods must be this
*controldefs type controlPrivate interface {
widget *C.GtkWidget widget() *C.GtkWidget
Control
} }
type controlParent struct { type controlParent struct {
c *C.GtkContainer c *C.GtkContainer
} }
func newControl(widget *C.GtkWidget) *controlbase { func basesetParent(c controlPrivate, p *controlParent) {
c := new(controlbase) C.gtk_container_add(p.c, c.widget())
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 // make sure the new widget is shown if not explicitly hidden
c.containerShow() c.containerShow()
} }
c.fcontainerShow = func() {
C.gtk_widget_show_all(c.widget) func basecontainerShow(c controlPrivate) {
C.gtk_widget_show_all(c.widget())
} }
c.fcontainerHide = func() {
C.gtk_widget_hide(c.widget) func basecontainerHide(c controlPrivate) {
C.gtk_widget_hide(c.widget())
} }
c.fallocate = baseallocate(c)
c.fpreferredSize = func(d *sizing) (int, int) { func basepreferredSize(c controlPrivate, d *sizing) (int, int) {
// GTK+ 3 makes this easy: controls can tell us what their preferred size is! // 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". // ...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 "minimum size" is the smallest size we /can/ display /anything/. The "natural size" is the smallest size we would /prefer/ to display.
@ -54,10 +53,15 @@ func newControl(widget *C.GtkWidget) *controlbase {
var r C.GtkRequisition var r C.GtkRequisition
C.gtk_widget_get_preferred_size(c.widget, nil, &r) C.gtk_widget_get_preferred_size(c.widget(), nil, &r)
return int(r.width), int(r.height) return int(r.width), int(r.height)
} }
c.fcommitResize = func(a *allocation, d *sizing) {
func basecommitResize(c controlPrivate, a *allocation, d *sizing) {
dobasecommitResize(c.widget(), a, d)
}
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 // 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 fine anyway; in fact, this allows us to move without knowing what the container is!
// this is what GtkBox does anyway // this is what GtkBox does anyway
@ -65,38 +69,44 @@ func newControl(widget *C.GtkWidget) *controlbase {
var r C.GtkAllocation var r C.GtkAllocation
r.x = C.int(a.x) r.x = C.int(c.x)
r.y = C.int(a.y) r.y = C.int(c.y)
r.width = C.int(a.width) r.width = C.int(c.width)
r.height = C.int(a.height) r.height = C.int(c.height)
C.gtk_widget_size_allocate(c.widget, &r) C.gtk_widget_size_allocate(w, &r)
} }
c.fgetAuxResizeInfo = func(d *sizing) {
func basegetAuxResizeInfo(d *sizing) {
// controls set this to true if a Label to its left should be vertically aligned to the control's top // controls set this to true if a Label to its left should be vertically aligned to the control's top
d.shouldVAlignTop = false d.shouldVAlignTop = false
} }
return c
}
type scrolledcontrol struct { type scroller struct {
*controlbase scrollwidget *C.GtkWidget
scroller *controlbase
scrollcontainer *C.GtkContainer scrollcontainer *C.GtkContainer
scrollwindow *C.GtkScrolledWindow scrollwindow *C.GtkScrolledWindow
} }
func newScrolledControl(widget *C.GtkWidget, native bool) *scrolledcontrol { func newScroller(widget *C.GtkWidget, native bool) *scroller {
scroller := C.gtk_scrolled_window_new(nil, nil) scrollwidget := C.gtk_scrolled_window_new(nil, nil)
s := &scrolledcontrol{ s := &scroller{
controlbase: newControl(widget), scrollwidget: scrollwidget,
scroller: newControl(scroller), scrollcontainer: (*C.GtkContainer)(unsafe.Pointer(scrollwidget)),
scrollcontainer: (*C.GtkContainer)(unsafe.Pointer(scroller)), scrollwindow: (*C.GtkScrolledWindow)(unsafe.Pointer(scrollwidget)),
scrollwindow: (*C.GtkScrolledWindow)(unsafe.Pointer(scroller)),
} }
// give the scrolled window a border (thanks to jlindgren in irc.gimp.net/#gtk+) // 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_scrolled_window_set_shadow_type(s.scrollwindow, C.GTK_SHADOW_IN)
C.gtk_container_add(s.scrollcontainer, s.widget) // TODO use native here
s.fsetParent = s.scroller.fsetParent C.gtk_container_add(s.scrollcontainer, widget)
s.fcommitResize = s.scroller.fcommitResize
return s 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)
}

36
redo/controlbase.sh Executable file
View File

@ -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

View File

@ -17,11 +17,10 @@ import "C"
// - standalone label on its own: should it be centered or not? // - standalone label on its own: should it be centered or not?
type label struct { type label struct {
*controlbase _widget *C.GtkWidget
misc *C.GtkMisc misc *C.GtkMisc
label *C.GtkLabel label *C.GtkLabel
standalone bool standalone bool
supercommitResize func(c *allocation, d *sizing)
} }
func finishNewLabel(text string, standalone bool) *label { func finishNewLabel(text string, standalone bool) *label {
@ -29,13 +28,11 @@ func finishNewLabel(text string, standalone bool) *label {
defer freegstr(ctext) defer freegstr(ctext)
widget := C.gtk_label_new(ctext) widget := C.gtk_label_new(ctext)
l := &label{ l := &label{
controlbase: newControl(widget), _widget: widget,
misc: (*C.GtkMisc)(unsafe.Pointer(widget)), misc: (*C.GtkMisc)(unsafe.Pointer(widget)),
label: (*C.GtkLabel)(unsafe.Pointer(widget)), label: (*C.GtkLabel)(unsafe.Pointer(widget)),
standalone: standalone, standalone: standalone,
} }
l.supercommitResize = l.fcommitResize
l.fcommitResize = l.labelcommitResize
return l return l
} }
@ -57,7 +54,31 @@ func (l *label) SetText(text string) {
C.gtk_label_set_text(l.label, ctext) 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 { if !l.standalone && c.neighbor != nil {
c.neighbor.getAuxResizeInfo(d) c.neighbor.getAuxResizeInfo(d)
if d.shouldVAlignTop { 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) 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)
} }

View File

@ -12,7 +12,7 @@ import (
import "C" import "C"
type tab struct { type tab struct {
*controlbase _widget *C.GtkWidget
notebook *C.GtkNotebook notebook *C.GtkNotebook
tabs []*layout tabs []*layout
@ -21,7 +21,7 @@ type tab struct {
func newTab() Tab { func newTab() Tab {
widget := C.gtk_notebook_new() widget := C.gtk_notebook_new()
t := &tab{ t := &tab{
controlbase: newControl(widget), _widget: widget,
notebook: (*C.GtkNotebook)(unsafe.Pointer(widget)), notebook: (*C.GtkNotebook)(unsafe.Pointer(widget)),
} }
// there are no scrolling arrows by default; add them in case there are too many tabs // 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 // 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)
}

View File

@ -14,10 +14,11 @@ import (
import "C" import "C"
type table struct { type table struct {
*scrolledcontrol
*tablebase *tablebase
_widget *C.GtkWidget
treeview *C.GtkTreeView treeview *C.GtkTreeView
scroller *scroller
model *C.goTableModel model *C.goTableModel
modelgtk *C.GtkTreeModel modelgtk *C.GtkTreeModel
@ -30,15 +31,11 @@ type table struct {
func finishNewTable(b *tablebase, ty reflect.Type) Table { func finishNewTable(b *tablebase, ty reflect.Type) Table {
widget := C.gtk_tree_view_new() widget := C.gtk_tree_view_new()
t := &table{ t := &table{
scrolledcontrol: newScrolledControl(widget, true), scroller: newScroller(widget, true),
tablebase: b, tablebase: b,
_widget: widget,
treeview: (*C.GtkTreeView)(unsafe.Pointer(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)) model := C.newTableModel(unsafe.Pointer(t))
t.model = model t.model = model
t.modelgtk = (*C.GtkTreeModel)(unsafe.Pointer(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)) d := reflect.Indirect(reflect.ValueOf(t.data))
return C.gint(d.Len()) 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
}

View File

@ -14,15 +14,15 @@ import (
import "C" import "C"
type textField struct { type textField struct {
*controlbase _widget *C.GtkWidget
entry *C.GtkEntry entry *C.GtkEntry
} }
func startNewTextField() *textField { func startNewTextField() *textField {
w := C.gtk_entry_new() widget := C.gtk_entry_new()
return &textField{ return &textField{
controlbase: newControl(w), _widget: widget,
entry: (*C.GtkEntry)(unsafe.Pointer(w)), entry: (*C.GtkEntry)(unsafe.Pointer(widget)),
} }
} }
@ -45,3 +45,35 @@ func (t *textField) SetText(text string) {
defer freegstr(ctext) defer freegstr(ctext)
C.gtk_entry_set_text(t.entry, 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)
}