Haskell and GUI revisited

It had been over two years since I last looked at them, but I returned to Haskell, GUI and GTK4 on Windows. This time, things were more straightforward. To set things up again, I added the following MSYS2 packages to the Stack-supplied MSYS2 with stack exec -- pacman -S:

and set PKG_CONFIG_PATH to the mingw64\lib\pkgconfig directory and XDG_DATA_DIRS to the mingw64\share directory (of the Stack-supplied MSYS2). I found it was necessary to install the MSYS2 packages before building the Haskell packages that depend upon them.

MSYS2 package mingw-w64-x86_64-gtk4 does not have a dependancy on mingw-w64-x86_64-atk, but Haskell package gi-gtk >= 4.0 has a dependency on gi-atk. I asked a question about that at the haskell-gi GitHub repository.

A basic gktTest

I named the example package gtkTest, as before, with Main.hs as follows (being the haskell-gi repository’s current “Hello! World” program):

with a package.yaml specifying dependencies:

and with Stack project-level configuration:

I sought to understand how this program worked and, in particular, the use of ?self and app.run.

new

new is used in connection with app, window and button. Module Data.GI.Base.Constructible provides a type class Constructible with a default instance:

In the code extract above, and other extracts, I have renamed type variables for consistency between extracts.

GI.Gtk.Objects.Application.Application is an instance of GObject.

Data.GI.Base.GObject.constructGObject makes use of Data.GI.Base.Signals.on:

and on binds the implicit parameter ?self.

Given the language extension ImplicitParams, the implicit parameter ?self can be passed to activate.

AttrOp

Data.GI.Base.Attributes.AttrOp is a type with a number of data constructors, including (:=) (assign a value to an attribute) and On (connect the given signal to a signal handler.).

app.run Nothing

In the example, app.run means the same as #run app.

Given the language extension OverloadedRecordDot, app.run is equivalent to getField @"run" app.

Module GHC.Record provides type class HasField which promises getField:

Given the language extension VisibleTypeApplications, @"run" in getField @"run" app specifies the type t in type class HasField t obj p. Given the language extentions DataKinds, "run" is a type-level literal of kind String. As app is of type Application, that specifies type obj in type class HasField t obj p.

Module GI.Gtk.Objects.Application provides an instance of HasField

Including the instance head (HasField t Application p) as a constraint in the instance context is a ‘trick’ which permits polymorphic fields.

Data.GI.Base.Overloading.overloadedMethod is promised by type class OverloadedMethod:

Given the language extension VisualTypeApplications, the @info in O.overloadedMethod @info specifies the type info in type class OverloadedMethod info obj s. info is constrained to be equivalent to ResolveApplicationMethod t Application. When t is the type literal "run", that is equal to ApplicationRunMethodInfo (see further below).

Module GI.Gio.Objects.Application provides an instance of OverloadedMethod:

and module GI.Gtk.Objects.Application provides a type family:

A less basic gtkTest

I tried a less basic version of gtkTest, using a Grid of three Button values, with Main.hs:

The result was as below, but I had to specify #hexpand := True for the buttons to take advantage of the available horizontal space. That is because an ApplicationWindow is a single-child container while a Grid is a multi-child container; the default behaviour is different.

A gtkTest with input and output

I tried a further variation with input (two Entry values) and output (a Label value):

The use of Data.Text.show requires Haskell package text >= 2.1.2.

The default value of halign is AlignFill, which centres when there is no meaningful way to stretch.

The result was as below: