Builder GTK 4 Porting, Part IV

This week was a little slower as I was struggling with an adjustment to my new medication. Things progress nonetheless.

Text Editor

I spent a little time this week triaging some incoming Text Editor issues and feature requests. I’d really like this application to get into maintenance mode soon because I have plenty of other projects to maintain.

  • Added support for gnome-text-editor - to open a file from standard input, even if you’re communicating to a single instance application from terminal.
  • Branch GNOME 42 so we can add new strings.
  • Fix a no-data-loss crash during shutdown.


  • Fix template evaluation on macOS.
  • Make boolean expression precedence more predictable.
  • Cleanup output of templates with regards to newlines.


  • Propagate modified page state to tabs
  • Some action tweaks to make things more keyboard shortcut friendly.


  • Merged support for configuration editing from Georges.
  • Add lots of keybindings using our new keybinding engine.
  • Track down and triage that shortcut controllers do not capture/bubble to popovers. Added workarounds for common popovers in Builder.
  • Teach Builder to load keybindings from plugins again and auto-manage them.
  • Lots of tweaks to the debugger UI and where widgetry is placed.
  • Added syntax highlighting for debugger disassembly.
  • Added menus and toggles for various logging and debugger hooks. You can get a breakpoint on g_warning() or g_critical() by checking a box.
  • Ability to select a build target as the default build target finally.
  • More menuing fixes all over the place, particularly with treeviews and sourceviews.
  • Fix keyboard navigation and activation for the new symbol-tree
  • Port the find-other-file plugin to the new workspace design which no longer requires using global search.
  • GTK 4 doesn’t appear to scroll to cells in textview as reliably as I’d like, so I dropped the animation code in Builder and we jump strait to the target before showing popovers.
  • Various work on per-language settings overrides by project.
  • Drop the Rust rls plugin as we can pretty much just rely on rust-analyzer now.
  • Lots of CSS tweaks to make things fit a bit better with upcoming GNOME styling.
  • Fix broken dialog which prevented SDK updates from occurring with other dependencies.

A screenshot of builder's find-other-file plugin

A screenshot of Builder's debugger

A screenshot showing the build target selection dialog

A screenshot of the run menu A screenshot of the logging menu

Builder GTK 4 Porting, Part III

Another week of porting Builder which ultimately sent me on a few fun tangents. I especially enjoyed the work on Template-GLib which brought me back to my days working on languages and runtimes.


  • Prototype and submit a merge request to add support for setting an action parent on a widget. This allows you to alter the normal GtkActionMuxer action resolution. Very handy for situations like Builder and other document-oriented applications.


  • GtkSourceView updates to make it possible to write an external “snippet editor” application (Günther is working on one already)
  • Improve Solarized style schemes a bit for better IDE integration by specifying colors for diff as they can be used by git-based gutter renderers.
  • A little more performance work on gutter renderers as they have always been a major source of runtime costs.
  • Allow gutter renderers to overdraw atop the textview so they can do some more fancy things.


  • Figured out why I was getting spinning CPU with the VTE port for GTK 4. Submitted a diagnosis/fix upstream.


  • Prototyped and landed a new shortcut manager with uses a keybindings.json-like file similar to VS Code (albeit with slightly different syntax which makes more sense for GTK applications). Plugins will be able to override and extend these, as will the user.
  • Prototyped and landed support for showing “selection area” within the gutter renderer.
  • Compress information in a bit tighter space for gutter, as we have a lot of information to display there. Still more we can do should anyone have free time to work on this.
  • Improve breakpoints drawing now that we have some overlapping selections to worry about.
  • Lots of menu cleanup across plugins.
  • More porting from our old IdeSourceView into a new implementation.
  • Lots of object-lifecycle fixes now that Builder more aggressively shuts down components.

Keybindings look something like this.

I would like to specify how wonderful the new shortcut components are in GTK 4, particularly if you’re writing complex applications that have to manage layered shortcuts, user overrides, and such. I’m thinking applications in the class of Inkscape, GIMP, Builder, Darktable, and such will really benefit from this someday.

A screenshot of Builders updated line selections


One of the things that Builder needed was the ability to express when a shortcut is active. In VS Code, they have a "when" element which works well enough for this. However, it has some basic expressions that need to be evaluated at runtime.

