GTK box borders

Do you use GTK? Have you tested borders around boxes? Here is a ready made test of GTK 3.22 box borders as shown in the screenshot on the left.

GTK is GIMP Tool Kit, a set of software to create a GUI, a Graphical User Interface. GTK is at version 3 and there is a start on version 4. GTK version 3 is stable at 3.22, when I am writing this, and there is an unstable 3.24 in development.

To test GTK CSS options for borders, we create a GTK box, add a CSS class name, then test CSS options for borders. There is a link to the source code I used for the test.

The default border width can be too narrow. You change the border width using the gtk_container_set_border_width function as shown in the following code.

gtk_container_set_border_width (GTK_CONTAINER (box), 10);

The border width function sets the margin on a box. You can do that and more with CSS instead of using this limited function.

In this code, we create multiple boxes, give each box a unique class, then set CSS for the class.

Note that the application level window and box have no CSS for border, margin, or padding.

CSS

GTK can look in lots of places for a CSS file but then you are stuck with the problem of distributing multiple files instead of one neat program. GTK lets you place your CSS into source code to compile as part of your program. Compiling the CSS into your program protects against lost of the CSS file and stops people messing around with the CSS, the perfect protection for a test like this.

If you want people to experiment with the CSS via an external file, you have to add code to do something when the file is missing or GTK does not like the file format. As an example, adding border-radius to the CSS kills all the CSS, not just the CSS for an element.

The CSS items that work are background-color, border-color, border-style, border-width, color, font-style, margin, and padding. One that does not work is border-radius. I did not test anything else in this code.

Windows and Boxes

GTK starts when you create a GTK application object. You create an application window to display stuff. GTK windows can store only one element so you immediately add a standard GtkBox to the window. The standard GtkBox can have any number of children. The name this outer box is box.

We add our test boxes one at a time. The outer box is set to vertical expansion. When we add a box to the outer box, the new box is added below the previous box. To space out the boxes, the outer box is created with a padding of integer 10. The value is pixels.

To add text, you place the text in a GtkLabel then add the label to the box. Our test boxes each contain a label to describe the CSS settings for the test box.

The test code

The code for my test is available as a download at https://petermoulding.com/gtk_box_borders.c. The code is also displayed below. The download may have variations from the displayed code.

The programming language is C. GTK is used with other languages. The format of the GTK code varies for each language.

Code format

The code is formatted for readability, not to fit any current fashion.

Compilation

I compiled the code using Linux Mint 19.1 which has GTK 3.22. I created a directory c for my work on C code. The test program is in directory c/gtk_box_borders. I change to the source code directory then compile with the following command.

gcc `pkg-config --cflags gtk+-3.0` -o gtk_box_borders gtk_box_borders.c `pkg-config --libs gtk+-3.0`

I can then run the compiled program using the following command.

./gtk_box_borders

You may need some GTK development libraries. Other distributions of Linux may have different versions of GTK or no GTK. You can look online to find out how to load GTK on your operating system and how to compile the code for your operating system.

The code

The following code is the code at the time of writing this article. There may be variations in the downloadable source code.

/*
 * GTK 3.22 test of Gtk box borders using CSS.
 * Copyright (C) 2018 Peter Moulding
 * Read more at https://petermoulding.com/gtk_box_borders
 */

/*
 * File:   gtk_box_borders.c
 * cd c/gtk_box_borders
 * Compile with:
 * gcc `pkg-config --cflags gtk+-3.0` -o gtk_box_borders gtk_box_borders.c `pkg-config --libs gtk+-3.0`
 * Then run:
 * ./gtk_box_borders
 */
#include <stdio.h>
/* C does not have boolean values by default. Include them. */
#include <stdbool.h>
/* Include GTK+. */
#include <gdk/gdk.h>
#include <gtk/gtk.h>

bool application_activate (GtkApplication* app, gpointer user_data);
void box_add              (GtkWidget *parent_box, char *class, char *text);
void css_add              (char *css);
void label_add            (GtkWidget *parent_box, char *text);

char      *application_box_class    = "application_box";
int        application_box_padding  = 8;
char      *application_id           = "com.petermoulding.gtk_box_borders";
char      *application_name         = "Gtk box borders";
int        application_height       = 600;
int        application_width        = 600;
char      *border_class             = "border";

char *css =
    ".application_box { background-color: #00ffff; }\n"
    ".border          { border-color: #cccccc; border-style: solid; border-width: 10px; }\n"
    ".border_solid    { border-style: solid; }\n"
    ".border_margin   { margin: 10px; }\n"
    ".border_padded   { padding: 10px; }\n"
    ".border_both     { margin: 10px; padding: 10px; }\n"
    ".border_color    { background-color: navy; border-color: lime; color: yellow; font-style: italic; }\n"
    ".border_none     { border-style: none; }\n"
    ".border_hidden   { border-style: hidden; }\n"
    ".border_dashed   { border-style: dashed; }\n"
    ".border_dotted   { border-style: dotted; }\n"
    ".border_double   { border-style: double; }\n"
    ".border_groove   { border-style: groove; }\n"
    ".border_ridge    { border-style: ridge; }\n"
    ".border_inset    { border-style: inset; }\n"
    ".border_outset   { border-style: outset; }\n"
    ;

