Cross-Widget Transitions

You can do some pretty flashy things with Dazzle. Tonight I added the missing pieces to be able to make widgets look like they transition between parents.

The following is a video of moving a GtkTextView from one GtkStack to another and flying between those containers. Compare this to GtkStack transitions that can only transition between their children, not their ancestors. Fun!

Dazzle spotlight – Multi Paned and Action Muxing

There really is a lot of code in libdazzle already. So occasionally it makes sense to spotlight some things you might be interested in using.

Action Muxing

Today, while working on various Builder redesigns, I got frustrated with a common downfall of GAction as used in Gtk applications. Say you have a UI split up into two sections: A Header Bar and Content Area. Very typical of a window. But it’s also very typical of an editor where you have the code editor below widgetry describing and performing actions on that view.

The way the GtkActionMuxer works is by following the widget hierarchy to resolve GActions. Since the HeaderBar is a sibling to the content area (and not a direct ancestor) you cannot activate those actions. It would be nice for the muxer to gain more complex support, but until then… Dazzle.

It does what most of us do already, copy the action groups between widgets so they can be activated.

// my_countainer should be either your headerbar or parent of it
// my_view is your view containing the attached action groups
// the mux key is used to remove old action groups
dzl_gtk_widget_mux_action_groups (my_container,
                                  my_view,
                                  "a mux key");

Exciting, I know.

Multi Paned

The number of times I’ve been asked if Gtk has a GtkPaned that can have multiple handles is pretty high. So last year, while working on Builder’s panel engine, I made one. It also gained some new features this week as part of our Builder redesign.

It knows how to handle minimum sizes, natural sizes, expanding, dragging, and lots of other tricky bits. There are lots of really annoying bits about GtkPaned from it’s early days that make it a pain when dealing with window resizes. MultiPaned tries to address many of those and do the right thing by default.

It should be strictly a step up from using GtkPaned of GtkPaned of GtkPaned.

Hello world in a handful of languages

The other day, Sri asked me for a quick Gtk example for a talk. Basically he just needed a window with a header bar. So I put together this repo on github with some examples in a variety of languages. After a few contributions from our community, we have even more examples.

I think it would be neat if people submitted pull requests for more languages. Take a peek and see if something you know about is missing. Ideally the example should use GtkApplication, Gtk templates (and resources if possible), and a window with a header bar.

Having these available will make it easier for me to make templates in Builder for more exotic languages.

Library Documentation

Now that gtk-doc is getting updates and integration with Meson, I decided to give it another shot. So here is some work-in-progress documentation for libdazzle, template-glib, and jsonrpc-glib.

There is plenty of work to do, but this makes it easier for others to come along and help me do the hard part, the documentation.

I’m sort of impressed, in hindsight, at the staggering amount of things we’ve built around Builder in the last couple of years.

This week in Builder

Given the success of this pattern of project-related updates in Gtk, maybe we can try to do this for Builder too. We’ve had lots of updates this past week in Builder and related projects.

  • Builder has switched to meson for building Builder. Autotools has been removed. This does NOT affect build systems supported by Builder.
  • Non-builder related widgets and utilities have moved into libdazzle. Roughly 50k fewer lines of code in Builder.
  • template-glib and jsonrpc-glib are now external projects. Our meson-based build system will automatically install them if necessary.
  • libdazzle’s DzlApplication now handles theme loading, menu merging, and icon loading. It also simplifies widget theming for non-Adwaita themes.
  • Builder’s panel engine was revamped and moved to libdazzle. Various CSS fallbacks were added, including Arc theme.
  • New releases (3.25.2) of gnome-builder, template-glib, jsonrpc-glib, and libdazzle.
  • Builder has switched to DzlSuggestionEntry for search. This design is not final by any means, so I expect further changes.
  • Shortcut engine development continues.
  • libdazzle’s fuzzy search has been vastly improved, while keeping overhead low.
  • Lots of widgets have been moved into libdazzle.
    • DzlThreeGrid — This is a three-column layout (with centered column) and rows.
    • Preferences ­— Copy the Builder style preferences in your app
    • Menu merging — Automatic menus.ui merging and management, handy for plugins.
    • StackList — Make your list boxes fly around
    • CPU graphs
    • Panels
    • MultiPaned — No more nested GtkPaned, deals with resizing, expanding, min and natural sizes appropriately.
    • RadioBox — Fancy joined buttons that act like radio boxes. Easily extendable for dynamic content.
    • Empty State ­— Your standard empty state helper
    • Progress Buttons — Both GNOME Software and Epiphany style animated download buttons.
    • Chromium and Firefox web-browser style auto-completion.
    • Lazy Tree Builders — These really simplify building large tree views with dynamic content.
    • Lots of utilities, data structures, and miscellaneous library glue.
  • A whole bunch of tests have been added to libdazzle. More are always needed.