Builder already has an expression engine which is used for project templates and what not and it even supports GObject Introspection. It was in sore need of some updates but is very capable for the problem here. I have the tendency to only implement enough to solve problems so this was a nice return to finishing some of that up.

One of the nice things here, when the use case is so focused, is that there is no need for a runtime, JIT, etc. It’s just an AST that has a tmpl_expr_eval() API where you pass in scope and all the objects continue to live in C land, nothing special to deal with.

  • Add anonymous functions (no lambdas though).
  • Allow assigning functions to a symbol.
  • Improve function calling under various situations (named, anonymous, and GObject Introspection functions).
  • Make constructors work.
  • Add a bunch of builtins for things like asserts, casts, math.
  • Fix typeof() builtin for a number of GObject Introspection cases.
  • Add unit tests!
  • Add null keyword.
  • You can now do things like typeof(widget).is_a(typeof(Ide.Page))
  • The require Gtk version "4.0" style imports made it so you couldn’t call .require() due to the Bison parser. That is fixed and you can use @keyword for reserved keywords.
  • A nop has been added with the keyword pass.
  • Fix a bunch of valgrind discovered things.
  • Add linewise comments using the # python style.

I don’t know how much time I’ll spend on this in the future, or if it will go another few years without much work. But with a maintainer who had time, it could be a nice little glue language without all the “muchness” that more well known languages tend to bring in.

Builder GTK 4 Porting, Part II

Another week of work towards porting Builder to GTK 4. Since I can’t add to TWIG from IRC, I’ll try harder to drop some occasional updates here.


  • Merged fixes for highlighting unicode literals for C
  • Improved parsing of language values in snippet bundles
  • GtkSourceGutter will not correctly mark prelit and selection quarks within GtkSourceGutterLines.
  • Fixed a bunch of little mouse pointer annoyances when using GtkSourceHover interactive tooltips.
  • GtkSourceGutterRenderers can now opt-out of signal emission for GtkSourceGutterRenderer::query_data(). Signal emission with GObject is rather slow, so avoiding it on every line can be helpful. Just set the virtual method pointer to NULL. The signal was only ever added to make porting easier anyway.


  • Merged fixes to be used as a subproject with static libraries only
  • CSS styling matches recent changes in libadwaita, particularly around making navigation tabs and panel frame headers more unified.
  • PanelWidget no longer uses a GtkBinLayout so that it’s easier for subclasses to integrate with popovers from size_allocate() to call gtk_popover_present().


  • Georges did a live coding stream where they ported a bunch of the “buildui” plugin. That is merged to the GTK 4 port now. It brings a number of features back to the UI including the build terminal, run terminal, build panel (with pipeline stages, warnings, and errors), and project information in the omnibar popover.
  • Günther did a bunch of work porting our old snippet files to the new XML-based snippet bundle format upstream in GtkSourceView. Along with that came porting of the snippets plugin for Builder’s new GTK 4-based editor.
  • Workspace windows have a bit better predictability when restoring sizes.
  • The project creation workflow was ported, albeit needs to have the redesign implemented still.
  • Lots of resiliency fixes for clang and symbol-tree plugins to improve life-cycle management.
  • The Valgrind plugin was ported to C. This was also back-ported to main because it fixed a number of oddities (crashes) occurring in PyGObject.
  • Builder’s “omni-gutter” GtkSourceGutterRenderer was ported to GTK 4 and got a lot of cleanups along the way. I believe there is still some outstanding things to fix such as handling rendering for symbolic icons as I’m pretty sure that’s not correct yet.
  • The “debuggerui” plugin has been ported to GTK 4 and appears to be working well now. This plugin is responsible for bridging the internal IdeDebugger interfaces to the UI interface.
  • Style schemes were updated for GtkSourceView 5
  • Tabs are now the default navigation interface for pages. There is likely still a lot to do around how we want empty frames to look and behave though.
  • The project-tree is now most ported, and with some workarounds to allow making GtkPopover work you can actually display popovers and activate menu items.
  • A long-standing plugin ordering issue has been fixed so that gtk/menus.ui embedded in plugin resources have menu-merging in the proper order.
  • Some incremental work landed to support per-project configuration of languages.
  • The “ls” plugin (directory views) supports “humanized” timestamps again and columns are resizable once more.
  • You can place panels in the right-side-bar now.


