Onscreen Keyboard integration with GTK+3 application

Hi there,
I am developing a GTK application with GTK+3.0 in eclipse for embedded processor Toradex Colibri iMX6DL running on embedded Linux and I am quite successful with my application. I am going to use this application in a touchscreen where keyboard will not be present. So can anyone suggest how to get onscreen keyboard when a entry is focused in gtk application.
Also is there any keyboard widget available in GTK by default or any-other library for keyboard.(The main thing is I want the keyboard to open inside my window not as a separate application

Thanks for your time.
Regards,
Nishanth

GTK provides extension points for input method modules, like IBus, to be able to insert text into the various text entry widgets.

GNOME Shell, for instance, uses IBus for its own on screen keyboard. There are other, stand alone projects in various state of maintenance, like Caribou.

Hi @ebassi,
Thanks for your suggestion and I am Working out on it. But we are trying to get the keyboard as a part of same application and not to open keyboard as a separate application like how android does.
We are planning of developing keyboard as a popup with set of buttons as in normal keyboards and when the key is pressed it returns its alphabet in the entry. Will it be reliable to do like this or do we have any-other method of implementing on-screen keyboard.

Hi Nishanth,

You can set up a group of buttons to work as a keyboard. Use a hashtable to relate or look up values in a table and then use those values for typing in the entry. That way you can factor in the shift key and other keys to get them to work. A short demo of the idea.

Eric

/*
   gcc -Wall keyboard1.c -o keyboard1 `pkg-config --cflags --libs gtk+-3.0`
   Tested with GTK3.22 and GTK3.22 on Ubuntu18.04   
*/
#include<gtk/gtk.h>

struct key{
    gint id;
    GtkWidget *button;
  };

static const gchar letters[18]="QWERTYASDFGHZXCVBN";
//Need single chars as strings.
static gchar single_char[2]={'A', '\0'};

static void button_clicked(GtkWidget *button, gpointer *user_data)
  {
    gpointer *button_index=g_hash_table_lookup((GHashTable*)user_data[0], button);
    g_print("Button index %i\n", (gint)(*button_index));
    gint index=(gint)(*button_index);
    single_char[0]=letters[index];
    gchar *string=g_strdup_printf("%s%s", gtk_entry_get_text(GTK_ENTRY(user_data[1])), single_char);
    gtk_entry_set_text(GTK_ENTRY(user_data[1]), string);
    g_free(string);
  }
int main(int argc, char *argv[])
  {
    gtk_init (&argc, &argv);
    gint i=0;
    gint j=0;
    
    GtkWidget *window=gtk_window_new(GTK_WINDOW_TOPLEVEL);
    gtk_window_set_title(GTK_WINDOW(window), "Keyboard");
    gtk_window_set_default_size(GTK_WINDOW(window), 400, 200);
    gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
    g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);

    GtkWidget *entry=gtk_entry_new();
    gtk_widget_set_hexpand(entry, TRUE);

    //Save buttons in an array.
    struct key k1;
    GArray *keyboard=g_array_new(FALSE, FALSE, sizeof(struct key));    
    for(i=0;i<18;i++)
      {
        single_char[0]=letters[i];
        k1.id=i;
        k1.button=gtk_button_new_with_label(single_char);
        g_array_append_val(keyboard, k1);
      }   
 
    //A hash table to look up array index values.
    struct key *p1=NULL;
    GHashTable *hash_table=g_hash_table_new(NULL, NULL);
    for(i=0;i<18;i++)
      {
        p1=&g_array_index(keyboard, struct key, i);
        g_hash_table_insert(hash_table, p1->button, &(p1->id));
      }

    gpointer user_data[2]={hash_table, entry};
    GtkWidget *grid1=gtk_grid_new();
    for(i=0;i<3;i++)
      {
        for(j=0;j<6;j++)
          {
            p1=&g_array_index(keyboard, struct key, i*6+j);
            gtk_grid_attach(GTK_GRID(grid1), p1->button, j, i, 1, 1);
            g_signal_connect(p1->button, "clicked", G_CALLBACK(button_clicked), user_data);
          }
      } 

    GtkWidget *scroll=gtk_scrolled_window_new(NULL, NULL);
    gtk_widget_set_vexpand(scroll, TRUE);
    gtk_widget_set_hexpand(scroll, TRUE);
    gtk_container_add(GTK_CONTAINER(scroll), grid1);

    GtkWidget *expander=gtk_expander_new("Keyboard");
    gtk_widget_set_vexpand(expander, TRUE);
    gtk_widget_set_hexpand(expander, TRUE);
    gtk_container_add(GTK_CONTAINER(expander), scroll);

    GtkWidget *grid2=gtk_grid_new();
    gtk_grid_attach(GTK_GRID(grid2), expander, 0, 0, 1, 1);
    gtk_grid_attach(GTK_GRID(grid2), entry, 0, 1, 1, 1);

    gtk_container_add(GTK_CONTAINER(window), grid2);

    gtk_widget_show_all(window);

    gtk_main();

    g_hash_table_destroy(hash_table);
    g_array_free(keyboard, TRUE);

    return 0;
  }  