If you’d like to help with the development of Builder or any of it’s associated libraries, come join us in #gnome-builder on irc.gnome.org.

Razzle Dazzle

I spend a lot of time making flashy widgets for Gtk. Mostly because I find it entertaining. You can now benefit from the culmination of many years of my trial and error in a new library I’ve created, libdazzle. The code is currently on github but I suspect I’ll move it to git.gnome.org relatively soon.

Why another library? Because I wanted to. But also, because managing all of this custom widgetry in Builder is starting to get out of hand and I’d really like others to be able to use the code too. Every time I go work on another hobby project I get annoyed that I have to copy and paste a bunch of code over.

This library is truly the culmination of many years of my time writing flashy Gtk+ widgets and the surrounding plumbing. You’ll find everything from an animation framework, menu merging, tree helpers, missing data structures, caching, signal and property management, a panel engine, layered gsettings, theme management, state machines, keyboard shortcuts (including chords) and more.

It comes in at nearly 50,000 lines of C. You can imagine why I’m tired of the copy pasta.

The project builds with Meson, so it should be easy to use as a sub-project if you don’t want to rely on a system installed version. It has support for GObject Introspection and can also generate a .vapi for Vala if that’s your thing.

There is no documentation yet, but I’m slowly getting more and more tests added which can serve as examples in the mean time. Go read some code, it’s good for you.

Here are a couple teasers

3.26 Developments

My approach to development can often differ from my peers. I prefer to spend the early phase of a cycle doing lots of prototypes of various features we plan to implement. That allows me to have the confidence necessary to know early in the cycle what I can finish and where to ask for help.

We have some big stuff coming this cycle.

Panel Engine Revamp

Allan has been working on some major design work in how our panels and documents work. This has been needed for some time and things are looking good. To keep up with this, I’ve been doing some major improvements to panel-gtk, our panel engine. I managed to shake out a few bugs in the process and those fixes have made their way into the gnome-builder-3-24 branch.

The test program is not much to look at, but we have some necessary plumbing in place to do new things.

Shortcut Engine and Key Themes

Furthermore, I’ve been building a new shortcut engine to do the more advanced features we need. Gtk Shortcut Engine (GSE) provides plumbing for applications that need complex features such as multi-key “chords”, keyboard themes, and custom overrides by users. Many of you have asked for this in Builder, and I’m confident in saying it is coming for 3.26. You can find the work in progress in the shortcut-engine repository.

Ultimately I had to import a copy of the upstream’d GtkShortcutsWindow (based on what we wrote for Builder in 3.20) so that we could support chords. So the code-base looks bigger than it really is. The primary design (besides the keyboard themes) is the concept of a GseShortcutController and GseShortcutContext. These two things allow us to do some fun stuff like emacs-style “minor modes” as well as Vim-style modal keybindings.

I expect this to allow us to cleanup our Vim emulation quite a bit. It also solves some of our outstanding problems with keyboard shortcuts and unpredictable GAction activation. It’s really quite fundamental to how you’ll be interacting with Builder from a keyboard going forward.

Debugger

The big feature for 3.26 is the debugger. I have enough of a working prototype in place to have a reasonably good idea of what the moving parts are. As soon as we land the new shortcut engine and some of the panel updates I’ll be back finishing up that feature.

That’s it for now!

Accelerators, a history lesson

