After the Hiatus
It’s been more than a year since I made my last post on this blog. When I stopped posting last year, I was burned out, distracted by COVID-19, our tiny apartment was suddenly a hub of work activity for both of us, and I was still disheartened by the changes being made to GTK in version 4.
Now, I can see things a little more clearly… sort of. COVID-19 is still here and we may all go into (at least) one more round of lock-downs. But, other things are advancing…
My wife and I have both been double-vaccinated and we’ve worked out an arrangement whereby we can both work in this tiny apartment without driving each other up the wall.
Thirteen days ago, Mike Wey released GtkD 4, but the articles I’ve got on the go are all centred around GtkD 3.9. And frankly, I’m not sure I want to update to 4 because the GTK team dropped window position handling. Yes, it’s a small thing, but I see a problem with this…
The GTK team members believe window positions should be handled by the OS’s window manager. I do agree with them except for one thing: not all window managers remember window positions. I use three monitors, so this is kind of important to me. I like my applications to open in the last place I used them so I don’t have to search miles of screen real estate to find them.
So, with that in mind, I’d like to hear from you. Is anyone still interested in articles about GtkD 3.x? Please let me know in the comments below.
And with all that said, let’s dig into today’s article.
0112: GTK GIO Applications - Introduction
Up ‘til now, every example has been built up from a
MainWindow widget and a
Main struct, both of which are instantiated in the standard entry point function,
TestRigWindow—the actual object we’ve been instantiating in our examples—inherits from
MainWindow, so it amounts to the same thing.) But today, we’re looking at an alternative way of building applications, this time using the GTK/GIO
Application class modules.
I’d like to point out that I didn’t stutter in that last sentence. There are two
Application modules… the GIO
Application is the parent class and the GTK class is derived from it. This can be a bit confusing when it comes time to write code because both modules need to be imported if we want to handle GIO signals. But, there’s a simple way to keep them straight, so let’s just dive in.
Application is a more flexible framework for writing software. It doesn’t just give us the tools for building classic GUI-based software, it makes several other project types possible:
- a GUI front-end for a background service,
- the background service itself,
- remote applications controlled locally,
- local applications controlled remotely, and
- a GUI-less command line application (the kind intended to be run from a terminal).
On top of that, a GIO
Application has a signal/callback system that gives us all kinds of flexibility in how we start up our application.
Finally, it also gives us a system of flags we can use for all kinds of stuff including:
- designating the application as a service,
- passing in parameters to modify the behaviour of the running software, or
- react to the existence of environment variables on the computer where the application is running.
Old Method vs. New
The biggest difference between the
MainWindow approach and this one is this…
Application paradigm, signals are used to associate callbacks with such things as
shutdown. In the paradigm we’ve been using ‘til now,
Main.quit() (respectively) take care of these things. Nothing new is going on here, but responsibility for application-level stuff has shifted from a C-style struct (
Main) to a D-style object (
MainWindow vs. ApplicationWindow
In the classical construction method,
MainWindow acts as a top-level container for our program, but the GTK/GIO
Application instead uses the
ApplicationWindow as its primary UI container. They both inherit from
Window, so we still get pretty much the same functionality. But the GTK/GIO
Application construction method adds such things as window
IDs, a pop-up help window for keyboard shortcuts, and a mechanism to handle how and when a
Menubar is displayed. More on these as we go along, but for now, let’s dig into a barebones example…
Working with GIO/GTK Applications
Right up front, naturally, we need to do some imports to get all this working. But because the GTK and GIO modules are both named
Application, we need to put some extra effort into keeping them straight. That’s done with D’s aliasing feature:
import gio.Application : GioApplication = Application;
import gtk.Application : GtkApplication = Application;
Alias names are up to you, of course, but for this demonstration, I’m emphasizing clarity of function.
How main() Differs
In the classical construction method, our
main() function had to do a few things before handing control over to the
struct… I’m pointing this out because, as it turns out,
Main is a
struct, not a
class. It’s the reason we don’t sub-class it into
MyMain or some-such in order to move its definition outside of the
With the GTK/GIO
Application construction method, however, we don’t have this restriction, so
main() only has to do one thing, instantiate the GTK
void main(string args)
MyApplication thisApp = new MyApplication(args);
} // main()
Note that it passes any command-line arguments along to the
MyApplication constructor, another change from the old way of doing things in which we passed them to
Main.init(). And keep in mind,
MyApplication is derived from the GTK
Application object, not its GIO namesake.
Speaking of which, let’s have a look at this sub-class…
MyApplication, a GTK Application Lovechild
The preamble looks like this:
class MyApplication : GtkApplication
ApplicationFlags flags = ApplicationFlags.FLAGS_NONE;
string id = null; // if application uniqueness isn't important
In order to call the super-class constructor, we need two things:
- one or more flags to set up the
Application’s type and abilities, and
IDin the form of a string.
Now, you’ll note that this particular example has
id set to
null. That’s because, if we really don’t care about
Application uniqueness, we don’t have to supply an
ID. In the next example, we’ll talk more about this, but for now, let’s move on to…
The MyApplication Constructor
This is where we set up the
Application and get things going:
} // this()
The first line isn’t all that different from what we’re used to. We call the super-class constructor, passing it the
flags variables. In this example, because
null and our
flags variable is
NONE, we aren’t asking anything of the super-class constructor except to start the application.
The next line, however, is a departure from the old method we’ve been using. I’m sure you recognize the function naming convention even if you don’t know the
addOnActivate() function itself. It’s a signal hook-up, as you’ve likely already guessed. Why it’s there is because (and you might wanna make an extra-large mental note of this):
Application actions are processed via signals and callbacks.
This means the GIO/GTK
Application construction approach brings external operations directly under the control of a single, high-level entity, the
Application object. I’m talking about things like window and accelerator management… or OS-related tasks such as handling command-line arguments, starting up, shutting down… In other words, because the uppercase-A
Application handles all things external, the lowercase-a application (in other words: our code) doesn’t need to be aware of them. There’s no mixing of internal and external operations and therefore better separation of code.
Looking at the last line, we find another way this new application construction method differs from the old. The command-line arguments—instead of being passed to an
init() function—are passed to the
What’s the difference? Not much, actually. Both
Gio.Application.run() count the number of command-line arguments and build a string array with one element per argument. The biggest difference here is that
Main is a C struct whereas
Gio.Application is a proper class and is more consistent with the OOP paradigm we’ve been using in every example posted on this blog.
The onActivate Callback
This is the only place, in a simple application, where we need to reference the
void onActivate(GioApplication app) // non-advanced syntax
AppWindow appWindow = new AppWindow(this);
} // onActivate()
This is because the
addOnActivate() function lives in that module. There are other signal hook-up functions in
Gtk.Application and, of course,
Gtk.Application inherits from
Gio.Application, but when hooking up signals—just as we’ve seen elsewhere—we need to declare the arguments to be exactly what they are in the wrapper file.
We have one last class to look at…
The AppWindow Class
This class, as mentioned above, inherits from
ApplicationWindow which in turn inherits from GTK
Window which means for the most part, it’s just another
Window. It does have a few features not found in the generic GTK
Window, however, things like:
IDs to make window management easier,
- help overlays (more on these in a moment), and
- hideable menubars.
IDs—is more or less self-explanatory. The
IDs for window management.
Windows can be added, removed… you get the idea.
Help overlays, however, aren’t something we’ll find in the old construction method. These are inspired by mobile apps where the help screen slides in over top of the
ApplicationWindow and slides back out when we’re done with it.
Menubars are also inspired by mobile apps, although they’re becoming more prevalent on desktops as well.
Anyway, that’s all nice in theory, but how about a look at the code:
class AppWindow : ApplicationWindow
int width = 400, height = 200;
string title = "Barebones Application";
} // this()
} // class AppWindow
As with run-of-the-mill
MainWindows, we set up dimensions and a title in the preamble, then in the constructor we call the super-class constructor, set the size, the title, and then call
showAll(). The only thing here that departs from the old construction method is setting up an
Application pointer which we pass to the super-class constructor, so what’s up with that?
Not a lot, really. We’re setting up an association between the
ApplicationWindow and the GIO/GTK
Application so the
Application can manage the
ApplicationWindow. Makes sense, right?
Anyway, that’s all for today. This should give you a basic understanding of what’s going on behind the curtain when you use this alternate application construction method.
Next time, we’ll dig a little deeper. See you then.
You're free to accept or decline this invitation to become our newest sponsor.
Comments? Questions? Observations?
Did we miss a tidbit of information that would make this post even more informative? Let's talk about it in the comments.
- come on over to the D Language Forum and look for one of the gtkDcoding announcement posts,
- drop by the GtkD Forum,
- follow the link below to email me, or
- go to the gtkDcoding Facebook page.
You can also subscribe via RSS so you won't miss anything. Thank you very much for dropping by.
© Copyright 2023 Ron Tarrant