There are a bunch of foundational things to still get landed before I feel I can get Builder flipped over to our Nightly builds. In particular we need to land support for things like:

  • Keyboard shortcuts using GtkShortcutController. This was all done with libdazzle previously.
  • Allow plugins to define custom keyboard shortcuts and merge them into the controller.
  • Port “shellcmd” plugin and integrate keyboard shortcuts to apply those commands.
  • Finish rewrite of the search popover. I’m trying to delay this until GtkListView with sections is supported, as it would provide us a much greater path for performance.
  • A lot of our problems would be simpler if we could make GtkActionMuxer use an alternate action muxer parent from another (non-descendant) widget.
  • Configuration editing in the project configuration window. This is a new design so the port is not 1:1.

A screenshot of Builder with the project-tree context menu and debugger on display

A screenshot of Builder with various panels moved around to non-standard locations

Builder GTK 4 Porting, Part I

Now that GNOME 42 was released in March I’ve had spare cycles to focus on porting Builder to GTK 4 in April. I’ve made a lot of progress thus far, but there is still plenty to go.

Builder is certainly one of the larger GTK applications out there and so it can be a bit daunting unless you embrace your inner hubris and just forget where the end of the tunnel is.

During this porting effort I’ve also been taking time to redo a number of things that just weren’t working well for us. Additionally I’ve spent significant time on what would appear to be tangents, with the goals of easing the porting effort. For example, before I set off on the port I spent time doing things like

  • Work with Matthias Clasen to make GtkTextView support GPU rendering, then make it fast. More recently we’ve even experimented with drawing font shapes at any scale on the GPU thanks to work by Behdad on GLyphy.
  • Write a new macOS back-end for GTK 4 to improve our cross-platform portability and performance story
  • Modernize GTK 4’s GL renderer to help reduce overhead. And then modernize that a bit more to work around Apple’s lack of something like eglSwapBuffersWithDamage().
  • Port Sysprof to GTK 4 and continue helping integrate it into layers of the stack like GLib, GJS, GTK, Pango, GtkSourceView, GNOME Shell, and more.
  • Port D-Spy to GTK 4 and abstract a library for Builder usage
  • Port GtkSourceView to GTK 4
    • Improve GPU rendering by premultiplying alpha, altering when/where some nodes are snapshotted, and how we draw certain textures.
    • JIT’d regexes for syntax highlighting sub-system using PCRE2 instead of the old GRegex APIs
    • JIT’d regexes for text search sub-system also with PCRE2
    • Port Builder’s completion engine upstream to reduce time-to-display and memory overhead
    • Port Builder’s snippet engine and syntax upstream
    • Port Builder’s interactive tool-tips (Hover Providers) upstream
    • Port Builder’s auto-indentation engine upstream
    • Write a new Vim emulation engine upstream in GtkSourceView so we can drop Builder’s
  • Work with Alexander to implement application-wide recoloring support for libadwaita-based applications using GtkSourceView style-schemes
  • Write GNOME Text Editor to prove all the components are working
  • Write a new spell-check engine for GTK 4 (also used by Text Editor) including the creation of a new hybrid data-structure combining a piecetable and a B+Tree
  • Write a new docking/panel engine for GTK 4
  • Write a GTK 4 based designer (Drafting) in coordination with my upstream GTK 4 work so that we can support separation of designer and build/dev containers where code may run. More on that later though when it lands in Builder.

We’ve also been eagerly awaiting GTK 4 ports of both WebKit and VTE, neither of which are trivial efforts to do well. And by well I mean passing GL composited GL textures for a browser or taking advantage of GTK’s fast text rendering/caching/layout. The good news is that these are starting to come together.

People that follow my Twitter have already seen these of course. My goal is to land the GTK 4 port in Builder’s Nightly in May some time so that we get plenty of testing before GNOME 43. It’s a large application, so it will need all the testing it can get.

A screenshot of Builder's GTK 4 port


Preferences is getting a major revamp in our GTK 4 port to make things a bit nicer to look at and also easier to locate features and tweaks. The tricky part with things like this is making it easy for plugins to extend it without “reeking of plug-in based design”.

A screenshot of Builder's new preferences window

