Difference between activate and change_state from the GActionEntry

I have an Demo code with an Item using the a gboolean state used for a check menu.
The code works fine, but in this context ( the way the code is ):

#include <gtk/gtk.h>

static void check_clbk ( GSimpleAction *action, G_GNUC_UNUSED GVariant *parameter, G_GNUC_UNUSED gpointer user_data )
{
    GVariant *action_state;
    action_state = g_action_get_state ( G_ACTION ( action ) );
    const gboolean flag = g_variant_get_boolean ( action_state );

    if ( flag )
    {
        g_print ( "OFF\n" );
        g_simple_action_set_state ( action, g_variant_new_boolean ( FALSE ) );
    }
    else
    {
        g_print ( "ON\n" );
        g_simple_action_set_state ( action, g_variant_new_boolean ( TRUE ) );
    }
}

static void create_actions ( GtkApplication *const application )
{
    static const GActionEntry actions[] =
    {
        { "checkMenu", check_clbk, NULL, "true", NULL, { 0, 0, 0 } },
    };

    g_action_map_add_action_entries ( G_ACTION_MAP ( application ), actions, G_N_ELEMENTS ( actions ), application );
}

static void create_menu ( GMenu *const main_menu, const gchar *const menu_name )
{
    GMenu     *edit_menu;
    GMenuItem *item;

    edit_menu = g_menu_new();
    item      = g_menu_item_new ( "Check", "app.checkMenu" );

    g_menu_item_set_action_and_target ( item, "app.checkMenu", NULL, "true", NULL  );
    g_menu_append_item ( edit_menu, item );
    g_object_unref ( item );

    g_menu_append_submenu ( main_menu, menu_name, G_MENU_MODEL ( edit_menu ) );
    g_object_unref ( edit_menu );
}

static void startup ( GtkApplication *const application )
{
    GMenu *menu;
    menu  = g_menu_new();

    /// *** Create the Menu
    create_menu ( menu, "Edit" );

    /// ***
    gtk_application_set_menubar ( application, G_MENU_MODEL ( menu ) );

    /// ***
    create_actions ( application );
    g_object_unref ( menu );
}

static void activate ( GtkApplication *const application )
{
    GtkWidget *window;
    window = gtk_application_window_new ( application );

    gtk_window_set_application ( GTK_WINDOW ( window ), application );
    gtk_window_set_title ( GTK_WINDOW ( window ), "GMenu" );

    gtk_widget_show_all ( window );
}

gint main ( gint argc, gchar **argv )
{
    GtkApplication *application;
    gint status;

    application = gtk_application_new ( "GMenu", G_APPLICATION_FLAGS_NONE );

    g_signal_connect_swapped ( application, "startup",  G_CALLBACK ( startup ),  application );
    g_signal_connect_swapped ( application, "activate", G_CALLBACK ( activate ), application );

    status = g_application_run ( G_APPLICATION ( application ), argc, argv );
    g_object_unref ( application );

    return status;
}

It works fine if the action was created with change_state set to NULL and activate pointed to check_clbk:

{ "checkMenu", check_clbk, NULL, "true", NULL, { 0, 0, 0 } },

and also works fine, if the action was created with activate set to NULL and change_state pointed to check_clbk:

{ "checkMenu", NULL, NULL, "true", check_clbk, { 0, 0, 0 } }

How should I treat/understand this situation ?

Well, after I read the whole documentation I came to the following:
The activate is just a way of interpreting the item as activated/clicked, where the change-state is where the actual changes takes/should take place.

If one calls g_signal_connect on both:

g_signal_connect_swapped ( action,  "activate",     G_CALLBACK ( activate_radio ),     action );
g_signal_connect_swapped ( action,  "change-state", G_CALLBACK ( change_radio_state ), action );

then only activate radio gets called.

If one calls g_action_change_state inside activate_radio callback does a request for the state of action to be changed to value and the change-state signal takes place.

At this point the change_radio_state callback gets called.

In the following demo code:

#include <gtk/gtk.h>

static void activate_radio ( GSimpleAction *action,
                             G_GNUC_UNUSED GVariant *parameter,
                             G_GNUC_UNUSED gpointer user_data )
{
    g_return_if_fail ( G_IS_SIMPLE_ACTION ( action ) );
    ///g_action_change_state ( G_ACTION ( action ), parameter );
    g_print( "activate_radio was called\n" );
}

