The gtk implementation uses the base benchmark structure. To be able to use the gtk features we need the gtk.h header file.
Since this is a benchmark we will leave "org.gtk.example" as the identifier. To initialize the application we register a callback to build the window and then start the application by passing the received command line arguments.
GtkApplication *app; app = gtk_application_new ("org.gtk.example", G_APPLICATION_FLAGS_NONE); g_signal_connect (app, "activate", G_CALLBACK (activate), NULL); status = g_application_run (G_APPLICATION (app), argc, argv); g_object_unref (app);
Here we have the main structure for building the window according to the specified mockup.
{Insert Button Callback, 4:1} {Search Changed Callback, 4:1} static void activate (GtkApplication* app, gpointer user_data){ {Widgets Declaration, 1} {Layout Organization, 1} {Widgets Initialization, 1} {Widgets Packing, 1} gtk_widget_show_all (window); }
Used in section 2:3
The two main areas of the window are the place to insert new notes and the area to filter and display them. Here we declare the widgets variables. The components in the GTK stack are all based from GTKWidget.
GtkWidget * main_box; GtkWidget * insert_button; GtkWidget * insert_box; GtkWidget * insert_label; GtkWidget * search_label; GtkWidget * search_box; GtkWidget * search_scroll;
Since we need to access the insert box, the search list and the search text from their callback functions we will make them globals.
GtkWidget * window; GtkWidget * insert_text; GtkWidget * search_list; GtkWidget * search_text;
Added to in section 4:1
Used in section 2:3
Here we create the window with the title "Notes Reminder" and set the size to 400x300 as specified before. After that, the main areas of the interface layout are created and their margins and spacings are defined.
To make the list with the notes be scrolable and not expand infinitely we create a scrolled window and put the search box inside it.
// create window window = gtk_application_window_new (app); gtk_window_set_title (GTK_WINDOW (window), TITLE); gtk_window_set_default_size (GTK_WINDOW (window), 400, 300); // create containing boxes main_box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 15); // Insert Box insert_box = gtk_button_box_new(GTK_ORIENTATION_HORIZONTAL); gtk_button_box_set_layout (GTK_BUTTON_BOX (insert_box), GTK_BUTTONBOX_SPREAD); // Search Box search_box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 15); gtk_widget_set_margin_start (search_box, 10); gtk_widget_set_margin_end (search_box, 10); // Scroll Window search_scroll = gtk_scrolled_window_new (NULL, NULL); gtk_scrolled_window_set_min_content_height(GTK_SCROLLED_WINDOW (search_scroll), 120); gtk_container_add(GTK_CONTAINER (window), main_box);
With the layout defined now we instantiate the widgets that will be used. We register the callback for the note insertion when the insert button is clicked and the callback of the note search that will fill the search results in the listbox when new text is written.
The search-changed
event doesn't trigger instantly after each change, so it was chosen to be used in the search field instead of a keypress event because it would make the refreshing of the list look like flickering.
Since the list starts empty, we make an initial search with a blank text string which will match all the notes in the .remember
file.
// Insert Widgets
insert_label = gtk_label_new ("Create New");
insert_button = gtk_button_new_with_label("Insert");
insert_text = gtk_entry_new ();
g_signal_connect(insert_button, "clicked", G_CALLBACK (insert_clicked), NULL);
// Search Widgets
search_text = gtk_search_entry_new ();
search_label = gtk_label_new ("Search");
search_list = gtk_list_box_new ();
g_signal_connect(search_text, "search-changed", G_CALLBACK (search_changed), NULL);
gchar * text = "";
{Search Notes, 2:3}
With the widgets instantiated we pack them into the layout widgets we set up before.
// Fill the containers gtk_container_add(GTK_CONTAINER (main_box), insert_label); gtk_container_add(GTK_CONTAINER (main_box), insert_box); gtk_container_add(GTK_CONTAINER (insert_box), insert_text); gtk_container_add(GTK_CONTAINER (insert_box), insert_button); gtk_container_add(GTK_CONTAINER (main_box), search_box); gtk_container_add(GTK_CONTAINER (search_box), search_label); gtk_container_add(GTK_CONTAINER (search_box), search_text); gtk_container_add(GTK_CONTAINER (search_scroll), search_list); gtk_container_add(GTK_CONTAINER (search_box), search_scroll);
After we have text in the text field to insert new notes and we click the Insert
button we want the text to be added to our notes database. If there is no text in the text field we can ignore the button click. If there is text and the note was inserted we want to clear the text box so we can add other new notes.
static void insert_clicked (GtkWidget *widget, gpointer data){
guint16 text_length = gtk_entry_get_text_length (GTK_ENTRY (insert_text));
if(text_length != 0){
gchar * text;
text = gtk_entry_get_text (GTK_ENTRY (insert_text));
{Insert Note, 2:3}
if(result == 0){
gtk_entry_set_text (GTK_ENTRY (insert_text), "");
}
}
}
Used in section 4:1
When the text in the notes search field changes we want to update the notes list. To do this when a change is detected in the search field we clear the list, query the remember text field with our new query and insert the results in the list.
Since we instantiated new GtkWidgets and added them to the list. We need to tell the window to show all the new widgets.
static void search_changed (GtkWidget *widget, gpointer data){
gchar * text;
text = gtk_entry_get_text (GTK_ENTRY (search_text));
gtk_container_foreach(GTK_CONTAINER (search_list), gtk_widget_destroy, NULL);
{Search Notes, 2:3}
gtk_widget_show_all (window);
}
Used in section 4:1
For each of the results of the note search in the text file we will build a GtkLabel
element, make its appear with '...' at the end if needed to fit in the current window and add it to the notes list.
GtkWidget * label = gtk_label_new(current_note); gtk_label_set_ellipsize(GTK_LABEL (label), PANGO_ELLIPSIZE_END); gtk_container_add (GTK_CONTAINER (search_list), label);
Used in section 2:3
In order to compile this code we can use the pkg-config to import the gtk3 dependencies.
all: gcc `pkg-config --cflags gtk+-3.0` -o main main.c `pkg-config --libs gtk+-3.0` clean: rm main
RAM Usage - 26.54MB
Implementation Dificulty - All the widgets are based on the GtkWidget which allows fastly understand how they can be used. Their documentation is good and this implemented was done just with the help of their reference manual. The code implementation gave us full control of the implementaiton, but we could use the glade tool which allows us to drag and drop most of the gtk components to build the UI. The layout boxes took a bit to be able to manage the position of the elements,
Supported Platforms - GNU/Linux, Unix, Windows and Mac OS X