Another thing we’ve always wanted in Builder but couldn’t really do at the time was to allow per-project overrides. I’m happy to say that we will likely have this landed in our GTK 4 port. A number of things like per-language settings can be overriden on a per-project basis. I also hope to see build systems and tooling extend the project settings much more than we did in the past. For example, the Meson plugin could use meson_options.txt to add appropriate toggles.


Builder has always had a minimalist design to keep “machinery” out of the way and in most cases just do the right thing for you. We still want that design but we also want to expand on the things you can do. So you’ll find some new menuing in Builder’s GTK 4 port which will get wired up soon to do just that.

Things that have to do with Building can be found near the Build button.

A screenshot of Builder's build menu in the center top of the window

Things that have to do with Running can be found near the Run button.

A screenshot of the run menu in the center top of the window.

We’ve also dropped the “surface” abstraction in the GTK 4 port as it really only added confusion without much benefit. Our new paneling engine will make our reasoning for having that abstraction a non-issue.


You’ll notice that the GTK 4 port of Builder has a statusbar which can be extended by plugins. I tried for years to avoid it, but honestly, it just makes designing things easier and everyone is already using them in similar products. No need to begrudgingly hold out from a design standpoint.

Text Editor with More

Some people really don’t like IDEs, and I totally get that.

However, if you like the new Text Editor I wrote for inclusion in GNOME 42 but find it’s not quite powerful enough for the type of programming you do, Builder might still be able to help you out. It has an editor mode which is a much more powerful version of Text Editor with a robust and mature plugin architecture.

A screenshot of Builder's simple editor mode.

Additionally, users of Builder’s IDE functionality can add one of these supplemental editor windows to additional monitors. The window will be linked to your project’s primary workspace meaning all the same functionality is there, simply on an additional monitor.


It even runs on macOS! If you’d like to see this work better there, I imagine there are opportunities to improve transparent podman support so that people who use macOS can still take advantage of Linux containers. Even though I wrote the new macOS back-end for GTK, I don’t actually use it in my daily life, so my effort there is going to be limited.

A screenshot of Builder's GTK 4 port running on macOS

Additionally, if you’d like to help on things, the scope of Builder can grow considerably now that we have a toolkit that will enable us to do that. If you’d like to help do things like simulators, cross-device integration, debuggers, language server integration, or other fancy new features, we’d love your contributions!

Rendering Text with Glyphy

HarfBuzz 4.0 was recently released and it gained the ability to draw shapes using hb_draw(). Recently Behdad updated Glyphy to use that new HarfBuzz API which allows us to draw fonts on the GPU without involving FreeType.

I created a branch and along with Matthias we taught GTK’s OpenGL renderer to use Glyphy to store size-independent descriptions of glyphs in GL texture atlases which are drawn with shaders. All the magic really comes from Glyphy, so this was largely an integration project.

Contrast this with how things worked previously, which is to render the glyph for a particular size and x/y shift on the CPU and upload that to a texture atlas. The glyph is then drawn by using the coloring shader to apply color while copying into place. This has all sorts of drawbacks like pixel alignment restrictions and needing a copy of every glyph at every size and shift you need to render in the frame in a texture atlas (which can be reused across frames). Just as an example, if you wanted to animate the size of some text, you’d have a pretty expensive operation every frame and still run into perceived jitter as you align to pixel boundaries each frame.

With Glyphy, we have a single version of the glyph for any size in the texture atlas, stored as encoded arc lists generated from SDF information (as opposed to uploading SDF information to the texture as some other implementations have done). This results in incredibly crisp rendering at any scale or offset (where as other implementations using SDF information in textures tend to create artifacts).

After just a few days of tinkering with it, it seems good enough to actually render Text Editor.

There is certainly more work to be done before this type of advancement can be enabled by default, but it was a fun prototype to be sure.

Modernizing GTK’s macOS backend (again)

Since the early days of working on the macOS backend for GTK 4 I knew eventually we’d have to follow suit with what the major browsers were doing in terms of drawing efficiency. Using OpenGL was (while deprecated, certainly not going anywhere) fine from a performance standpoint of rendering. But it did have a few drawbacks.

