Added the beginning of the Unix (GTK+) implementation.
This commit is contained in:
parent
3a99ee6569
commit
21c1ec6f83
|
@ -0,0 +1,116 @@
|
|||
// +build !windows,!darwin,!plan9
|
||||
// TODO is there a way to simplify the above? :/
|
||||
|
||||
// 16 february 2014
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"unsafe"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// #cgo pkg-config: gtk+-3.0
|
||||
// #include <stdlib.h>
|
||||
// #include <gtk/gtk.h>
|
||||
// /* because cgo is flaky with macros */
|
||||
// static inline void gSignalConnect(GtkWidget *widget, char *signal, GCallback callback, void *data) { g_signal_connect(widget, signal, callback, data); }
|
||||
// /* so we can call uistep */
|
||||
// extern gboolean our_thread_callback(gpointer);
|
||||
import "C"
|
||||
|
||||
type (
|
||||
gtkWidget C.GtkWidget
|
||||
)
|
||||
|
||||
func fromgbool(b C.gboolean) bool {
|
||||
return b != C.FALSE
|
||||
}
|
||||
|
||||
func togbool(b bool) C.gboolean {
|
||||
if b {
|
||||
return C.TRUE
|
||||
}
|
||||
return C.FALSE
|
||||
}
|
||||
|
||||
//export our_thread_callback
|
||||
func our_thread_callback(C.gpointer) C.gboolean {
|
||||
uistep()
|
||||
return C.TRUE
|
||||
}
|
||||
|
||||
func gtk_init() bool {
|
||||
// TODO allow GTK+ standard command-line argument processing
|
||||
b := fromgbool(C.gtk_init_check((*C.int)(nil), (***C.char)(nil)))
|
||||
if !b {
|
||||
return false
|
||||
}
|
||||
// thanks to tristan in irc.gimp.net/#gtk
|
||||
C.gdk_threads_add_idle(C.GSourceFunc(C.our_thread_callback), C.gpointer(unsafe.Pointer(nil)))
|
||||
return true
|
||||
}
|
||||
|
||||
func gtk_main() {
|
||||
C.gtk_main()
|
||||
}
|
||||
|
||||
func gtk_window_new() *gtkWidget {
|
||||
// 0 == GTK_WINDOW_TOPLEVEL (the only other type, _POPUP, should not be used)
|
||||
return (*gtkWidget)(unsafe.Pointer(C.gtk_window_new(0)))
|
||||
}
|
||||
|
||||
// because *gtkWidget and *C.GtkWidget are not compatible
|
||||
func gtkwidget(g *gtkWidget) (*C.GtkWidget) {
|
||||
return (*C.GtkWidget)(unsafe.Pointer(g))
|
||||
}
|
||||
|
||||
// TODO do we need the argument?
|
||||
// TODO fine-tune the function type
|
||||
func g_signal_connect(obj *gtkWidget, sig string, callback interface{}) {
|
||||
v := reflect.ValueOf(callback)
|
||||
if v.Kind() != reflect.Func {
|
||||
panic(fmt.Sprintf("UI library internal error: callback %v given to g_signal_connect not a function", v))
|
||||
}
|
||||
ccallback := C.GCallback(unsafe.Pointer(v.Pointer()))
|
||||
csig := C.CString(sig)
|
||||
defer C.free(unsafe.Pointer(csig))
|
||||
C.gSignalConnect(gtkwidget(obj), csig, ccallback, unsafe.Pointer(nil))
|
||||
}
|
||||
|
||||
// TODO ensure this works if called on an individual control
|
||||
func gtk_widget_show(widget *gtkWidget) {
|
||||
C.gtk_widget_show_all(gtkwidget(widget))
|
||||
}
|
||||
|
||||
func gtk_widget_hide(widget *gtkWidget) {
|
||||
C.gtk_widget_hide(gtkwidget(widget))
|
||||
}
|
||||
|
||||
func gtk_window_set_title(window *gtkWidget, title string) {
|
||||
ctitle := C.CString(title)
|
||||
defer C.free(unsafe.Pointer(ctitle))
|
||||
C.gtk_window_set_title((*C.GtkWindow)(unsafe.Pointer(window)),
|
||||
(*C.gchar)(unsafe.Pointer(ctitle)))
|
||||
}
|
||||
|
||||
func gtk_window_resize(window *gtkWidget, width int, height int) {
|
||||
C.gtk_window_resize((*C.GtkWindow)(unsafe.Pointer(window)), C.gint(width), C.gint(height))
|
||||
}
|
||||
|
||||
func gtk_fixed_new() *gtkWidget {
|
||||
return (*gtkWidget)(unsafe.Pointer(C.gtk_fixed_new()))
|
||||
}
|
||||
|
||||
func gtk_container_add(container *gtkWidget, widget *gtkWidget) {
|
||||
C.gtk_container_add((*C.GtkContainer)(unsafe.Pointer(container)), gtkwidget(widget))
|
||||
}
|
||||
|
||||
func gtk_fixed_put(container *gtkWidget, widget *gtkWidget, x int, y int) {
|
||||
C.gtk_fixed_put((*C.GtkFixed)(unsafe.Pointer(container)), gtkwidget(widget),
|
||||
C.gint(x), C.gint(y))
|
||||
}
|
||||
|
||||
func gtk_widget_set_size_request(widget *gtkWidget, width int, height int) {
|
||||
C.gtk_widget_set_size_request(gtkwidget(widget), C.gint(width), C.gint(height))
|
||||
}
|
|
@ -0,0 +1,168 @@
|
|||
// +build !windows,!darwin,!plan9
|
||||
|
||||
// 16 february 2014
|
||||
//package ui
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type sysData struct {
|
||||
cSysData
|
||||
|
||||
widget *gtkWidget
|
||||
container *gtkWidget // for moving
|
||||
}
|
||||
|
||||
type classData struct {
|
||||
make func() *gtkWidget
|
||||
setText func(widget *gtkWidget, text string)
|
||||
}
|
||||
|
||||
var classTypes = [nctypes]*classData{
|
||||
c_window: &classData{
|
||||
make: gtk_window_new,
|
||||
setText: gtk_window_set_title,
|
||||
},
|
||||
c_button: &classData{
|
||||
// make: gtk_button_new,
|
||||
},
|
||||
c_checkbox: &classData{
|
||||
},
|
||||
c_combobox: &classData{
|
||||
},
|
||||
c_lineedit: &classData{
|
||||
},
|
||||
c_label: &classData{
|
||||
},
|
||||
c_listbox: &classData{
|
||||
},
|
||||
}
|
||||
|
||||
func (s *sysData) make(initText string, window *sysData) error {
|
||||
ct := classTypes[s.ctype]
|
||||
if ct.make == nil { // not yet implemented
|
||||
println(s.ctype, "not implemented")
|
||||
return nil
|
||||
}
|
||||
ret := make(chan *gtkWidget)
|
||||
defer close(ret)
|
||||
uitask <- func() {
|
||||
ret <- ct.make()
|
||||
}
|
||||
s.widget = <-ret
|
||||
println(s.widget)
|
||||
if window == nil {
|
||||
uitask <- func() {
|
||||
fixed := gtk_fixed_new()
|
||||
gtk_container_add(s.widget, fixed)
|
||||
ret <- fixed
|
||||
}
|
||||
s.container = <-ret
|
||||
} else {
|
||||
s.container = window.container
|
||||
}
|
||||
err := s.setText(initText)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error setting initial text of new window/control: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *sysData) show() error {
|
||||
ret := make(chan struct{})
|
||||
defer close(ret)
|
||||
uitask <- func() {
|
||||
gtk_widget_show(s.widget)
|
||||
ret <- struct{}{}
|
||||
}
|
||||
<-ret
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *sysData) hide() error {
|
||||
ret := make(chan struct{})
|
||||
defer close(ret)
|
||||
uitask <- func() {
|
||||
gtk_widget_hide(s.widget)
|
||||
ret <- struct{}{}
|
||||
}
|
||||
<-ret
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *sysData) setText(text string) error {
|
||||
if classTypes[s.ctype] == nil || classTypes[s.ctype].setText == nil { return nil }
|
||||
ret := make(chan struct{})
|
||||
defer close(ret)
|
||||
uitask <- func() {
|
||||
classTypes[s.ctype].setText(s.widget, text)
|
||||
ret <- struct{}{}
|
||||
}
|
||||
<-ret
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *sysData) setRect(x int, y int, width int, height int) error {
|
||||
ret := make(chan struct{})
|
||||
defer close(ret)
|
||||
uitask <- func() {
|
||||
gtk_fixed_put(s.container, s.widget, x, y)
|
||||
gtk_widget_set_size_request(s.widget, width, height)
|
||||
ret <- struct{}{}
|
||||
}
|
||||
<-ret
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *sysData) isChecked() bool {
|
||||
// TODO
|
||||
return false
|
||||
}
|
||||
|
||||
func (s *sysData) text() string {
|
||||
// TODO
|
||||
return ""
|
||||
}
|
||||
|
||||
func (s *sysData) append(what string) error {
|
||||
// TODO
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *sysData) insertBefore(what string, before int) error {
|
||||
// TODO
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *sysData) selectedIndex() int {
|
||||
// TODO
|
||||
return -1
|
||||
}
|
||||
|
||||
func (s *sysData) selectedIndices() []int {
|
||||
// TODO
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *sysData) selectedTexts() []string {
|
||||
// TODO
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *sysData) setWindowSize(width int, height int) error {
|
||||
ret := make(chan struct{})
|
||||
defer close(ret)
|
||||
uitask <- func() {
|
||||
gtk_window_resize(s.widget, width, height)
|
||||
ret <- struct{}{}
|
||||
}
|
||||
<-ret
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *sysData) delete(index int) error {
|
||||
// TODO
|
||||
return nil
|
||||
}
|
4
todo.md
4
todo.md
|
@ -19,6 +19,10 @@ so I don't forget:
|
|||
- Listbox.SelectAll
|
||||
- have Combobox.InsertBefore, Listbox.InsertBefore, Combobox.Delete, and Listbox.Delete return an error on invalid index before creation
|
||||
|
||||
important things:
|
||||
- the C.gdk_threads_add_idle logic is messy and will break when I implement actual connections...
|
||||
- there's no GTK+ error handling whatsoever; we need to figure out how it works
|
||||
|
||||
super ultra important things:
|
||||
- the windows build appears to be unstable:
|
||||
- 64-bit doesn't work, period: it crashes in malloc in wine with heap corruption warnings aplenty during DLL loading; in windows 7 CreateWindowExW complains about an unregistered window class, yet the RegisterClassW appears to have succeeded and examining the stack in WinDbg indicates the correct class name is being sent (see below)
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
// +build !windows,!darwin,!plan9
|
||||
|
||||
// 16 february 2014
|
||||
//package ui
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
var uitask chan func()
|
||||
|
||||
func ui(initDone chan error) {
|
||||
runtime.LockOSThread()
|
||||
|
||||
uitask = make(chan func())
|
||||
if gtk_init() != true {
|
||||
initDone <- fmt.Errorf("gtk_init failed (reason unknown; TODO)")
|
||||
return
|
||||
}
|
||||
initDone <- nil
|
||||
|
||||
gtk_main()
|
||||
}
|
||||
|
||||
func uistep() {
|
||||
select {
|
||||
case f := <-uitask:
|
||||
f()
|
||||
default: // do not block
|
||||
}
|
||||
}
|
||||
|
||||
// temporary
|
||||
func MsgBox(string, string, ...interface{}) {}
|
||||
func MsgBoxError(title string, text string, args ...interface{}) {panic(title+"\n"+fmt.Sprintf(text,args...))}
|
Loading…
Reference in New Issue