int main(int argc, char** argv)
    {
    GtkApplication *app = gtk_application_new (application_id, G_APPLICATION_FLAGS_NONE);
    g_signal_connect (app, "activate", G_CALLBACK (application_activate), NULL);
    int status = g_application_run (G_APPLICATION (app), argc, argv);
    g_object_unref (app);
    return status;
    }
    
/*
 * Create the content.
    printf ("application_activate() height: %d, width: %d\n", application_height, application_width);
    ".border_radius { border-radius: 10px; border-style: solid;  border-width: 5px;  border-color: #ff0000; margin: 10px; padding: 10px;\n"
 */
bool application_activate (GtkApplication* app, gpointer user_data)
    {
    /* Create application window and add a box because a window can have only once child. A box can have many. */
    GtkWidget *application_window = gtk_application_window_new (GTK_APPLICATION (app));
    gtk_window_set_title (GTK_WINDOW (application_window), application_name);
    gtk_window_set_default_size (GTK_WINDOW (application_window), application_width, application_height);
    gtk_widget_show (application_window);
    GtkWidget *box = gtk_box_new (GTK_ORIENTATION_VERTICAL, application_box_padding);
    gtk_style_context_add_class(gtk_widget_get_style_context(box), application_box_class);
    gtk_container_add (GTK_CONTAINER (application_window), box);
    css_add(css);
    
    /* Add documentation. */
    char *text;
    text =
        "To test GTK CSS options for borders, we create a GTK box, add a CSS class name, then test CSS options for borders.\n"
        "\n"
        "The default border width can be too narrow. You change the border width using the gtk_container_set_border_width function as shown in the following code.\n"
        "gtk_container_set_border_width (GTK_CONTAINER (box), 10);\n"
        "The border width function sets the margin on a box. You can do that and more with CSS instead of using this limited function.\n"
        "\n"
        "In this code, we create multiple boxes, give each box a unique class, then set CSS for the class.\n"
        "Every test box starts with class border which gives them { border-color: #cccccc; border_style solid; border-width: 10px; }\n"
        "The application level window and box have CSS only for background colour. Read more at https://petermoulding.com/gtk_box_borders"
        ;
    label_add (box, text);
    
    box_add (box, "border_solid",  "This box has border-style: solid;");
    box_add (box, "border_margin", "This box adds margin: 10px; ");
    box_add (box, "border_padded", "This box adds padding: 10px; ");
    box_add (box, "border_both",   "This box has both margin and padding - margin: 10px; padding: 10px; ");
    box_add (box, "border_color",  "Note the colours and font style - background-color: navy; border-color: lime; color: yellow; font-style: italic; ");
    box_add (box, "border_radius", "border-radius breaks Gtk CSS. You end up with no CSS. I tested with this CSS: border-radius: 10px;");
    box_add (box, "border_none",   "border-style: none;  The border-width and border-colour do nothing because there is no border.");
    box_add (box, "border_hidden", "border-style: hidden; The border-width and border-colour could do something if you unhide this box.");
    box_add (box, "border_dashed", "border-style: dashed;");
    box_add (box, "border_dotted", "border-style: dotted;");
    box_add (box, "border_double", "border-style: double;");
    box_add (box, "border_groove", "border-style: groove;");
    box_add (box, "border_ridge",  "border-style: ridge;");
    box_add (box, "border_inset",  "border-style: inset;");
    box_add (box, "border_outset", "border-style: outset;");
    
    gtk_widget_show_all (box);
    return TRUE;
    }

void css_add(char *css)
    {
    /* CSS */
    GError *error = NULL;
    GtkStyleContext *context;
    GtkCssProvider *provider = gtk_css_provider_new ();
    gtk_css_provider_load_from_data (provider, css, strlen (css), &error);
    if (error != NULL)
        {
        fprintf (stderr, "CSS: %s\n", error->message);
        }
    gtk_style_context_add_provider_for_screen(
        gdk_screen_get_default(),
        GTK_STYLE_PROVIDER(provider),
        GTK_STYLE_PROVIDER_PRIORITY_USER);
    }

void box_add(GtkWidget *parent_box, char *class, char *text)
    {
    GtkWidget *box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
    gtk_style_context_add_class(gtk_widget_get_style_context(box), border_class);
    gtk_style_context_add_class(gtk_widget_get_style_context(box), class);
    gtk_container_add (GTK_CONTAINER (parent_box), box);
    label_add (box, text);
    }

void label_add (GtkWidget *parent_box, char *text)
    {
    GtkWidget *label = gtk_label_new (text);
    gtk_widget_set_halign(label, GTK_ALIGN_START);
    gtk_container_add (GTK_CONTAINER (parent_box), label);
    }


 

Tags