It’s good to dive into our shared history every now and again to learn something new. We want to build a customized shortcut engine for Builder and that means we need to have a solid understanding of all the ways to activate shortcuts in GTK+. So the following is a list of what I’ve found, as of GTK+ 3.22. I’ve included some pros and cons of each based on my experience using them in particularly large applications.

GtkAccelGroup and GtkAccelMap

The GtkAccelGroup class is remnant from the days before GtkUIManager was deprecated. It is a structure that is attached to a top-level GtkWindow and maps “paths” to closures. It’s purpose was to be used with a GtkAccelMap to ultimately bind accelerators (such as “<control>q”) to a closure (such as gtk_main_quit).

The GtkAccelMap is a singleton that maps accelerators to paths. When activating a keybinding, the accelerator machinery will try to match the path with one registered in the GtkAccelGroup on the window.

Pros

  • Widgets can be activated no matter what the focus widget. However, the window must be focused.
  • It is simple to display keyboard accelerators next to menu items.
  • Creating an accelerator editor is straight forward. Lookup accel path, map to accelerator.

Cons

  • Not deprecated, but much of the related machinery is deprecated.
  • Fairly complex API usage. It doesn’t seem to be designed to be used by application developers.

Signal Actions

Signal actions are GObject signals that contain the G_SIGNAL_ACTION bit set. By marking this bit, we allow the GTK+ machinery to map an accelerator to a particular signal on the GtkWidget (or any ancestor widget in the hierarchy if it matches).

In the GTK+ 2.x days, we would control these keybindings using a gtkrc file. But today, we use GTK+ CSS to create binding sets and attach them to widgets using selectors. This is how the VIM implementation in Builder is done.

It should be noted that signal actions (and binding them from gtkrc/css) were meant to provide a way to provide key themes to GTK+ such as an emacs mode. It was not meant to be the generic way that applications should use to activate keybindings.

Pros

  • You can use CSS selectors to apply binding sets (groups of accelerators) to widgets.
  • Accelerators can include parameters to signals, meaning you can be quite flexible in what gets activated. With enough bong hits you’ll eventually land at vim.css.

Cons

  • Only the highest-priority CSS selector can attach binding sets to the widget. So the highest-priority needs to know about all binding sets that are necessary. That reduces the effectiveness of this as a generic solution.
  • This relies on the focus chain for activation. That means you can’t have a signal activate if another widget focused such as a something in the header bar.
  • It feels like a layer violation. I could make arguments on both side of the fence for this, which is sort of indicative of the point.
  • We have to “unbind” various default accelerators on widgets that collide with our key themes. This means that “key themes” are not only additive, but destructive as well.

GAction

Years back, we wanted the ability to activate actions inside of a program in a structured way. This allowed for external menu items as well as simplifying the process of single-instance applications. This was integrated into the GtkWidget hierarchy as well. Using gtk_application_set_accels_for_action() you can map an accelerator to a GAction. Using the current widget focus, the GTK+ machinery will work its way up the widget hierarchy until it finds a matching action. For example, if a widget has a “foo” GActionGroup associated with it, and that group contains an action named “bar”, then mapping “<control>b” to “foo.bar” would activate that action.

Pros

  • Actions can have state, meaning that we can attach them to things like toggle buttons.
  • Application level actions can be activated no matter what window is focused.
  • It’s fairly easy to reason about what actions are discoverable and activatable from a node in the widget tree. Especially with GTK+ Inspector integration.
  • Theoretically we could rebuild a11y on top of GAction and fix a lot of synchronous IPC to be async similar to how GMenu integrates with external processes. This would require a lot of external collaboration on various stacks though, so it isn’t an immediate “Pro”.

Cons

  • Action activation requires the focus hierarchy to activate the action. This means to get something activatable from the entire window we need to attach the actions to the top-level. This can be a bit inconvenient, especially in multi-document scenarios which do not share widgetry.
  • Actions are rather verbose to implement. Doing them cleanly means implementing proper API to perform the action, and then wrapping those using GActionEntry or similar. Additionally, to propagate state, the base API needs to know about the action so it can update action state. Coming up with a strategy for who is the “owner” of state is challenging for newcomers.
  • The way to attach accelerators is via gtk_application_set_accels_for_action() which is a bit weird for widgets that would want to provide accelerators. It means the applications are given more freedom to control how accelerators work at the cost of having to setup a lot of their own mechanics for activation.

