Gtkmm: Building an Android-like screen transition

By Jean Hertel, 8/16/20

gtkmm , gtk+ , gtkstack , android

In the last adriconf update I decided that I wanted to change the way that users use the application and for this I decided to implement several changes.

One of these changes was to completely remove all dialog boxes and replace them with something simpler. I really like the screen transitions of Android because they’re soft and don’t give the impression that something is still running on another screen as usually seen with modal dialogs. But how to implement a screen transition using only GTKmm?

GTK+ (and GTKmm by extension) have an excellent component called Stack. With this component we can create several screens showing only one at a time. In the official documentation the examples always use a GTKStackSwitcher to give users the option to easily switch between screens but in our case it is easier to call the method set_visible_child.

Let’s see an example in action:

<?xml version="1.0" encoding="UTF-8"?>
<interface>
<requires lib="gtk+" version="3.22"/>
<object class="GtkWindow" id="mainWindow">
    <property name="width_request">200</property>
    <property name="height_request">200</property>
    <property name="can_focus">False</property>
    <child>
    <object class="GtkStack" id="ourStack">
        <property name="visible">True</property>
        <property name="can_focus">False</property>
        <property name="transition_type">slide-right</property>
        <child>
        <object class="GtkBox" id="box1">
            <property name="visible">True</property>
            <property name="can_focus">False</property>
            <property name="orientation">vertical</property>
            <child>
            <object class="GtkButton" id="button1">
                <property name="label" translatable="yes">To Screen 2</property>
                <property name="visible">True</property>
                <property name="can_focus">True</property>
                <property name="receives_default">True</property>
            </object>
            <packing>
                <property name="expand">False</property>
                <property name="fill">False</property>
                <property name="position">0</property>
            </packing>
            </child>
        </object>
        <packing>
            <property name="name">screen1</property>
        </packing>
        </child>
        <child>
        <object class="GtkBox" id="box2">
            <property name="visible">True</property>
            <property name="can_focus">False</property>
            <property name="orientation">vertical</property>
            <child>
            <object class="GtkButton" id="button2">
                <property name="label" translatable="yes">Back to screen 1</property>
                <property name="visible">True</property>
                <property name="can_focus">True</property>
                <property name="receives_default">True</property>
            </object>
            <packing>
                <property name="expand">False</property>
                <property name="fill">True</property>
                <property name="position">0</property>
            </packing>
            </child>
        </object>
        <packing>
            <property name="name">screen2</property>
            <property name="position">1</property>
        </packing>
        </child>
    </object>
    </child>
    <child type="titlebar">
    <placeholder/>
    </child>
</object>
</interface>

With these components we can now create some code:

#include "gtkmm.h"

int main(int argc, char *argv[]) {
    auto app = Gtk::Application::create(argc, argv, "nice.test");

    // Load our glade file
    auto gladeBuilder = Gtk::Builder::create();
    gladeBuilder->add_from_resource("/test/test.glade");

    Gtk::Window *mainWindow;
    Gtk::Stack *stack;
    Gtk::Button *button1, *button2;

    // Get our widgets
    gladeBuilder->get_widget("mainWindow", mainWindow);
    gladeBuilder->get_widget("ourStack", stack);
    gladeBuilder->get_widget("button1", button1);
    gladeBuilder->get_widget("button2", button2);

    // Setups what happens with our application
    button1->signal_clicked().connect([stack]() {
        stack->set_visible_child("screen2");
    });

    button2->signal_clicked().connect([stack]() {
        stack->set_visible_child("screen1");
    });


    return app->run(*mainWindow);
}

With this code we will have a screen similar to this one below: Screen transition

You may have noticed that the transition effect is always going to the right. We can control this with the transition_type property of Gtk::Stack.

Another point we should note is in relation to the id we use in the screen switching. If you look at the code you can see that the Gtk::Box have the id of box1 and box2 but when we invoke the method set_visible_child we use the names screen1 and screen2. The reason for this is that the Gtk::Stack object can only have children of a certain type and therefore will always encapsulate objects of the wrong type. We solved this by defining the name property in the packing properties of the object.