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++ { colname := ty.Field(i).Tag.Get("uicolumn") if colname == "" { colname = ty.Field(i).Name } cname := togstr(colname) switch { case ty.Field(i).Type == reflect.TypeOf((*image.RGBA)(nil)): // 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 newWindow(title string, width int, height int, control Control) *window { widget := C.gtk_window_new(C.GTK_WINDOW_TOPLEVEL) ctitle := togstr(title) defer freegstr(ctitle) w := &window{ widget: widget, wc: (*C.GtkContainer)(unsafe.Pointer(widget)), bin: (*C.GtkBin)(unsafe.Pointer(widget)), window: (*C.GtkWindow)(unsafe.Pointer(widget)), closing: newEvent(), } C.gtk_window_set_title(w.window, ctitle) g_signal_connect( C.gpointer(unsafe.Pointer(w.window)), "delete-event", C.GCallback(C.windowClosing), C.gpointer(unsafe.Pointer(w))) C.gtk_window_resize(w.window, C.gint(width), C.gint(height)) w.container = newContainer(control) w.container.setParent(&controlParent{w.wc}) // for dialogs; otherwise, they will be modal to all windows, not just this one w.group = C.gtk_window_group_new() C.gtk_window_group_add_window(w.group, w.window) return w }
func startNewTextField() *textfield { widget := C.gtk_entry_new() t := &textfield{ _widget: 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 startNewTextField() *textfield { widget := C.gtk_entry_new() t := &textfield{ controlSingleWidget: newControlSingleWidget(widget), editable: (*C.GtkEditable)(unsafe.Pointer(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 (w *window) openFile(f func(filename string)) { widget := C.newOpenFileDialog(w.window) window := (*C.GtkWindow)(unsafe.Pointer(widget)) dialog := (*C.GtkDialog)(unsafe.Pointer(widget)) fc := (*C.GtkFileChooser)(unsafe.Pointer(widget)) C.gtk_file_chooser_set_local_only(fc, C.FALSE) C.gtk_file_chooser_set_select_multiple(fc, C.FALSE) C.gtk_file_chooser_set_show_hidden(fc, C.TRUE) C.gtk_window_set_modal(window, C.TRUE) g_signal_connect( C.gpointer(unsafe.Pointer(dialog)), "response", C.GCallback(C.our_openfile_response_callback), C.gpointer(unsafe.Pointer(&f))) C.gtk_widget_show_all(widget) }
// 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 (w *window) openFile(f func(filename string)) { widget := C.newOpenFileDialog(w.window) window := (*C.GtkWindow)(unsafe.Pointer(widget)) dialog := (*C.GtkDialog)(unsafe.Pointer(widget)) fc := (*C.GtkFileChooser)(unsafe.Pointer(widget)) // non-local filenames are relevant mainly to GIO where we can open *anything*, not to Go os.File; see https://twitter.com/braket/status/506142849654870016 C.gtk_file_chooser_set_local_only(fc, C.TRUE) C.gtk_file_chooser_set_select_multiple(fc, C.FALSE) C.gtk_file_chooser_set_show_hidden(fc, C.TRUE) C.gtk_window_set_modal(window, C.TRUE) g_signal_connect( C.gpointer(unsafe.Pointer(dialog)), "response", C.GCallback(C.our_openfile_response_callback), C.gpointer(unsafe.Pointer(&f))) C.gtk_widget_show_all(widget) }
func newCheckbox(text string) *checkbox { ctext := togstr(text) defer freegstr(ctext) widget := C.gtk_check_button_new_with_label(ctext) 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 }
func newSpinbox(min int, max int) Spinbox { // gtk_spin_button_new_with_range() initially sets its value to the minimum value widget := C.gtk_spin_button_new_with_range(C.gdouble(min), C.gdouble(max), 1) s := &spinbox{ controlSingleWidget: newControlSingleWidget(widget), spinbutton: (*C.GtkSpinButton)(unsafe.Pointer(widget)), changed: newEvent(), } C.gtk_spin_button_set_digits(s.spinbutton, 0) // integers C.gtk_spin_button_set_numeric(s.spinbutton, C.TRUE) // digits only // this isn't specifically documented as the signal to connect to until 3.14 // it has existed as far back as 3.4, though, if not earlier // there's also ::change-value which is for keyboard changing g_signal_connect( C.gpointer(unsafe.Pointer(s.spinbutton)), "value-changed", C.GCallback(C.spinboxChanged), C.gpointer(unsafe.Pointer(s))) return s }
func gdk_threads_add_idle(idleop *gtkIdleOp) { C.gdk_threads_add_idle(C.GCallback(C.our_idle_callback), C.gpointer(unsafe.Pointer(idleop))) }
// extern gboolean our_window_configure_event_callback(GtkWidget *, GdkEvent *, gpointer); // extern void our_button_clicked_callback(GtkButton *, gpointer); // extern gboolean our_idle_callback(gpointer); // /* because cgo is flaky with macros; static inline because we have //exports */ // static inline void gSignalConnect(GtkWidget *widget, char *signal, GCallback callback, void *data) { g_signal_connect(widget, signal, callback, data); } import "C" //export our_window_delete_event_callback func our_window_delete_event_callback(widget *C.GtkWidget, event *C.GdkEvent, what C.gpointer) C.gboolean { // called when the user tries to close the window s := (*sysData)(unsafe.Pointer(what)) s.signal() return C.TRUE // do not close the window } var window_delete_event_callback = C.GCallback(C.our_window_delete_event_callback) //export our_window_configure_event_callback func our_window_configure_event_callback(widget *C.GtkWidget, event *C.GdkEvent, what C.gpointer) C.gboolean { // called when the window is resized s := (*sysData)(unsafe.Pointer(what)) if s.container != nil && s.allocate != nil { // wait for init width, height := gtk_window_get_size(s.widget) // top-left is (0,0) here s.resizeWindow(width, height) } // no need to manually redraw everything: since we use gtk_widget_set_size_request(), that queues both resize and redraw for us (thanks Company in irc.gimp.net/#gtk+) return C.FALSE // continue the event chain } var window_configure_event_callback = C.GCallback(C.our_window_configure_event_callback)
C.cairo_surface_flush(surface) toARGB(i, uintptr(unsafe.Pointer(C.cairo_image_surface_get_data(surface))), int(C.cairo_image_surface_get_stride(surface))) C.cairo_surface_mark_dirty(surface) C.cairo_set_source_surface(cr, surface, 0, 0) // origin of the surface // that just set the brush that cairo uses: we have to actually draw now // (via https://developer.gnome.org/gtkmm-tutorial/stable/sec-draw-images.html.en) C.cairo_rectangle(cr, x0, y0, x1, y1) // breaking the nrom here since we have the coordinates as a C double already C.cairo_fill(cr) C.cairo_surface_destroy(surface) // free surface return C.FALSE // signals handled without stopping the event chain (thanks to desrt again) } var area_draw_callback = C.GCallback(C.our_area_draw_callback) func translateModifiers(state C.guint, window *C.GdkWindow) C.guint { // GDK doesn't initialize the modifier flags fully; we have to explicitly tell it to (thanks to Daniel_S and daniels (two different people) in irc.gimp.net/#gtk+) C.gdk_keymap_add_virtual_modifiers( C.gdk_keymap_get_for_display(C.gdk_window_get_display(window)), (*C.GdkModifierType)(unsafe.Pointer(&state))) return state } func makeModifiers(state C.guint) (m Modifiers) { if (state & C.GDK_CONTROL_MASK) != 0 { m |= Ctrl } if (state & C.GDK_META_MASK) != 0 { // TODO get equivalent for Alt m |= Alt
} //export our_area_get_child_position_callback func our_area_get_child_position_callback(overlay *C.GtkOverlay, widget *C.GtkWidget, rect *C.GdkRectangle, data C.gpointer) C.gboolean { var nat C.GtkRequisition a := (*area)(unsafe.Pointer(data)) rect.x = C.int(a.textfieldx) rect.y = C.int(a.textfieldy) C.gtk_widget_get_preferred_size(a.textfieldw, nil, &nat) rect.width = C.int(nat.width) rect.height = C.int(nat.height) return C.TRUE } var area_get_child_position_callback = C.GCallback(C.our_area_get_child_position_callback) //export our_area_textfield_populate_popup_callback func our_area_textfield_populate_popup_callback(entry *C.GtkEntry, menu *C.GtkMenu, data C.gpointer) { a := (*area)(unsafe.Pointer(data)) a.inmenu = true } var area_textfield_populate_popup_callback = C.GCallback(C.our_area_textfield_populate_popup_callback) //export our_area_textfield_focus_out_event_callback func our_area_textfield_focus_out_event_callback(widget *C.GtkWidget, event *C.GdkEvent, data C.gpointer) C.gboolean { a := (*area)(unsafe.Pointer(data)) if !a.inmenu { C.gtk_widget_hide(a.textfieldw) a.textfielddone.fire()
}() return } // otherwise just connect the delete signal g_signal_connect_pointer(box, "response", dialog_response_callback, unsafe.Pointer(d)) C.gtk_widget_show_all(box) } //export our_dialog_response_callback func our_dialog_response_callback(box *C.GtkDialog, res C.gint, data C.gpointer) { d := (*dialog)(unsafe.Pointer(data)) d.cleanup((*C.GtkWidget)(unsafe.Pointer(box))) go d.send(res) // send on another goroutine, like everything else } var dialog_response_callback = C.GCallback(C.our_dialog_response_callback) func (d *dialog) cleanup(box *C.GtkWidget) { // have to explicitly close the dialog box, otherwise wacky things will happen C.gtk_widget_destroy(box) if d.parent != dialogWindow { C.gtk_window_group_remove_window(d.newgroup, d.pwin) C.g_object_unref(C.gpointer(unsafe.Pointer(d.newgroup))) // free the group if d.prevgroup != nil { C.gtk_window_group_add_window(d.prevgroup, d.pwin) } // otherwise it'll go back into the default group on its own } } func (d *dialog) send(res C.gint) { // this is where processing would go