static void change_radio_state ( GSimpleAction *action,
                                 GVariant *state,
                                 G_GNUC_UNUSED gpointer user_data )
{
    g_return_if_fail ( G_IS_SIMPLE_ACTION ( action ) );
    g_simple_action_set_state ( action, state );
    g_print( "change_radio_state was called\n" );
}

static void startup ( GtkApplication * application )
{
    GMenu *main_menu;
    GMenu *file_menu;

    GSimpleAction *color_action;

    GSimpleAction *red_action;
    GSimpleAction *green_action;
    GSimpleAction *blue_action;

    /// ***
    GVariant *state_color;
    state_color     = g_variant_new_string ( "blue" );
    color_action    = g_simple_action_new_stateful ( "colors", G_VARIANT_TYPE_STRING, state_color );

    /// ***
    red_action      = g_simple_action_new ( "red",   NULL );
    green_action    = g_simple_action_new ( "green", NULL );
    blue_action     = g_simple_action_new ( "blue",  NULL );

    /// ***
    g_signal_connect_swapped ( color_action,  "activate",     G_CALLBACK ( activate_radio ),     color_action );
    g_signal_connect_swapped ( color_action,  "change-state", G_CALLBACK ( change_radio_state ), color_action );

    /// ***
    g_action_map_add_action ( G_ACTION_MAP ( application ), G_ACTION ( color_action ) );

    g_action_map_add_action ( G_ACTION_MAP ( application ), G_ACTION ( red_action ) );
    g_action_map_add_action ( G_ACTION_MAP ( application ), G_ACTION ( green_action ) );
    g_action_map_add_action ( G_ACTION_MAP ( application ), G_ACTION ( blue_action ) );
    /// ***
    main_menu = g_menu_new ();
    file_menu = g_menu_new();

    /// ***
    GMenuItem *item;
    item = g_menu_item_new ( "Red", "app.colors" );
    g_menu_item_set_attribute ( item, "target", "s", "red", NULL  );
    g_menu_append_item        ( file_menu, item );
    g_object_unref ( item );

    /// ***
    item = g_menu_item_new ( "Green", "app.colors" );
    g_menu_item_set_attribute ( item, "target", "s", "green", NULL  );
    g_menu_append_item        ( file_menu, item );
    g_object_unref ( item );

    /// ***
    item = g_menu_item_new ( "Blue", "app.colors" );
    g_menu_item_set_attribute ( item, "target", "s", "blue", NULL  );
    g_menu_append_item ( file_menu, item );
    g_object_unref ( item );

    /// ***
    g_menu_insert_submenu ( main_menu, 0, "File",  G_MENU_MODEL ( file_menu ) );

    /// ***
    gtk_application_set_menubar ( application, G_MENU_MODEL ( main_menu ) );

    g_object_unref ( red_action );
    g_object_unref ( green_action );
    g_object_unref ( blue_action );

    g_object_unref ( file_menu );
    g_object_unref ( main_menu );
}

static void activate ( GtkApplication * app )
{
    GtkWidget *window;
    window = gtk_application_window_new ( app );

    gtk_window_set_application ( GTK_WINDOW ( window ), GTK_APPLICATION ( app ) );
    gtk_window_set_title ( GTK_WINDOW ( window ), "Actions" );

    gtk_widget_show_all ( GTK_WIDGET ( window ) );
}

int main ( int argc, char **argv )
{
    GtkApplication *app;
    int status;

    app = gtk_application_new  ( "org.gtk.example", G_APPLICATION_FLAGS_NONE );
    g_signal_connect_swapped   ( app, "startup",    G_CALLBACK ( startup ),  app );
    g_signal_connect_swapped   ( app, "activate",   G_CALLBACK ( activate ), app );
    status = g_application_run ( G_APPLICATION ( app ), argc, argv );
    g_object_unref ( app );
    return status;
}

The message:

change_radio_state was called

will never get printed, because the change_radio_state never gets called.

To make it possible one needs to call g_action_change_state inside activate_radio.

This make more sens to me.

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.