What do we need in Builder?

Builder certainly lies on the “more complex” side of things in terms of what is needed from the toolkit for accelerators. Let’s go over a few of those necessities to help create a wishlist for whatever code gets written.

VIM and Emacs Keybindings

These will never be 100% perfect, but we need reasonable implementations that allow Emacs and VIM users not scream at their computer while trying to switch to a new tool. That means we need to hit the 80% really well, even if that last 20% is diminishing returns. Today, we’ve found a way to mostly do this using G_SIGNAL_ACTION and a lot of custom GTK+ CSS. The con mentioned above of GTK+ CSS requiring that the -gtk-key-bindings: CSS property know about all binding sets that need to be applied from the selector make it unrealistic for us to keep pushing this further.

Custom Accelerators

We want the user to be able to start from a basic “key theme” such as Gedit, VIM, or Emacs and apply their own overrides. This means that our keybinding registration cannot be static, but flexible to account for changes while the application is running. We also need to know when there are collisions with other accelerators so that we can let the user know what they are doing may have side-effects.

GtkShortcutsWindow Integration

In Builder we do not account for the “key theme” in the shortcuts window. This can be confusing to users of Emacs and Vim mode as what they see may not actually activate the desired action. We would like a way to map the accelerators available (grouped and with proper sections) to be automatically reflected in our GtkShortcutsWindow.

Elevating Accelerator Visibility

The shortcuts window is nice, but making the accelerators available to the user while they are exploring the interface is far more beneficial (based on my experiences). So whatever we end up implementing needs to make it fairly trivial to display accelerators in the UI. Today, we do that manually by hard-coding these accelerators into the GtkBuilder UI files. That is less than ideal.

Window-wide Activation

If I’m focused on the source code editor, I may want to activate an accelerator from a panel that is visible. For example, <control><shift>F to activate the documentation search.

Using actions to do this today would require that all panel plugins register their actions on the top-level as well as keybindings using gtk_application_set_accels_for_action(). While this is acceptable for the underlying machinery, it’s almost certainly not the API we want to expose to plugin developers in Builder. It’s tedious and prone to breakage when multiple “key themes” are in play. So we need an abstraction layer here that can use the appropriate strategy for what the plugin has in mind and also allows for different accelerators based on the “key theme”.

Re-usability

There are other large applications in the GNOME eco-system that I care about. GIMP is one of those projects that has a hard time moving to GTK+ 3.x due to a few reasons, some of which are out of our control upstream. The sheer size of the application makes it difficult. Graphical tools often are full of custom widgetry which necessarily dives into implementation details. Another reason is how important accelerators are to an immersive, creativity-based application. I would like whatever we create to be useful to other projects (after a few API iterations). GIMP’s GTK+ 3 port is one obvious possibility.

Builder Happenings

Over the last couple of weeks I’ve started implementing Run support for Builder. This is somewhat tricky business since we care about complicated manners. Everything from autotools support to profiler/debugger integration to flatpak and jhbuild runtime support. Each of these complicates and contorts the problem in various directions.

Discovering the “target binary” in your project via autotools is no easy task. We have a few clever tricks but more are needed. One thing we don’t support yet is discovering bin_SCRIPTS. So launching targets like python or gjs applications is not ready yet. Once we discover .desktop files that should start working. Pretty much any other build system would be easier to implement this.

So much work behind the scenes for a cute little button.

Screenshot from 2016-07-17 22-09-21

While running, you can click stop to kill the spawned application’s process group.

Screenshot from 2016-07-17 22-10-38

But that is not all that is going on. Matthew Leeds has been working on search and replace recently (as well as a whole bunch of paper cuts). That work has landed and it is looking really good!

Screenshot from 2016-07-17 22-12-45

Also exciting is that thanks to Sebastien Lafargue we have a fancy new color picker that integrated with Builder’s source editor. You can visualize colors in your source files and change them using the dropper or numerous sliders based on how you’d like to tweak the color.

Screenshot from 2016-07-17 22-11-38

I still have a bunch of things I want to land before GUADEC, so back to the hacker den for me.