1 Like

Hello @cecashon
Thank you very much for your support and the example given helps me a lot and i am going to starting coding for a complete keyboard in similar way.

Hi Nishanth,

I don’t have language expertise and think international keyboard layouts are a tough problem. Being an English speaker, an English only keyboard can be done but I don’t know how keys and keyboards are mapped in different languages or if there is a standard way of going about it. Maybe someone else knows more than I do about that.

Eric

Thank you @cecashon,
We will be using the english keyboard only so there will not be issue and once again thank you for the example
Regards
Nishanth

Hi @cecashon, @ebassi,
Thank you guys for your support and now i came up with a idea of how to implement a keyboard inside a GTK application but i am facing an issue with the size of the buttons. I created the keyboard layout in GLADE and in preview it looks fine but when i run the code in a embedded processor running embedded Linux, the size of the buttons looks very large and i really cant reduce the size. So is there any possiblities of reducing the button size lesser than its minimum size?

To style the buttons you can use CSS. The button size will dynamically change with the label font size. In the keyboard1.c code try.

...
    gtk_container_add(GTK_CONTAINER(window), grid2);

    gchar *css_string=g_strdup("label{font-size: 30px; font-weight: bold;}");
    GError *css_error=NULL;
    GtkCssProvider *provider=gtk_css_provider_new();
    gtk_css_provider_load_from_data(provider, css_string, -1, &css_error);
    gtk_style_context_add_provider_for_screen(gdk_screen_get_default(), GTK_STYLE_PROVIDER(provider), GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
    if(css_error!=NULL)
      {
        g_print("CSS loader error %s\n", css_error->message);
        g_error_free(css_error);
      }
    g_object_unref(provider);
    g_free(css_string);

    gtk_widget_show_all(window);
...

and test some font sizes out. This is tested on GTK3.22. If you are using a version of GTK before 3.20 “label” in the CSS string will be “GtkLabel”.

Eric

Thank you @cecashon. It helped me a lot and i have some issues with adding the background color for the buttons. I am elaborating my doubts,
1.I tried with the above code and i am quite successful in changing the size of the button and in the similar way can you please share some examples of how to change the size of combobox and entries.
2.I tried this code
.myButton
{
font-size: 30px;
font-weight: bold;
color:red;
background-color:red;
min-width:5px;
min-height:5px
}
Everything worked well expect the background color. So i changed background-color with background, the color of the button changed but i was not in a correct way but if i click a buttSO how to add background color to a button using CSS.

Thank you for your time

If possible, check your application with the GTK inspector. So you can actually see the widgets hierarchy and their CSS properties.

$ GTK_DEBUG=interactive ./yourapp .

It is possible that you are setting the background color properly, but, there could be another widget inside your button that is overlapping, or even just another CSS property taking precedence e.g.

.myButton {
   background-color: red;
   background-image: none;	
}

I developed some applications with an on-screen keyboard, and I quickly abandoned the “grid of buttons” approach. I used an external process (specifically onboard, but any keyboard supporting the XEmbed protocol would work) and embedded the keyboard in my dialog with a GtkSocket.

/* The keyboard process must be started *before* your application.
   ui.keyboard_plug is the XID to embed returned by that process. */
if (ui.keyboard_plug > 0) {
    widget = gtk_socket_new();
    gtk_widget_set_size_request(widget, -1, 280);
    gtk_widget_show(widget);
    gtk_container_add(GTK_CONTAINER(gtk_dialog_get_content_area(ui.input_dialog)), widget);
    gtk_socket_add_id(GTK_SOCKET(widget), (Window) ui.keyboard_plug);
}

After that I just traversed all my UI looking for GtkEntry instances and attached an handler to their "button-press-event" signal.

This approach is simple and effective but it has some drawbacks:

  1. AFAIK it works only on X11;
  2. any click on the entries will open up the keyboard;
  3. because of 2, I had to use a hack to be able to click the up and down arrows of GtkSpinButton without opening the keyboard dialog.