Skip to content

checkbutton: Some fixes when used with GActions

Florian Müllner requested to merge fmuellner/gtk:check-activate into master

This addresses some oddities when using GtkCheckButton as GtkActionable, in particular related to keynav.

Take the following sample program:

Sample program

static void
update_label (GActionGroup *group,
              GtkWidget    *label)
{
  g_autoptr(GVariant) state = NULL;

  state = g_action_group_get_action_state (group, "action");
  gtk_label_set_label (GTK_LABEL (label), g_variant_get_string (state, NULL));
}

static void
on_state_changed (GActionGroup *group,
                  const char   *action_name,
                  GVariant     *value,
                  gpointer      user_data)
{
  GtkWidget *label = user_data;
  update_label (group, label);
}

static void
activate_action (GSimpleAction *action,
                 GVariant      *parameter,
                 gpointer       user_data)
{
  g_action_change_state (G_ACTION (action), parameter);
}

static void
change_action_state (GSimpleAction *action,
                     GVariant      *state,
                     gpointer       user_data)
{
  g_simple_action_set_state (action, state);
}

static void
on_app_startup (GApplication *application,
                gpointer      user_data)
{
  static GActionEntry entries[] = {
    { "action", activate_action, "s", "'foo'", change_action_state }
  };
  g_action_map_add_action_entries (G_ACTION_MAP (application),
                                   entries, G_N_ELEMENTS (entries),
                                   application);
}

static void
on_app_activated (GApplication *application,
                  gpointer       user_data)
{
  static GtkWidget *window = NULL;

  if (!window)
    {
      GtkWidget *box, *label, *button, *group;

      window = gtk_application_window_new (GTK_APPLICATION (application));
      gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);

      box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
      gtk_widget_set_halign (box, GTK_ALIGN_CENTER);
      gtk_widget_set_valign (box, GTK_ALIGN_CENTER);
      gtk_window_set_child (GTK_WINDOW (window), box);

      label = gtk_label_new ("");
      gtk_widget_set_margin_bottom (label, 24);
      gtk_box_append (GTK_BOX (box), label);

      button = gtk_check_button_new_with_label ("Foo");
      gtk_actionable_set_detailed_action_name (GTK_ACTIONABLE (button),
                                               "app.action::foo");
      gtk_box_append (GTK_BOX (box), button);
      group = button;

      button = gtk_check_button_new_with_label ("Bar");
      gtk_check_button_set_group (GTK_CHECK_BUTTON (button),
                                  GTK_CHECK_BUTTON (group));
      gtk_actionable_set_detailed_action_name (GTK_ACTIONABLE (button),
                                               "app.action::bar");
      gtk_box_append (GTK_BOX (box), button);
      group = button;

      button = gtk_check_button_new_with_label ("Baz");
      gtk_check_button_set_group (GTK_CHECK_BUTTON (button),
                                  GTK_CHECK_BUTTON (group));
      gtk_actionable_set_detailed_action_name (GTK_ACTIONABLE (button),
                                               "app.action::baz");
      gtk_box_append (GTK_BOX (box), button);

      g_signal_connect (application,
                        "action-state-changed::action", G_CALLBACK (on_state_changed),
                        label);
      update_label (G_ACTION_GROUP (application), label);
    }
  gtk_window_present (GTK_WINDOW (window));
}

int
main (int argc, char *argv[]) {
  g_autoptr (GtkApplication) app = NULL;

  app = gtk_application_new ("org.gnome.fmuellner.Example", G_APPLICATION_FLAGS_NONE);
  g_signal_connect (app, "startup", G_CALLBACK (on_app_startup), NULL);
  g_signal_connect (app, "activate", G_CALLBACK (on_app_activated), NULL);

  return g_application_run (G_APPLICATION (app), argc, argv);
}
  1. clicking a button will update the action's state (as reflected in the label above)
  2. using arrow keys to move between buttons will move the check, but not update the action
  3. activating the focused button with enter/space will toggle its check, but not update the action either

Step 1 is working as expected, this MR addresses 2 and 3.

Edited by Florian Müllner

Merge request reports