In particular, OpenGL (and Metal afaik) layers don’t have ways to damage specific regions of the GPU rendering. That means as we’d flip between front and back buffers, the compositor will re-composite the whole window. That’s rather expensive for areas that didn’t change, even when using a “scissor rect”.

If you’re willing to go through the effort of using IOSurface, there does exist another possibility. So this past week I read up on the APIs for CALayer and IOSurface and began strapping things together. As a life-long Linux user, I must say I’m not very impressed with the macOS experience as a user or application author, but hey, it’s a thing, and I guess it matters.

The IOSurface is like a CPU-fronted cache on a GPU texture. You can move the buffer between CPU and GPU (which is helpful for software rendering with cairo) or leave it pretty much just in the GPU (unless it gets paged out). It also has a nice property that you can bind it to an OpenGL texture using GL_TEXTURE_RECTANGLE. Once you have a texture, you can back GL_FRAMEBUFFER with it for your rendering.

That alone isn’t quite enough, you also need to be able to attach that content to a layer in your NSWindow. We have a base layer which hosts a bunch of tiles (each their own layer) whose layer.contents property can be mapped directly to an IOSurfaceRef, easy peasy.

With that in place, all of the transparent regions use tiles limited to the transparent areas only (which will incur alpha blending by the compositor). The rest of the area is broken up into tiles which are opaque and therefore do not require blending by the compositor and can be updated independently without damaging the rest of the window contents.

You can see the opaque regions by using “Quartz Debug” and turning on the “Show opaque regions” checkbox. Sadly, screen-capture doesn’t appear to grab the yellow highlights you get when you turn on the “Flash screen updates” checkbox, so you’ll have to imagine that.

Opaque regions displayed in green

This is what all the major browsers are doing on macOS, and now we are too.

This also managed to simplify a lot of code in the macOS backend, which is always appreciated.

GSignalGroup and GBindingGroup

Some of the first abstractions we made when creating GNOME Builder are now available to everyone in GObject!

In particular, writing Text Editors requires tracking lots of information and changes from various sources. Sometimes those changes come from 2nd-degree objects via the object you really care about.

For example, with a GtkTextView that might mean tracking changes to a GtkTextBuffer, GtkTextTagTable, or many other application-specific accessory objects through the form of signals and properties.

To make that easier, we now have GSignalGroup and GBindingGroup merged directly into GObject. At their core, they allow you to treat a series of signals or bindings as a group and connect/disconnect them as a group. Furthermore, you can use g_object_bind_property() to bind the target instance to the binding/signal group removing even more code from your application. More than likely you’ll be better about supporting how things can change out from under you too.

In the process of upstreaming these objects, we did a bit of hardening, API cleanup, and thread-safety guarantees.

Now, go and delete some code!

Creating GtkSourceView style schemes

GtkSourceView has the concept of “style schemes” which map language features (types, keywords, strings, etc) to various colors and font properties. Creating them can be a bit laborious even if you’re starting with a color palette prepared. The artistic process is iterative so reducing the time between iterations is paramount.

Furthermore, I have aphantasia which means I need to be able to see things visually because I lack the wiring to simply imagine it in my head.

With that in mind, I spent a little time over the holiday season creating a standalone application to create or modify modify style-schemes. It provides live preview and targets the new features in GtkSourceView 5.x. It’s not a circle app or anything yet, but you can always grab it from the CI pipeline artifacts which include a Flatpak bundle.

It is a simple application with humble requirements.

  • Edit attributes about a style scheme.
  • Manage the color palette and allow importing color palettes from external formats (such as GIMP color palettes).
  • Visually modify styles for global, common, and per-language override.

My hope is that this encourages people to create art, even if you have neurological wiring similar to me.

Text Editor Happenings

Text Editor has really been shaping up in the past couple weeks as we race towards getting things ready for GNOME 42.

Preferences Dialog

A screenshot of the preferences dialog We removed the preferences sidebar experiment because it was a bit clunky and none of the other core apps shared the design metaphor. Instead we’ve brought back a preferences dialog, albeit with an improved design. It builds on the previous GtkSourceStyleSchemePreview work but with a filtered set based on the current light/dark desktop setting.

Open Popover

A screenshot of the files popover The “Open” popover also got another round of design work based on using it for a while. The style is a bit slimmer and more to the point. It is also much more keyboard navigable now. It’s nice to be able to hit Ctrl+K, type a few characters, and either hit Return to open the first match or navigate through the list with arrows. Escape will return you back to the editor. Easy stuff.

