By Jean Hertel, 16.08.20
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:
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.