Re: modality, hinting to the WM, and GtkWindowGroup



Hi,

I haven't followed the full discussion on this thread nor have I viewed at any of the bugs mentioned.  Thus, my comment might nor be fully qualified.  But, I also would like to have a dialog being modal to some but not all windows.  The use case is where like in Gimp there are several top level windows, each holding an image (or more general, a project).  Then, a dialog such as 'Do you really want to close this project without saving? Yes/No' should only block (be modal to) one of the top level windows.

Actually I found a way to this myself by implementing the concept of 'sleep' for a GtkWindow.  If a window is set to sleep it means that it shows a busy cursor when the mouse is over the window and that it does not react on any user input. This is a bit different from having a modal dialog grabbing all user input, but it comes very close.  The application programmer of course has to keep track of which windows he puts to sleep and sets back to non-sleep but with proper programming it should be straight forward.  So, how about supporting a 'sleep' option for GtkWindow (gtk_window_set_sleep(GtkWindow *, gboolean sleep)).

Here is a code fragment that I am using in my implementation (with slightly different function names and prototypes)

// gtk_window_set_sleep
static void guiSetWindowSleep(GtkWidget *w, bool sleep)
{
    if (GTK_IS_WINDOW(w)) {
        GdkWindow *win = w->window;
        if (win) {
            guint count = GPOINTER_TO_UINT(
                    g_object_get_data(G_OBJECT(win), "sleep"));
            if (sleep) {
                if (count++ == 0) {
                    guiSetBusyCursor(win);
                }
            } else if (count != 0) {
                if (--count == 0) {
                    guiClearBusyCursor(win);
                }
            }
            g_object_set_data(G_OBJECT(win), "sleep", GUINT_TO_POINTER(count));
        }
    }
}

static void guiSetBusyCursor(GdkWindow *win)
{
    GdkWindow *busy = (GdkWindow *)g_object_get_data(G_OBJECT(win), "busy");
    if (!busy) {
        // we place an input-only window over the whole top-level window
        // and set a busy pointer
        GdkWindowAttr attr;
        attr.event_mask = GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
            GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK | GDK_ALL_EVENTS_MASK;
        attr.x = 0;
        attr.y = 0;
        attr.width = G_MAXINT;
        attr.height = G_MAXINT;
        attr.wclass = GDK_INPUT_ONLY;
        attr.window_type = GDK_WINDOW_CHILD;
        attr.cursor = busyCursor;

        busy = gdk_window_new(win, &attr, GDK_WA_X | GDK_WA_Y | GDK_WA_CURSOR);
        gdk_window_set_keep_above(busy, true);

        g_object_set_data_full(G_OBJECT(win), "busy", busy,
                (GDestroyNotify)gdk_window_destroy);
    }
    gdk_window_show(busy);
    // gdk_window_set_accept_focus(win, false);
}

static void guiClearBusyCursor(GdkWindow *win)
{
    GdkWindow *busy = (GdkWindow *)g_object_get_data(G_OBJECT(win), "busy");
    if (busy) {
        gdk_window_hide(busy);
        // gdk_window_set_accept_focus(win, true);
    }
}

The variable busyCursor was set in the main function to something like this:

    busyCursor = gdk_cursor_new(GDK_WATCH);

Unfortunately, there are some events I still have to block manually.  For this I install my own event handler, so somewhere in the main function, I have this line:

    gdk_event_handler_set((GdkEventFunc)GuiDoEvent, 0, 0);

and the event handler is here:

static void GuiDoEvent(GdkEvent *event)
{
    switch (event->type) {
        case GDK_DELETE:
        case GDK_KEY_PRESS:
        case GDK_KEY_RELEASE:
            if (GPOINTER_TO_UINT(g_object_get_data(
                        G_OBJECT(event->any.window), "sleep")) == 0) {
                gtk_main_do_event(event);
            }
            break;
        case GDK_CONFIGURE:
        case GDK_EXPOSE:
            gtk_main_do_event(event);
            if (GPOINTER_TO_UINT(g_object_get_data(
                        G_OBJECT(event->any.window), "sleep")) != 0) {
                GdkWindow *busy = (GdkWindow *)g_object_get_data(
                        G_OBJECT(event->any.window), "busy");
                if (busy) {
                    gdk_window_raise(busy);     // make sure we are on top
                }
            }
            break;
        default:
            gtk_main_do_event(event);
            break;
    }
 }

This implementation of the sleep concept is a bit of a hack but should show the idea.  If something like this (in a clean way) could be made part of gtk then the application programmer could easily implement his own modal dialogs in any way he thinks is useful for his application.

Regards,

    Steffen


Yahoo! Messenger with Voice. PC-to-Phone calls for ridiculously low rates.

[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]