Spaces Drawing

I added a GSetting for those that rely on visual spaces. Even though there is no UI for this at the moment you can still get what you want fairly easily using the gsettings command line tool.

flatpak run --command=gsettings org.gnome.TextEditor.Devel \
    set org.gnome.TextEditor draw-spaces \


If you’re feeling experimental you can even try my easter egg to test out the Vim emulation using GtkSourceVimIMContext. It probably will never be an advertised feature, but it’s there.

flatpak run --command=gsettings org.gnome.TextEditor.Devel \
    set org.gnome.TextEditor keybindings vim # or "default"


The most visual piece of work this week was in introducing recoloring support. It builds atop libadwaita and uses a CSS provider to override the colors in the theme. I expect there will be a recoloring API in the not-too distant future for libadwaita which will provide this for us.

When you select a style-scheme Text Editor will now use the colors defined in the scheme to alter how the entire application looks. For example, below are screenshots for a number of style-schemes both bundled with GtkSourceView and found on the internet.

A screenshot of a style scheme A screenshot of a style scheme
A screenshot of a style scheme A screenshot of a style scheme
A screenshot of a style scheme A screenshot of a style scheme
A screenshot of a style scheme A screenshot of a style scheme
A screenshot of a style scheme

A screenshot of the preferences And yes, the coloring is reflected throughout the entire interface.

As always, I tend to be slow to blog but quick to drivel over here.

GtkSourceView is getting a VIM emulator

One of the things we spent some time on early with Builder was creating built-in VIM emulation. Originally it was written in C, but then got converted to this nasty CSS for a couple of reasons. First, that keybindings in GTK 3 were largely done with CSS due to how we moved away from gtkrc in the 2.x to 3.x migration. And beyond that, CSS was the one way that’d let us share a lot of keybinding code between shortcut themes so we could support emacs, vim, sublime, etc.

One thing that didn’t work well was VIM’s “modes”. People often think of VIM as a single state (normal, insert, replace, visual, etc). In reality it’s a stack of them. Just look at the following state diagram if you’re going to actually to match things somewhat accurately. Doing this in a manner that would allow sharing between shortcut themes in Builder was simply beyond the amount of time we had to spend on it given all the other features that needed to land.

As we start to port Builder to GTK 4 this is one area that worried me the most. GTK 4 has improved so much in this regard, but we also have this giant legacy ball of tar. So recently I started looking at a new approach to providing VIM emulation in a way that would let us improve the quality and extend it’s reach.

The experiment I’ve come up with so far is to build upon GtkIMContext. This allows us to filter key presses and releases from the widget and do so in a way that allows layered input-methods (ibus, emoji, unicode, etc) to continue working.

One question that will always pop up is “why not use $some_form_of_vim” and bridge that. The answer is pretty simple, it’s a huge dependency to take on as a library. It also requires synchronizing text and across boundaries which may not even be capable in places where GtkSourceView is used. Beyond that there are a large number of impedance mismatches in how things work from a generic standpoint.

But fear not, we can actually do pretty good for the most commonly used things. We can probably even implement registers (some of it is there already) and marks for people that want it. Some day I imagine we’ll land code folding upstream, so even supporting that might be possible.

My goal is to get this into 5.4 so that we have it well tested by time Builder lands a GTK 4 port. The exposed API is very small and just the single object GtkSourceVimIMContext. By keeping the implementation private we allow ourselves quite a bit of flexibility to adapt how we implement things if and when that is necessary.

I haven’t decided yet if we would want to have a property like GtkSourceView:enable-vim to simply enable it. I’m hesitant to do that because supporting it correctly can still be somewhat involved. You need presentation of a command bar and active command state for example. To do that requires application coordination anyway, so perhaps requiring the application to add a GtkEventControllerKey with the GtkSourceVimIMContext is the better option.

If you’d like to help on any of this, feel free to send me MRs. Everyone has slightly different usage of VIM and there are lots of missing spots you can fill in.

git clone
cd gtksourceview
git checkout wip/chergert/vim
meson build
cd build
hack and repeat

As always, I usually leave screenshots and videos for people here if you can handle my other mindless dribble.