GTK3 themes

Starting with Xojo 2017r2, Linux apps now use GTK3. Since Xojo uses native controls that means your app's controls will use the theme of the Linux distribution the app runs on. This can sometimes mean that your app's UI will not look exactly like what you designed in the Layout Editor because a theme may dramatically change control sizes and padding. This is a problem that can occur with any modern GTK3 app.

If this is a problem for your apps, there are a couple ways you can work around it:

  • Use Fixed/Locked Control Padding

  • Have Your App Load and Use a Specific Theme

Both of these solutions can be handled by Declares that you put in your App.Opening event handler.

Use fixed/locked control padding

For many apps, the primary concern is to not have controls change their size or position depending on the theme being used. You can ensure specific styling for controls in your apps by using a static amount of padding that won't change regardless of the user's theme selection. This has the benefit of letting your app use other aspects of the theme, such as colors, while maintaining control layout better.

You can put this code in the App.Opening event handler to set a specific padding:

#If TargetLinux Then
  Declare Function gdk_screen_get_default Lib "libgdk-3" () As Ptr
  Declare Function gtk_css_provider_new Lib "libgtk-3" () As Ptr
  Declare Function gtk_css_provider_load_from_data Lib "libgtk-3" _
    (provider As Ptr, data As CString, dataLen As Integer, error As Ptr) As Boolean
  Declare Sub gtk_style_context_add_provider_for_screen Lib "libgtk-3" _
    (screen As Ptr, provider As Ptr, priority As UInt32)
  Declare Sub g_object_unref Lib "libgobject-2.0" (obj As Ptr)

  Const GTK_STYLE_PROVIDER_PRIORITY_APPLICATION = 600

  Var screen As Ptr = gdk_screen_get_default()
  Var provider As Ptr = gtk_css_provider_new()
  If provider = Nil Then Break

  Var data As String = "GtkWidget:not(GtkMenuItem) { padding-left: 1; padding-right: 1; padding-top: 1; padding-bottom: 1;}"
  If gtk_css_provider_load_from_data(provider, data, Data.Len, Nil) Then
    gtk_style_context_add_provider_for_screen(screen, provider, GTK_STYLE_PROVIDER_PRIORITY_APPLICATION)
  End If

  g_object_unref(provider)
#Endif

Have your app load and use a specific theme

If you would like your app to use a specific theme so that your layout works exactly as you want it, you can bundle a theme and load it when your app starts. Your app will use this loaded theme regardless of what the user has selected as a theme for the distribution. You load the theme from the App.Opening event handler like this:

#If TargetLinux Then
  Declare Function gdk_screen_get_default Lib "libgdk-3" () As Ptr
  Declare Function gtk_css_provider_new Lib "libgtk-3" () As Ptr
  Declare Function gtk_css_provider_load_from_path Lib "libgtk-3" _
    (provider As Ptr, path As CString, error As Ptr) As Boolean
  Declare Sub gtk_style_context_add_provider_for_screen Lib "libgtk-3" _
    (screen As Ptr, provider As Ptr, priority As UInt32)
  Declare Sub g_object_unref Lib "libgobject-2.0" (obj As Ptr)

  Const GTK_STYLE_PROVIDER_PRIORITY_APPLICATION = 600

  Var screen As Ptr = gdk_screen_get_default()
  Var provider As Ptr = gtk_css_provider_new()
  If provider = Nil Then Break

  ' Load the theme
  Var themePath As FolderItem
  themePath = App.ExecutableFile.Parent.Parent.Child("mintxtheme").Child("gtk.css")

  If gtk_css_provider_load_from_path(provider, themePath.NativePath, Nil) Then
    gtk_style_context_add_provider_for_screen(screen, provider, GTK_STYLE_PROVIDER_PRIORITY_APPLICATION)
  End If

  g_object_unref(provider)
#Endif

In the above code the "mintxtheme" that is used is loaded from a folder that is placed alongside the app's folder, but you can choose to load it from anywhere and may want to bundle it in your app's Resouces folder. You can download Mint themes from here: Mint Themes Download

Analyzing theme properties

The above code should be sufficient for most people's apps. But if you require even more control of themes and how they render widgets you will have to dig into Linux more. The GTK+ Inspector can be a great tool for this as it lets you see property and widget information in real time.

The GTK+ Inspector

On some distributions you may need to install the "libgtk-3-dev" package before GTK+ Inspector can be enabled.

You may also find it helpful to review the GtkCssProvider documentation.

modGtk3

The Xojo community has created a project (download modGTK3) that contains all the required Declares and Styles for theming. If you intend to use it in your own projects, have a look at where the global theme is set, and where/how the Control tweaks are set:

  • App.Openinging -> modGTK3.InitGlobalGTK3Style()

  • Control.Opening -> me.InitGTK3Control()

Note: modGTK3 extends InitGTK3Control for Controls. In this example project, it's called from Control Subclasses. Subclassing your Controls is highly recommended anyway, as you get a single place for tweaks and workarounds for different XojoVersions or BuildTargets.

Refer to the original Xojo forum conversation for more information.