Fixed modality issues on the GTK+ MsgBox() changes. The system introduced here has the advantage of scaling out to all other types of dialogs.
This commit is contained in:
parent
54966a3823
commit
10f55564d0
|
@ -67,6 +67,12 @@ func g_signal_connect(obj *C.GtkWidget, sig string, callback C.GCallback, sysDat
|
|||
C.gSignalConnect(obj, csig, callback, unsafe.Pointer(sysData))
|
||||
}
|
||||
|
||||
func g_signal_connect_pointer(obj *C.GtkWidget, sig string, callback C.GCallback, p unsafe.Pointer) {
|
||||
csig := C.CString(sig)
|
||||
defer C.free(unsafe.Pointer(csig))
|
||||
C.gSignalConnect(obj, csig, callback, p)
|
||||
}
|
||||
|
||||
// there are two issues we solve here:
|
||||
// 1) we need to make sure the uitask request gets garbage collected when we're done so as to not waste memory, but only when we're done so as to not have craziness happen
|
||||
// 2) we need to make sure one idle function runs and finishes running before we start the next; otherwise we could wind up with weird things like the ret channel being closed early
|
||||
|
|
117
dialog_unix.go
117
dialog_unix.go
|
@ -9,9 +9,10 @@ import (
|
|||
)
|
||||
|
||||
// #include "gtk_unix.h"
|
||||
// extern void our_dialog_response_callback(GtkDialog *, gint, gpointer);
|
||||
// /* because cgo seems to choke on ... */
|
||||
// /* parent will be NULL if there is no parent, so this is fine */
|
||||
// GtkWidget *gtkNewMsgBox(GtkWindow *parent, GtkMessageType type, GtkButtonsType buttons, char *title, char *text)
|
||||
// static inline GtkWidget *gtkNewMsgBox(GtkWindow *parent, GtkMessageType type, GtkButtonsType buttons, char *title, char *text)
|
||||
// {
|
||||
// GtkWidget *k;
|
||||
//
|
||||
|
@ -22,33 +23,93 @@ import (
|
|||
// }
|
||||
import "C"
|
||||
|
||||
func _msgBox(parent *Window, primarytext string, secondarytext string, msgtype C.GtkMessageType, buttons C.GtkButtonsType) (result chan int) {
|
||||
result = make(chan int)
|
||||
go func() {
|
||||
ret := make(chan C.gint)
|
||||
defer close(ret)
|
||||
uitask <- func() {
|
||||
var pwin *C.GtkWindow = nil
|
||||
// dialogg performs the bookkeeping involved for having a GtkDialog behave the way we want.
|
||||
type dialog struct {
|
||||
parent *Window
|
||||
pwin *C.GtkWindow
|
||||
hadgroup C.gboolean
|
||||
prevgroup *C.GtkWindowGroup
|
||||
newgroup *C.GtkWindowGroup
|
||||
result chan int
|
||||
}
|
||||
|
||||
func mkdialog(parent *Window) *dialog {
|
||||
return &dialog{
|
||||
parent: parent,
|
||||
result: make(chan int),
|
||||
}
|
||||
}
|
||||
|
||||
func (d *dialog) prepare() {
|
||||
// to implement parent, we need to put the GtkMessageDialog into a new window group along with parent
|
||||
// a GtkWindow can only be part of one group
|
||||
// so we use this to save the parent window group (if there is one) and store the new window group
|
||||
// after showing the message box, we restore the previous window group, so future parent == dialogWindow can work properly
|
||||
// thanks to pbor and mclasen in irc.gimp.net/#gtk+
|
||||
var prevgroup *C.GtkWindowGroup = nil
|
||||
var newgroup *C.GtkWindowGroup
|
||||
|
||||
if parent != dialogWindow {
|
||||
pwin = togtkwindow(parent.sysData.widget)
|
||||
if d.parent != dialogWindow {
|
||||
d.pwin = togtkwindow(d.parent.sysData.widget)
|
||||
d.hadgroup = C.gtk_window_has_group(d.pwin)
|
||||
// we can't remove a window from the "default window group"; otherwise this throws up Gtk-CRITICAL warnings
|
||||
if C.gtk_window_has_group(pwin) != C.FALSE {
|
||||
prevgroup = C.gtk_window_get_group(pwin)
|
||||
C.gtk_window_group_remove_window(prevgroup, pwin)
|
||||
if d.hadgroup != C.FALSE {
|
||||
d.prevgroup = C.gtk_window_get_group(d.pwin)
|
||||
C.gtk_window_group_remove_window(d.prevgroup, d.pwin)
|
||||
}
|
||||
newgroup = C.gtk_window_group_new()
|
||||
C.gtk_window_group_add_window(newgroup, pwin)
|
||||
d.newgroup = C.gtk_window_group_new()
|
||||
C.gtk_window_group_add_window(d.newgroup, d.pwin)
|
||||
}
|
||||
}
|
||||
|
||||
func (d *dialog) run(mk func() *C.GtkWidget) {
|
||||
d.prepare()
|
||||
box := mk()
|
||||
if d.parent == dialogWindow {
|
||||
go func() {
|
||||
res := make(chan C.gint)
|
||||
defer close(res)
|
||||
uitask <- func() {
|
||||
r := C.gtk_dialog_run((*C.GtkDialog)(unsafe.Pointer(box)))
|
||||
d.cleanup(box)
|
||||
res <- r
|
||||
}
|
||||
d.send(<-res)
|
||||
}()
|
||||
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
|
||||
d.result <- int(res)
|
||||
}
|
||||
|
||||
func _msgBox(parent *Window, primarytext string, secondarytext string, msgtype C.GtkMessageType, buttons C.GtkButtonsType) (result chan int) {
|
||||
result = make(chan int)
|
||||
d := mkdialog(parent)
|
||||
uitask <- func() {
|
||||
cprimarytext := C.CString(primarytext)
|
||||
defer C.free(unsafe.Pointer(cprimarytext))
|
||||
csecondarytext := (*C.char)(nil)
|
||||
|
@ -56,23 +117,11 @@ func _msgBox(parent *Window, primarytext string, secondarytext string, msgtype C
|
|||
csecondarytext = C.CString(secondarytext)
|
||||
defer C.free(unsafe.Pointer(csecondarytext))
|
||||
}
|
||||
box := C.gtkNewMsgBox(pwin, msgtype, buttons, cprimarytext, csecondarytext)
|
||||
response := C.gtk_dialog_run((*C.GtkDialog)(unsafe.Pointer(box)))
|
||||
C.gtk_widget_destroy(box)
|
||||
|
||||
if parent != dialogWindow {
|
||||
C.gtk_window_group_remove_window(newgroup, pwin)
|
||||
C.g_object_unref(C.gpointer(unsafe.Pointer(newgroup))) // free the group
|
||||
if prevgroup != nil {
|
||||
C.gtk_window_group_add_window(prevgroup, pwin)
|
||||
} // otherwise it'll go back into the default group on its own
|
||||
d.run(func() *C.GtkWidget {
|
||||
return C.gtkNewMsgBox(d.pwin, msgtype, buttons, cprimarytext, csecondarytext)
|
||||
})
|
||||
}
|
||||
|
||||
ret <- response
|
||||
}
|
||||
result <- int(<-ret)
|
||||
}()
|
||||
return result
|
||||
return d.result
|
||||
}
|
||||
|
||||
func (w *Window) msgBox(primarytext string, secondarytext string) (done chan struct{}) {
|
||||
|
|
2
todo.md
2
todo.md
|
@ -20,6 +20,8 @@ UNIX:
|
|||
- figure out why Page Up/Page Down does tab stops
|
||||
|
||||
ALL PLATFORMS:
|
||||
- explain that if a local and global dialog are both opened at once, whetehr or not the local dialog is modal is system-defined (but the window it is local to will still be properly disabled once dismissed)
|
||||
- will require moving dialog behavior to the package overview
|
||||
- make sure MouseEvent's documentation has dragging described correctly (both Windows and GTK+ do)
|
||||
- make all widths and heights parameters in constructors in the same place (or drop the ones in Window entirely?)
|
||||
- Message boxes that belong to agiven parent are still application-modal on all platforms except Mac OS X because the whole system waits... we'll need to use a channel for this, I guess :S
|
||||
|
|
Loading…
Reference in New Issue