Search code examples
cgtkgtk3

Emit custom gtk signal with variable as user data


I want to send a custom signal with user data. The user data (coming from a GtkEntry) is not filled upon connecting the signal, but is when the signal is emitted.
How can I emit a signal with the content of a GtkEntry as user data?

Right now I have a signal handler for the "standard" signal button_press_event. This handler reads the GtkEntry's and emits the custom signal. This is where I want de GtkEntry data to be sent as user data.

I do not want to send a pointer to GtkEntry, since the GtkEntry belongs to login_credentials_screen.c

I have a UI file:

<!-- Generated with glade 3.38.2 -->
<interface>
  <requires lib="gtk+" version="3.24"/>
  <object class="GtkWindow">
    <property name="can-focus">False</property>
    <child>
      <object class="GtkBox">
        <property name="visible">True</property>
        <property name="can-focus">False</property>
        <property name="halign">center</property>
        <property name="valign">center</property>
        <property name="orientation">vertical</property>
        <child>
          <object class="GtkLabel">
            <property name="height-request">80</property>
            <property name="visible">True</property>
            <property name="can-focus">False</property>
            <property name="halign">center</property>
            <property name="valign">center</property>
            <property name="label" translatable="yes">Enter the correct user code
and company code to log in</property>
            <property name="justify">center</property>
            <style>
              <class name="text_large"/>
            </style>
          </object>
          <packing>
            <property name="expand">False</property>
            <property name="fill">True</property>
            <property name="position">1</property>
          </packing>
        </child>
        <child>
          <object class="GtkBox">
            <property name="height-request">40</property>
            <property name="visible">True</property>
            <property name="can-focus">False</property>
            <property name="halign">center</property>
            <property name="valign">center</property>
            <property name="margin-top">5</property>
            <property name="spacing">20</property>
            <child>
              <object class="GtkEntry" id="user_code_input">
                <property name="visible">True</property>
                <property name="can-focus">True</property>
                <property name="placeholder-text" translatable="yes">User code</property>
              </object>
              <packing>
                <property name="expand">False</property>
                <property name="fill">True</property>
                <property name="position">0</property>
              </packing>
            </child>
            <child>
              <object class="GtkEntry" id="company_code_input">
                <property name="visible">True</property>
                <property name="can-focus">True</property>
                <property name="placeholder-text" translatable="yes">Company code</property>
              </object>
              <packing>
                <property name="expand">False</property>
                <property name="fill">True</property>
                <property name="position">1</property>
              </packing>
            </child>
          </object>
          <packing>
            <property name="expand">False</property>
            <property name="fill">True</property>
            <property name="position">2</property>
          </packing>
        </child>
        <child>
          <object class="GtkEventBox" id="login_button">
            <property name="visible">True</property>
            <property name="can-focus">False</property>
            <child>
              <object class="GtkLabel">
                <property name="width-request">250</property>
                <property name="height-request">60</property>
                <property name="visible">True</property>
                <property name="can-focus">False</property>
                <property name="halign">center</property>
                <property name="margin-top">15</property>
                <property name="label" translatable="yes">Log in</property>
                <style>
                  <class name="login_buttons"/>
                  <class name="text_normal"/>
                </style>
              </object>
            </child>
          </object>
          <packing>
            <property name="expand">False</property>
            <property name="fill">True</property>
            <property name="position">3</property>
          </packing>
        </child>
      </object>
    </child>
  </object>
</interface>

I have main.c:

#include <glib.h>
#include <gtk/gtk.h>
#include "login_credentials_screen.h"

void on_button_clicked(GtkWidget *widget, GdkEvent *event, gpointer data);

void main(void)
{
    login_cred_subscribe_login_button_callback(on_button_clicked);
}

void on_button_clicked(GtkWidget *widget, GdkEvent *event, gpointer data)
{
    printf("Userdata: %s\n", data);
}

And login_credentials_screen.c:

GtkBuilder *login_credentials_builder;
GObject *login_credentials_screen;
GObject *user_code_entry;
GObject *company_code_entry;

void init_login_credentials_screen(void)
{
    login_credentials_builder = gtk_builder_new_from_file("login_credentials_screen.ui");
    login_credentials_screen = gtk_builder_get_object(login_credentials_builder, "login_credentials_screen");
    user_code_entry = gtk_builder_get_object(login_credentials_builder, "user_code_input");
    company_code_entry = gtk_builder_get_object(login_credentials_builder, "company_code_input");

    g_signal_connect(gtk_builder_get_object(login_credentials_builder, "login_button"), "button_press_event", G_CALLBACK(on_login_button_clicked), "login_button");
    g_signal_new("login-button-clicked", G_TYPE_OBJECT, G_SIGNAL_RUN_FIRST, 0, NULL, NULL, g_cclosure_marshal_VOID__POINTER, G_TYPE_NONE, 1, G_TYPE_POINTER);
}

void login_cred_subscribe_login_button_callback(void *function)
{
    g_signal_connect(gtk_builder_get_object(login_credentials_builder, "login_button"), "login-button-clicked", G_CALLBACK(function), NULL); //NULL needs to be something else?
}

void on_login_button_clicked(GtkWidget *widget, GdkEvent *event, gpointer data)
{
    printf("%s\n", gtk_entry_get_text(GTK_ENTRY(user_code_entry)));
    printf("%s\n", gtk_entry_get_text(GTK_ENTRY(company_code_entry)));

    g_signal_emit_by_name(widget, "login-button-clicked", gtk_entry_get_text(GTK_ENTRY(company_code_entry)));
}

Solution

  • I noticed that the g_signal_connect can be called with a variable (char * in this case) as user_data parameter, and the variable does not need to be set untill calling the emit.

    This results in the following:

    char user_code_string[20];
    
    void login_cred_subscribe_login_button_callback(void *function)
    {
        g_signal_connect(gtk_builder_get_object(login_credentials_builder, "login_button"), "login-button-clicked", G_CALLBACK(function), user_code_string);
    }
    
    void on_login_button_clicked(GtkWidget *widget, GdkEvent *event, gpointer data)
    {
        strncpy(user_code_string, gtk_entry_get_text(GTK_ENTRY(user_code_entry)), 20);
        g_signal_emit_by_name(widget, "login-button-clicked");
    }
    

    Upon connecting (g_signal_connect) the login-button-clicked-signal, the variable does not need to be set.

    Before emitting the signal (g_signal_emit_by_name), the variable can be set.