diff options
| author | Pietro Gagliardi <[email protected]> | 2014-10-17 20:02:34 -0400 |
|---|---|---|
| committer | Pietro Gagliardi <[email protected]> | 2014-10-17 20:02:34 -0400 |
| commit | 8f1165e0c7246a1faec655088004cce8c5e047d7 (patch) | |
| tree | 961c5712ff842e10121958f04d8ada57533221b7 | |
| parent | 344a344abd471e36ebd6ea0ced71df1ce74ea189 (diff) | |
Did most of the GTK+ migration.
| -rw-r--r-- | newctrl/area_unix.go | 26 | ||||
| -rw-r--r-- | newctrl/button_unix.go | 57 | ||||
| -rw-r--r-- | newctrl/checkbox_unix.go | 68 | ||||
| -rw-r--r-- | newctrl/container_unix.go | 57 | ||||
| -rw-r--r-- | newctrl/label_unix.go | 64 | ||||
| -rw-r--r-- | newctrl/table_unix.go | 222 | ||||
| -rw-r--r-- | newctrl/textfield_unix.go | 81 |
7 files changed, 551 insertions, 24 deletions
diff --git a/newctrl/area_unix.go b/newctrl/area_unix.go index 01e604c..fd17bc0 100644 --- a/newctrl/area_unix.go +++ b/newctrl/area_unix.go @@ -34,9 +34,8 @@ import "C" type area struct { *areabase - _widget *C.GtkWidget + *scroller drawingarea *C.GtkDrawingArea - scroller *scroller clickCounter *clickCounter @@ -59,7 +58,6 @@ func newArea(ab *areabase) Area { textfieldw := C.gtk_entry_new() a := &area{ areabase: ab, - _widget: widget, drawingarea: (*C.GtkDrawingArea)(unsafe.Pointer(widget)), scroller: newScroller(widget, false, false, true), // not natively scrollable; no border; have an overlay for OpenTextFieldAt() clickCounter: new(clickCounter), @@ -67,6 +65,7 @@ func newArea(ab *areabase) Area { textfield: (*C.GtkEntry)(unsafe.Pointer(textfieldw)), textfielddone: newEvent(), } + a.fpreferredSize = a.preferredSize for _, c := range areaCallbacks { g_signal_connect( C.gpointer(unsafe.Pointer(a.drawingarea)), @@ -492,28 +491,7 @@ var modonlykeys = map[C.guint]Modifiers{ C.GDK_KEY_Super_R: Super, } -func (a *area) widget() *C.GtkWidget { - return a._widget -} - -func (a *area) setParent(p *controlParent) { - a.scroller.setParent(p) -} - -func (a *area) allocate(x int, y int, width int, height int, d *sizing) []*allocation { - return baseallocate(a, x, y, width, height, d) -} - func (a *area) preferredSize(d *sizing) (width, height int) { // the preferred size of an Area is its size return a.width, a.height } - -func (a *area) commitResize(c *allocation, d *sizing) { - a.scroller.commitResize(c, d) -} - -func (a *area) getAuxResizeInfo(d *sizing) { - // a Label to the left of an Area should be vertically aligned to the top - d.shouldVAlignTop = true -} diff --git a/newctrl/button_unix.go b/newctrl/button_unix.go new file mode 100644 index 0000000..a1da527 --- /dev/null +++ b/newctrl/button_unix.go @@ -0,0 +1,57 @@ +// +build !windows,!darwin + +// 7 july 2014 + +package ui + +import ( + "unsafe" +) + +// #include "gtk_unix.h" +// extern void buttonClicked(GtkButton *, gpointer); +import "C" + +type button struct { + *controlSingleWidget + button *C.GtkButton + clicked *event +} + +// shared code for setting up buttons, check boxes, etc. +func newButton(text string) *button { + ctext := togstr(text) + defer freegstr(ctext) + widget := C.gtk_button_new_with_label(ctext) + b := &button{ + controlSingleWidget: newControlSingleWidget(widget), + button: (*C.GtkButton)(unsafe.Pointer(widget)), + clicked: newEvent(), + } + g_signal_connect( + C.gpointer(unsafe.Pointer(b.button)), + "clicked", + C.GCallback(C.buttonClicked), + C.gpointer(unsafe.Pointer(b))) + return b +} + +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)) + b.clicked.fire() +} diff --git a/newctrl/checkbox_unix.go b/newctrl/checkbox_unix.go new file mode 100644 index 0000000..fc27bee --- /dev/null +++ b/newctrl/checkbox_unix.go @@ -0,0 +1,68 @@ +// +build !windows,!darwin + +// 7 july 2014 + +package ui + +import ( + "unsafe" +) + +// #include "gtk_unix.h" +// extern void checkboxToggled(GtkToggleButton *, gpointer); +import "C" + +type checkbox struct { + *controlSingleWidget + 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) + c := &checkbox{ + controlSingleWidget: newControlSingleWidget(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 +} + +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 { + return fromgbool(C.gtk_toggle_button_get_active(c.toggle)) +} + +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() +} diff --git a/newctrl/container_unix.go b/newctrl/container_unix.go new file mode 100644 index 0000000..3f4d949 --- /dev/null +++ b/newctrl/container_unix.go @@ -0,0 +1,57 @@ +// +build !windows,!darwin + +// 23 february 2014 + +package ui + +import ( + "unsafe" +) + +// #include "gtk_unix.h" +import "C" + +type container struct { + *controlSingleWidget + container *C.GtkContainer +} + +type sizing struct { + sizingbase + + // for size calculations + // gtk+ needs nothing + + // for the actual resizing + // gtk+ needs nothing +} + +func newContainer() *container { + c := new(container) + c.controlSingleWidget = newControlSingleWidget(C.newContainer(unsafe.Pointer(c))) + c.container = (*C.GtkContainer)(unsafe.Pointer(c.widget)) + return c +} + +func (c *container) parent() *controlParent { + return &controlParent{c.container} +} + +const ( + gtkXMargin = 12 + gtkYMargin = 12 + gtkXPadding = 12 + gtkYPadding = 6 +) + +func (w *window) beginResize() (d *sizing) { + d = new(sizing) + if spaced { + d.xmargin = gtkXMargin + d.ymargintop = gtkYMargin + d.ymarginbottom = d.ymargintop + d.xpadding = gtkXPadding + d.ypadding = gtkYPadding + } + return d +} diff --git a/newctrl/label_unix.go b/newctrl/label_unix.go new file mode 100644 index 0000000..819c0e4 --- /dev/null +++ b/newctrl/label_unix.go @@ -0,0 +1,64 @@ +// +build !windows,!darwin + +// 7 july 2014 + +package ui + +import ( + "unsafe" +) + +// #include "gtk_unix.h" +import "C" + +type label struct { + *controlSingleWidget + misc *C.GtkMisc + label *C.GtkLabel +} + +func newLabel(text string) Label { + ctext := togstr(text) + defer freegstr(ctext) + widget := C.gtk_label_new(ctext) + l := &label{ + controlSingleWidget: newControlSingleWidget(widget), + misc: (*C.GtkMisc)(unsafe.Pointer(widget)), + label: (*C.GtkLabel)(unsafe.Pointer(widget)), + } + return l +} + +/*TODO +func newStandaloneLabel(text string) Label { + l := finishNewLabel(text, true) + // standalone labels are always at the top left + C.gtk_misc_set_alignment(l.misc, 0, 0) + return l +} +*/ + +func (l *label) Text() string { + return fromgstr(C.gtk_label_get_text(l.label)) +} + +func (l *label) SetText(text string) { + ctext := togstr(text) + defer freegstr(ctext) + C.gtk_label_set_text(l.label, ctext) +} + +/*TODO +func (l *label) commitResize(c *allocation, d *sizing) { + if !l.standalone && c.neighbor != nil { + c.neighbor.getAuxResizeInfo(d) + if d.shouldVAlignTop { + // don't bother aligning it to the first line of text in the control; this is harder than it's worth (thanks gregier in irc.gimp.net/#gtk+) + C.gtk_misc_set_alignment(l.misc, 0, 0) + } else { + C.gtk_misc_set_alignment(l.misc, 0, 0.5) + } + } + basecommitResize(l, c, d) +} +*/ diff --git a/newctrl/table_unix.go b/newctrl/table_unix.go new file mode 100644 index 0000000..931a28e --- /dev/null +++ b/newctrl/table_unix.go @@ -0,0 +1,222 @@ +// +build !windows,!darwin + +// 29 july 2014 + +package ui + +import ( + "fmt" + "reflect" + "unsafe" +) + +// #include "gtk_unix.h" +// extern void goTableModel_toggled(GtkCellRendererToggle *, gchar *, gpointer); +// extern void tableSelectionChanged(GtkTreeSelection *, gpointer); +import "C" + +type table struct { + *tablebase + + *scroller + treeview *C.GtkTreeView + + model *C.goTableModel + modelgtk *C.GtkTreeModel + selection *C.GtkTreeSelection + + pixbufs []*C.GdkPixbuf + + selected *event + + // stuff required by GtkTreeModel + nColumns C.gint + old C.gint + types []C.GType + crtocol map[*C.GtkCellRendererToggle]int +} + +var ( + attribText = togstr("text") + attribPixbuf = togstr("pixbuf") + attribActive = togstr("active") +) + +func finishNewTable(b *tablebase, ty reflect.Type) Table { + widget := C.gtk_tree_view_new() + t := &table{ + scroller: newScroller(widget, true, true, false), // natively scrollable; has a border; no overlay + tablebase: b, + treeview: (*C.GtkTreeView)(unsafe.Pointer(widget)), + crtocol: make(map[*C.GtkCellRendererToggle]int), + selected: newEvent(), + } + model := C.newTableModel(unsafe.Pointer(t)) + t.model = model + t.modelgtk = (*C.GtkTreeModel)(unsafe.Pointer(model)) + t.selection = C.gtk_tree_view_get_selection(t.treeview) + g_signal_connect( + C.gpointer(unsafe.Pointer(t.selection)), + "changed", + C.GCallback(C.tableSelectionChanged), + C.gpointer(unsafe.Pointer(t))) + C.gtk_tree_view_set_model(t.treeview, t.modelgtk) + for i := 0; i < ty.NumField(); i++ { + cname := togstr(ty.Field(i).Name) + switch { + case ty.Field(i).Type == reflect.TypeOf(ImageIndex(0)): + // can't use GDK_TYPE_PIXBUF here because it's a macro that expands to a function and cgo hates that + t.types = append(t.types, C.gdk_pixbuf_get_type()) + C.tableAppendColumn(t.treeview, C.gint(i), cname, + C.gtk_cell_renderer_pixbuf_new(), attribPixbuf) + case ty.Field(i).Type.Kind() == reflect.Bool: + t.types = append(t.types, C.G_TYPE_BOOLEAN) + cr := C.gtk_cell_renderer_toggle_new() + crt := (*C.GtkCellRendererToggle)(unsafe.Pointer(cr)) + t.crtocol[crt] = i + g_signal_connect(C.gpointer(unsafe.Pointer(cr)), + "toggled", + C.GCallback(C.goTableModel_toggled), + C.gpointer(unsafe.Pointer(t))) + C.tableAppendColumn(t.treeview, C.gint(i), cname, + cr, attribActive) + default: + t.types = append(t.types, C.G_TYPE_STRING) + C.tableAppendColumn(t.treeview, C.gint(i), cname, + C.gtk_cell_renderer_text_new(), attribText) + } + freegstr(cname) // free now (not deferred) to conserve memory + } + // and for some GtkTreeModel boilerplate + t.nColumns = C.gint(ty.NumField()) + return t +} + +func (t *table) Lock() { + t.tablebase.Lock() + d := reflect.Indirect(reflect.ValueOf(t.data)) + t.old = C.gint(d.Len()) +} + +func (t *table) Unlock() { + t.unlock() + // there's a possibility that user actions can happen at this point, before the view is updated + // alas, this is something we have to deal with, because Unlock() can be called from any thread + go func() { + Do(func() { + t.RLock() + defer t.RUnlock() + d := reflect.Indirect(reflect.ValueOf(t.data)) + new := C.gint(d.Len()) + C.tableUpdate(t.model, t.old, new) + }) + }() +} + +func (t *table) LoadImageList(i ImageList) { + i.apply(&t.pixbufs) +} + +func (t *table) Selected() int { + var iter C.GtkTreeIter + + t.RLock() + defer t.RUnlock() + if C.gtk_tree_selection_get_selected(t.selection, nil, &iter) == C.FALSE { + return -1 + } + path := C.gtk_tree_model_get_path(t.modelgtk, &iter) + if path == nil { + panic(fmt.Errorf("invalid iter in Table.Selected()")) + } + defer C.gtk_tree_path_free(path) + return int(*C.gtk_tree_path_get_indices(path)) +} + +func (t *table) Select(index int) { + t.RLock() + defer t.RUnlock() + C.gtk_tree_selection_unselect_all(t.selection) + if index == -1 { + return + } + path := C.gtk_tree_path_new() + defer C.gtk_tree_path_free(path) + C.gtk_tree_path_append_index(path, C.gint(index)) + C.gtk_tree_selection_select_path(t.selection, path) +} + +func (t *table) OnSelected(f func()) { + t.selected.set(f) +} + +//export goTableModel_get_n_columns +func goTableModel_get_n_columns(model *C.GtkTreeModel) C.gint { + tm := (*C.goTableModel)(unsafe.Pointer(model)) + t := (*table)(tm.gotable) + return t.nColumns +} + +//export goTableModel_get_column_type +func goTableModel_get_column_type(model *C.GtkTreeModel, column C.gint) C.GType { + tm := (*C.goTableModel)(unsafe.Pointer(model)) + t := (*table)(tm.gotable) + return t.types[column] +} + +//export goTableModel_do_get_value +func goTableModel_do_get_value(data unsafe.Pointer, row C.gint, col C.gint, value *C.GValue) { + t := (*table)(data) + t.RLock() + defer t.RUnlock() + d := reflect.Indirect(reflect.ValueOf(t.data)) + datum := d.Index(int(row)).Field(int(col)) + switch { + case datum.Type() == reflect.TypeOf(ImageIndex(0)): + d := datum.Interface().(ImageIndex) + C.g_value_init(value, C.gdk_pixbuf_get_type()) + C.g_value_set_object(value, C.gpointer(unsafe.Pointer(t.pixbufs[d]))) + case datum.Kind() == reflect.Bool: + d := datum.Interface().(bool) + C.g_value_init(value, C.G_TYPE_BOOLEAN) + C.g_value_set_boolean(value, togbool(d)) + default: + s := fmt.Sprintf("%v", datum) + str := togstr(s) + defer freegstr(str) + C.g_value_init(value, C.G_TYPE_STRING) + C.g_value_set_string(value, str) + } +} + +//export goTableModel_getRowCount +func goTableModel_getRowCount(data unsafe.Pointer) C.gint { + t := (*table)(data) + t.RLock() + defer t.RUnlock() + d := reflect.Indirect(reflect.ValueOf(t.data)) + return C.gint(d.Len()) +} + +//export goTableModel_toggled +func goTableModel_toggled(cr *C.GtkCellRendererToggle, pathstr *C.gchar, data C.gpointer) { + t := (*table)(unsafe.Pointer(data)) + t.Lock() + defer t.Unlock() + path := C.gtk_tree_path_new_from_string(pathstr) + if len := C.gtk_tree_path_get_depth(path); len != 1 { + panic(fmt.Errorf("invalid path of depth %d given to goTableModel_toggled()", len)) + } + // dereference return value to get our sole member + row := *C.gtk_tree_path_get_indices(path) + col := t.crtocol[cr] + d := reflect.Indirect(reflect.ValueOf(t.data)) + datum := d.Index(int(row)).Field(int(col)) + datum.SetBool(!datum.Bool()) +} + +//export tableSelectionChanged +func tableSelectionChanged(sel *C.GtkTreeSelection, data C.gpointer) { + t := (*table)(unsafe.Pointer(data)) + t.selected.fire() +} diff --git a/newctrl/textfield_unix.go b/newctrl/textfield_unix.go new file mode 100644 index 0000000..286ad3f --- /dev/null +++ b/newctrl/textfield_unix.go @@ -0,0 +1,81 @@ +// +build !windows,!darwin + +// 7 july 2014 + +package ui + +import ( + "unsafe" +) + +// #include "gtk_unix.h" +// extern void textfieldChanged(GtkEditable *, gpointer); +// /* because cgo doesn't like GTK_STOCK_DIALOG_ERROR */ +// static inline void setErrorIcon(GtkEntry *entry) +// { +// gtk_entry_set_icon_from_stock(entry, GTK_ENTRY_ICON_SECONDARY, GTK_STOCK_DIALOG_ERROR); +// } +import "C" + +type textfield struct { + *controlSingleWidget + entry *C.GtkEntry + changed *event +} + +func startNewTextField() *textfield { + widget := C.gtk_entry_new() + t := &textfield{ + controlSingleWidget: newControlSingleWidget(widget), + entry: (*C.GtkEntry)(unsafe.Pointer(widget)), + changed: newEvent(), + } + g_signal_connect( + C.gpointer(unsafe.Pointer(t._widget)), + "changed", + C.GCallback(C.textfieldChanged), + C.gpointer(unsafe.Pointer(t))) + return t +} + +func newTextField() *textfield { + return startNewTextField() +} + +func newPasswordField() *textfield { + t := startNewTextField() + C.gtk_entry_set_visibility(t.entry, C.FALSE) + return t +} + +func (t *textfield) Text() string { + return fromgstr(C.gtk_entry_get_text(t.entry)) +} + +func (t *textfield) SetText(text string) { + ctext := togstr(text) + defer freegstr(ctext) + C.gtk_entry_set_text(t.entry, ctext) +} + +func (t *textfield) OnChanged(f func()) { + t.changed.set(f) +} + +func (t *textfield) Invalid(reason string) { + if reason == "" { + C.gtk_entry_set_icon_from_stock(t.entry, C.GTK_ENTRY_ICON_SECONDARY, nil) + return + } + C.setErrorIcon(t.entry) + creason := togstr(reason) + defer freegstr(creason) + C.gtk_entry_set_icon_tooltip_text(t.entry, C.GTK_ENTRY_ICON_SECONDARY, creason) + C.gtk_widget_error_bell(t._widget) +} + +//export textfieldChanged +func textfieldChanged(editable *C.GtkEditable, data C.gpointer) { + t := (*textfield)(unsafe.Pointer(data)) + t.changed.fire() +} |
