diff --git a/redo/controls.go b/redo/controls.go index e8610a5..3fb2a54 100644 --- a/redo/controls.go +++ b/redo/controls.go @@ -16,24 +16,15 @@ type Control interface { type Button interface { Control - // OnClicked creates a Request to set the event handler for when the Button is clicked. - OnClicked(func(d Doer)) *Request + // OnClicked sets the event handler for when the Button is clicked. + OnClicked(func()) - // Text and SetText creates a Request that get and set the Button's label text. - Text() *Request - SetText(text string) *Request + // Text and SetText get and set the Button's label text. + Text() string + SetText(text string) } -// NewButton creates a Request to create a new Button with the given label text. -func NewButton(text string) *Request { +// NewButton creates a new Button with the given label text. +func NewButton(text string) { return newButton(text) } - -// GetNewButton is like NewButton but sends the Request along the given Doer and returns the resultant Button. -// Example: -// b := ui.GetNewButton(ui.Do, "OK") -func GetNewButton(c Doer, text string) Button { - req := newButton(text) - c <- req - return (<-req.resp).(Button) -} diff --git a/redo/controls_darwin.go b/redo/controls_darwin.go index 376c430..3c3289e 100644 --- a/redo/controls_darwin.go +++ b/redo/controls_darwin.go @@ -50,32 +50,19 @@ type button struct { clicked *event } -func newButton(text string) *Request { - c := make(chan interface{}) - return &Request{ - op: func() { - ctext := C.CString(text) - defer C.free(unsafe.Pointer(ctext)) - b := &button{ - widgetbase: newWidget(C.newButton(ctext)), - clicked: newEvent(), - } - C.buttonSetDelegate(b.id, unsafe.Pointer(b)) - c <- b - }, - resp: c, +func newButton(text string) *button { + ctext := C.CString(text) + defer C.free(unsafe.Pointer(ctext)) + b := &button{ + widgetbase: newWidget(C.newButton(ctext)), + clicked: newEvent(), } + C.buttonSetDelegate(b.id, unsafe.Pointer(b)) + return b } -func (b *button) OnClicked(e func(c Doer)) *Request { - c := make(chan interface{}) - return &Request{ - op: func() { - b.clicked.set(e) - c <- struct{}{} - }, - resp: c, - } +func (b *button) OnClicked(e func()) { + b.clicked.set(e) } //export buttonClicked @@ -85,25 +72,12 @@ func buttonClicked(xb unsafe.Pointer) { println("button clicked") } -func (b *button) Text() *Request { - c := make(chan interface{}) - return &Request{ - op: func() { - c <- C.GoString(C.buttonText(b.id)) - }, - resp: c, - } +func (b *button) Text() string { + return C.GoString(C.buttonText(b.id)) } -func (b *button) SetText(text string) *Request { - c := make(chan interface{}) - return &Request{ - op: func() { - ctext := C.CString(text) - defer C.free(unsafe.Pointer(ctext)) - C.buttonSetText(b.id, ctext) - c <- struct{}{} - }, - resp: c, - } +func (b *button) SetText(text string) { + ctext := C.CString(text) + defer C.free(unsafe.Pointer(ctext)) + C.buttonSetText(b.id, ctext) } diff --git a/redo/controls_unix.go b/redo/controls_unix.go index 0346795..730e42b 100644 --- a/redo/controls_unix.go +++ b/redo/controls_unix.go @@ -53,38 +53,25 @@ type button struct { clicked *event } -func newButton(text string) *Request { - c := make(chan interface{}) - return &Request{ - op: func() { - ctext := togstr(text) - defer freegstr(ctext) - widget := C.gtk_button_new_with_label(ctext) - b := &button{ - widgetbase: newWidget(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))) - c <- b - }, - resp: c, +func newButton(text string) *button { + ctext := togstr(text) + defer freegstr(ctext) + widget := C.gtk_button_new_with_label(ctext) + b := &button{ + widgetbase: newWidget(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(c Doer)) *Request { - c := make(chan interface{}) - return &Request{ - op: func() { - b.clicked.set(e) - c <- struct{}{} - }, - resp: c, - } +func (b *button) OnClicked(e func()) { + b.clicked.set(e) } //export buttonClicked @@ -94,25 +81,12 @@ func buttonClicked(bwid *C.GtkButton, data C.gpointer) { println("button clicked") } -func (b *button) Text() *Request { - c := make(chan interface{}) - return &Request{ - op: func() { - c <- fromgstr(C.gtk_button_get_label(b.button)) - }, - resp: c, - } +func (b *button) Text() string { + return fromgstr(C.gtk_button_get_label(b.button)) } -func (b *button) SetText(text string) *Request { - c := make(chan interface{}) - return &Request{ - op: func() { - ctext := togstr(text) - defer freegstr(ctext) - C.gtk_button_set_label(b.button, ctext) - c <- struct{}{} - }, - resp: c, - } +func (b *button) SetText(text string) { + ctext := togstr(text) + defer freegstr(ctext) + C.gtk_button_set_label(b.button, ctext) } diff --git a/redo/controls_windows.go b/redo/controls_windows.go index b9ece44..81ad2ac 100644 --- a/redo/controls_windows.go +++ b/redo/controls_windows.go @@ -31,25 +31,12 @@ func (w *widgetbase) parent(win *window) { // don't embed these as exported; let each Control decide if it should -func (w *widgetbase) text() *Request { - c := make(chan interface{}) - return &Request{ - op: func() { - c <- getWindowText(w.hwnd) - }, - resp: c, - } +func (w *widgetbase) text() string { + return getWindowText(w.hwnd) } -func (w *widgetbase) settext(text string) *Request { - c := make(chan interface{}) - return &Request{ - op: func() { - C.setWindowText(w.hwnd, toUTF16(text)) - c <- struct{}{} - }, - resp: c, - } +func (w *widgetbase) settext(text string) { + C.setWindowText(w.hwnd, toUTF16(text)) } type button struct { @@ -59,42 +46,30 @@ type button struct { var buttonclass = toUTF16("BUTTON") -func newButton(text string) *Request { - c := make(chan interface{}) - return &Request{ - op: func() { - w := newWidget(buttonclass, - C.BS_PUSHBUTTON | C.WS_TABSTOP, - 0) - C.setWindowText(w.hwnd, toUTF16(text)) - C.controlSetControlFont(w.hwnd) - b := &button{ - widgetbase: w, - clicked: newEvent(), - } - C.setButtonSubclass(w.hwnd, unsafe.Pointer(b)) - c <- b - }, - resp: c, +func newButton(text string) *button { + op: func() { + w := newWidget(buttonclass, + C.BS_PUSHBUTTON | C.WS_TABSTOP, + 0) + C.setWindowText(w.hwnd, toUTF16(text)) + C.controlSetControlFont(w.hwnd) + b := &button{ + widgetbase: w, + clicked: newEvent(), } + C.setButtonSubclass(w.hwnd, unsafe.Pointer(b)) + return b } -func (b *button) OnClicked(e func(c Doer)) *Request { - c := make(chan interface{}) - return &Request{ - op: func() { - b.clicked.set(e) - c <- struct{}{} - }, - resp: c, - } +func (b *button) OnClicked(e func()) { + b.clicked.set(e) } -func (b *button) Text() *Request { +func (b *button) Text() string { return b.text() } -func (b *button) SetText(text string) *Request { +func (b *button) SetText(text string) { return b.settext(text) } diff --git a/redo/window.go b/redo/window.go index 2dc6a6e..2494406 100644 --- a/redo/window.go +++ b/redo/window.go @@ -6,41 +6,32 @@ package ui // Windows in package ui can only contain one control; the Stack and Grid layout Controls allow you to pack multiple Controls in a Window. // Note that a Window is not itself a Control. type Window interface { - // SetControl creates a Request to the Window's child Control. - SetControl(c Control) *Request + // SetControl sets the Window's child Control. + SetControl(c Control) - // Title and SetTitle create Requests to get and set the Window's title, respectively. - Title() *Request - SetTitle(title string) *Request + // Title and SetTitle get and set the Window's title, respectively. + Title() string + SetTitle(title string) - // Show and Hide create Requests to bring the Window on-screen and off-screen, respectively. - Show() *Request - Hide() *Request + // Show and Hide bring the Window on-screen and off-screen, respectively. + Show() + Hide() - // Close creates a Request to close the Window. + // Close closes the Window. // Any Controls within the Window are destroyed, and the Window itself is also destroyed. // Attempting to use a Window after it has been closed results in undefined behavior. // Close unconditionally closes the Window; it neither raises OnClosing nor checks for a return from OnClosing. // TODO make sure the above happens on GTK+ and Mac OS X; it does on Windows - Close() *Request + Close() - // OnClosing creates a Request to register an event handler that is triggered when the user clicks the Window's close button. + // OnClosing registers an event handler that is triggered when the user clicks the Window's close button. // On systems where whole applications own windows, OnClosing is also triggered when the user asks to close the application. // If this handler returns true, the Window is closed as defined by Close above. // If this handler returns false, the Window is not closed. - OnClosing(func(c Doer) bool) *Request + OnClosing(func() bool) } -// NewWindow returns a Request to create a new Window with the given title text and size. -func NewWindow(title string, width int, height int) *Request { +// NewWindow creates a new Window with the given title text and size. +func NewWindow(title string, width int, height int) Window { return newWindow(title, width, height) } - -// GetNewWindow is like NewWindow but sends the Request along the given Doer and returns the resultant Window. -// Example: -// w := ui.GetNewWindow(ui.Do, "Main Window") -func GetNewWindow(c Doer, title string, width int, height int) Window { - req := newWindow(title, width, height) - c <- req - return (<-req.resp).(Window) -} diff --git a/redo/window_darwin.go b/redo/window_darwin.go index c84c204..4fc442a 100644 --- a/redo/window_darwin.go +++ b/redo/window_darwin.go @@ -20,106 +20,52 @@ type window struct { spaced bool } -func newWindow(title string, width int, height int) *Request { - c := make(chan interface{}) - return &Request{ - op: func() { - id := C.newWindow(C.intptr_t(width), C.intptr_t(height)) - ctitle := C.CString(title) - defer C.free(unsafe.Pointer(ctitle)) - C.windowSetTitle(id, ctitle) - w := &window{ - id: id, - closing: newEvent(), - } - C.windowSetDelegate(id, unsafe.Pointer(w)) - c <- w - }, - resp: c, +func newWindow(title string, width int, height int) *window { + id := C.newWindow(C.intptr_t(width), C.intptr_t(height)) + ctitle := C.CString(title) + defer C.free(unsafe.Pointer(ctitle)) + C.windowSetTitle(id, ctitle) + w := &window{ + id: id, + closing: newEvent(), } + C.windowSetDelegate(id, unsafe.Pointer(w)) + return w } -func (w *window) SetControl(control Control) *Request { - c := make(chan interface{}) - return &Request{ - op: func() { - if w.child != nil { // unparent existing control - w.child.unparent() - } - control.unparent() - control.parent(w) - w.child = control - c <- struct{}{} - }, - resp: c, +func (w *window) SetControl(control Control) { + if w.child != nil { // unparent existing control + w.child.unparent() } + control.unparent() + control.parent(w) + w.child = control } -func (w *window) Title() *Request { - c := make(chan interface{}) - return &Request{ - op: func() { - c <- C.GoString(C.windowTitle(w.id)) - }, - resp: c, - } +func (w *window) Title() string { + return C.GoString(C.windowTitle(w.id)) } -func (w *window) SetTitle(title string) *Request { - c := make(chan interface{}) - return &Request{ - op: func() { - ctitle := C.CString(title) - defer C.free(unsafe.Pointer(ctitle)) - C.windowSetTitle(w.id, ctitle) - c <- struct{}{} - }, - resp: c, - } +func (w *window) SetTitle(title string) { + ctitle := C.CString(title) + defer C.free(unsafe.Pointer(ctitle)) + C.windowSetTitle(w.id, ctitle) } -func (w *window) Show() *Request { - c := make(chan interface{}) - return &Request{ - op: func() { - C.windowShow(w.id) - c <- struct{}{} - }, - resp: c, - } +func (w *window) Show() { + C.windowShow(w.id) } -func (w *window) Hide() *Request { - c := make(chan interface{}) - return &Request{ - op: func() { - C.windowHide(w.id) - c <- struct{}{} - }, - resp: c, - } +func (w *window) Hide() { + C.windowHide(w.id) } -func (w *window) Close() *Request { - c := make(chan interface{}) - return &Request{ - op: func() { - C.windowClose(w.id) - c <- struct{}{} - }, - resp: c, - } +func (w *window) Close() { + C.windowClose(w.id) } -func (w *window) OnClosing(e func(c Doer) bool) *Request { - c := make(chan interface{}) - return &Request{ - op: func() { - w.closing.setbool(e) - c <- struct{}{} - }, - resp: c, - } +func (w *window) OnClosing(e func() bool) { + w.closing.setbool(e) } //export windowClosing diff --git a/redo/window_unix.go b/redo/window_unix.go index 251865b..41f3c34 100644 --- a/redo/window_unix.go +++ b/redo/window_unix.go @@ -30,128 +30,73 @@ type window struct { spaced bool } -func newWindow(title string, width int, height int) *Request { - c := make(chan interface{}) - return &Request{ - op: func() { - widget := C.gtk_window_new(C.GTK_WINDOW_TOPLEVEL) - ctitle := togstr(title) - defer freegstr(ctitle) - layoutw := C.gtk_layout_new(nil, nil) - w := &window{ - widget: widget, - container: (*C.GtkContainer)(unsafe.Pointer(widget)), - bin: (*C.GtkBin)(unsafe.Pointer(widget)), - window: (*C.GtkWindow)(unsafe.Pointer(widget)), - layoutc: (*C.GtkContainer)(unsafe.Pointer(layoutw)), - layout: (*C.GtkLayout)(unsafe.Pointer(layoutw)), - 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))) - // we connect to the layout's size-allocate, not to the window's configure-event - // this allows us to handle client-side decoration-based configurations (such as GTK+ on Wayland) properly - // also see commitResize() in sizing_unix.go for additional notes - // thanks to many people in irc.gimp.net/#gtk+ for help (including tristan for suggesting g_signal_connect_after()) - g_signal_connect_after( - C.gpointer(unsafe.Pointer(layoutw)), - "size-allocate", - C.GCallback(C.windowResizing), - C.gpointer(unsafe.Pointer(w))) - // TODO size - C.gtk_container_add(w.container, layoutw) - c <- w - }, - resp: c, +func newWindow(title string, width int, height int) *window { + widget := C.gtk_window_new(C.GTK_WINDOW_TOPLEVEL) + ctitle := togstr(title) + defer freegstr(ctitle) + layoutw := C.gtk_layout_new(nil, nil) + w := &window{ + widget: widget, + container: (*C.GtkContainer)(unsafe.Pointer(widget)), + bin: (*C.GtkBin)(unsafe.Pointer(widget)), + window: (*C.GtkWindow)(unsafe.Pointer(widget)), + layoutc: (*C.GtkContainer)(unsafe.Pointer(layoutw)), + layout: (*C.GtkLayout)(unsafe.Pointer(layoutw)), + 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))) + // we connect to the layout's size-allocate, not to the window's configure-event + // this allows us to handle client-side decoration-based configurations (such as GTK+ on Wayland) properly + // also see commitResize() in sizing_unix.go for additional notes + // thanks to many people in irc.gimp.net/#gtk+ for help (including tristan for suggesting g_signal_connect_after()) + g_signal_connect_after( + C.gpointer(unsafe.Pointer(layoutw)), + "size-allocate", + C.GCallback(C.windowResizing), + C.gpointer(unsafe.Pointer(w))) + // TODO size + C.gtk_container_add(w.container, layoutw) + return w } -func (w *window) SetControl(control Control) *Request { - c := make(chan interface{}) - return &Request{ - op: func() { - if w.child != nil { // unparent existing control - w.child.unparent() - } - control.unparent() - control.parent(w) - w.child = control - c <- struct{}{} - }, - resp: c, +func (w *window) SetControl(control Control) { + if w.child != nil { // unparent existing control + w.child.unparent() } + control.unparent() + control.parent(w) + w.child = control } -func (w *window) Title() *Request { - c := make(chan interface{}) - return &Request{ - op: func() { - c <- fromgstr(C.gtk_window_get_title(w.window)) - }, - resp: c, - } +func (w *window) Title() string { + return fromgstr(C.gtk_window_get_title(w.window)) } -func (w *window) SetTitle(title string) *Request { - c := make(chan interface{}) - return &Request{ - op: func() { - ctitle := togstr(title) - defer freegstr(ctitle) - C.gtk_window_set_title(w.window, ctitle) - c <- struct{}{} - }, - resp: c, - } +func (w *window) SetTitle(title string) { + ctitle := togstr(title) + defer freegstr(ctitle) + C.gtk_window_set_title(w.window, ctitle) } - -func (w *window) Show() *Request { - c := make(chan interface{}) - return &Request{ - op: func() { - C.gtk_widget_show_all(w.widget) - c <- struct{}{} - }, - resp: c, - } +func (w *window) Show() { + C.gtk_widget_show_all(w.widget) } -func (w *window) Hide() *Request { - c := make(chan interface{}) - return &Request{ - op: func() { - C.gtk_widget_hide(w.widget) - c <- struct{}{} - }, - resp: c, - } +func (w *window) Hide() { + C.gtk_widget_hide(w.widget) } -func (w *window) Close() *Request { - c := make(chan interface{}) - return &Request{ - op: func() { - C.gtk_widget_destroy(w.widget) - c <- struct{}{} - }, - resp: c, - } +func (w *window) Close() { + C.gtk_widget_destroy(w.widget) } -func (w *window) OnClosing(e func(c Doer) bool) *Request { - c := make(chan interface{}) - return &Request{ - op: func() { - w.closing.setbool(e) - c <- struct{}{} - }, - resp: c, - } +func (w *window) OnClosing(e func() bool) { + w.closing.setbool(e) } //export windowClosing diff --git a/redo/window_windows.go b/redo/window_windows.go index 7b1ff92..e2a0973 100644 --- a/redo/window_windows.go +++ b/redo/window_windows.go @@ -35,109 +35,55 @@ func makeWindowWindowClass() error { return nil } -func newWindow(title string, width int, height int) *Request { - c := make(chan interface{}) - return &Request{ - op: func() { - w := &window{ - // hwnd set in WM_CREATE handler - closing: newEvent(), - } - hwnd := C.newWindow(toUTF16(title), C.int(width), C.int(height), unsafe.Pointer(w)) - if hwnd != w.hwnd { - panic(fmt.Errorf("inconsistency: hwnd returned by CreateWindowEx() (%p) and hwnd stored in window (%p) differ", hwnd, w.hwnd)) - } - c <- w - }, - resp: c, +func newWindow(title string, width int, height int) *window { + w := &window{ + // hwnd set in WM_CREATE handler + closing: newEvent(), + } + hwnd := C.newWindow(toUTF16(title), C.int(width), C.int(height), unsafe.Pointer(w)) + if hwnd != w.hwnd { + panic(fmt.Errorf("inconsistency: hwnd returned by CreateWindowEx() (%p) and hwnd stored in window (%p) differ", hwnd, w.hwnd)) + } + return w +} + +func (w *window) SetControl(control Control) { + if w.child != nil { // unparent existing control + w.child.unparent() + } + control.unparent() + control.parent(w) + w.child = control +} + +func (w *window) Title() string { + return getWindowText(w.hwnd) +} + +func (w *window) SetTitle(title string) { + C.setWindowText(w.hwnd, toUTF16(title)) +} + +func (w *window) Show() { + if !w.shownbefore { + C.ShowWindow(w.hwnd, C.nCmdShow) + C.updateWindow(w.hwnd) + w.shownbefore = true + } else { + C.ShowWindow(w.hwnd, C.SW_SHOW) } } -func (w *window) SetControl(control Control) *Request { - c := make(chan interface{}) - return &Request{ - op: func() { - if w.child != nil { // unparent existing control - w.child.unparent() - } - control.unparent() - control.parent(w) - w.child = control - c <- struct{}{} - }, - resp: c, - } +func (w *window) Hide() { + C.ShowWindow(w.hwnd, C.SW_HIDE) } -func (w *window) Title() *Request { - c := make(chan interface{}) - return &Request{ - op: func() { - c <- getWindowText(w.hwnd) - }, - resp: c, - } +func (w *window) Close() { + C.windowClose(w.hwnd) } -func (w *window) SetTitle(title string) *Request { - c := make(chan interface{}) - return &Request{ - op: func() { - C.setWindowText(w.hwnd, toUTF16(title)) - c <- struct{}{} - }, - resp: c, - } -} - -func (w *window) Show() *Request { - c := make(chan interface{}) - return &Request{ - op: func() { - if !w.shownbefore { - C.ShowWindow(w.hwnd, C.nCmdShow) - C.updateWindow(w.hwnd) - w.shownbefore = true - } else { - C.ShowWindow(w.hwnd, C.SW_SHOW) - } - c <- struct{}{} - }, - resp: c, - } -} - -func (w *window) Hide() *Request { - c := make(chan interface{}) - return &Request{ - op: func() { - C.ShowWindow(w.hwnd, C.SW_HIDE) - c <- struct{}{} - }, - resp: c, - } -} - -func (w *window) Close() *Request { - c := make(chan interface{}) - return &Request{ - op: func() { - C.windowClose(w.hwnd) - c <- struct{}{} - }, - resp: c, - } -} - -func (w *window) OnClosing(e func(Doer) bool) *Request { - c := make(chan interface{}) - return &Request{ - op: func() { - w.closing.setbool(e) - c <- struct{}{} - }, - resp: c, - } +func (w *window) OnClosing(e func() bool) { + w.closing.setbool(e) } //export storeWindowHWND