Newsletter, March 2022
Complex script support

core gio

Elias spent the last month reviewing my text work, improving widget focus traversal, and improving many small details about core. In particular, there is now a nix-based development environment for gio available in core’s flake.nix file. For details on nix, see:

I revised my text patches, and they were accepted! Gio can now render complex scripts, and can properly handle right-to-left text.

For the time being, applications seeking to leverage the new text shaping should do the following:

  • use fonts supporting the languages they want to display.
  • populate layout.Context.Locale with the information for the language in use.

I hope to automate font selection and populating the locale in future work, but those steps are manual for the time being.

There were 49 commits to core since the last newsletter.

Breaking API changes by author:

Elias Naur:

  • io/router: [API] don’t emit Enter and Leave events for touch input. Enter/Leave events make sense for mouse pointers, to track hover status. It doesn’t make sense to track hover for touch input, so this change stops pointer.Enter and pointer.Leave from being emitted for pointer.Touch sources. (cd0c9dab)

Chris Waldon:

  • deps,text,widget,font/opentype: [API] add harfbuzz-powered text shaper. This commit introduces a new text shaping infrastructure powered by Benoit Kugler’s Go source-port of harfbuzz. This shaper can properly display complex scripts and RTL text. This commit changes the signature of the text.Shaper function, which is a breaking API change. (1e5a3696)
  • widget{,/material}: [API] update editor to support complex scripts. This commit updates material.Editor and material.Label to support the new text shaper. This requires breaking their assumption that glyphs of font data map 1:1 to runes of text data. (42c99a5c)
  • font/opentype: [API] replace old font type with harfbuzz. This commit replaces the previous opentype.Font with an implementation that uses the new text shaper. In order to keep the implementation simple, support for opentype font collections was dropped. It should be possible to re-add this support after some changes to the text shaper’s line wrapping algorithm. (01276238)
  • text: [API] remove Text and Advances from Layout. These fields are no longer needed with the new text shaper. Advances is redundant to the glyph information, and Text should never be used during layout, as you should traverse the cluster list instead. This commit also removed the now-unused string field from the path LRU cache key. (9576b659)
  • font/gofont: [API] use new opentype impl for Collection(). This commit switches gofont.Collection from returning a collection of fonts using the old text shaper to using the new harfbuzz-based shaper. The underlying type of gofont.Collection() has changed, which may break users who dug into the font data. (e14bbee2)
  • widget: [API] make text.Alignment direction-sensitive. This commit ensures that text.Alignment is intuitive for the direction of the text being aligned. RTL text with Alignment Start will be aligned to the right edge of the area, whereas LTR text with Alignment Start will continue to be aligned to the left edge. Vice versa for the End alignment. (7daab97f)

Non-breaking changes by author:

Elias Naur:

  • app: [macOS] ensure only one redraw request is in flight at any time. After 34f10d9cbb3a45da2ecb8a9fab60b0a5995c1a31, the display link callback will never block. However, if the main thread is blocked for another reason, say a bug in the user program, callback requests will pile up as blocked goroutines. (083d407b)
  • .builds: disable Vulkan on FreeBSD. (317635b1)
  • gpu,app: don’t call time.Now when not profiling. runtime.nanotime1 shows up in profiles on Android, so avoid calling time.Now when we can. (f19b16fe)
  • app: [Android] re-introduce Choreographer for frame pacing. According to #375, change b86928ceecf6800069cfd5a92e5b6f2216367fe5 increased frame pacing jitter. This change effectively reverts it by re-introducing Choreographer for pacing. (b48b1270)
  • gpu/internal/vulkan: resize descriptor set pool correctly. Before this change, the resized descriptor set pool would never increase, defeating the purpose of re-using pools. (c244b7c3)
  • gpu/internal/vulkan: [Vulkan] replace Device/QueueWaitIdle with fences. vkDeviceWaitIdle and vkQueueWaitIdle are expensive; a vkFence is cheaper and the usual way to ensure a previous frame has completed before starting another. (e8aa881d)
  • gpu/internal/vulkan,internal/vk: allocate descriptor sets in batches. We know exactly the configuration and number of sets in each pool, so we may as well allocate them all up front. (7785310e)
  • gpu: minimize FBO resizes in current renderer. References: #375 (eeb2febf)
  • cmd/gogio: [Android] export GioActivity to please Android 32 and later. Fixes: #378 (99f6224e)
  • .builds: use Android SDK for 32-bit build test. With GOARCH=386, we can’t readily build packages that use Cgo. However, we already have the Android SDK available, so use that to test for 32-bit issues such as #384. (4a061a7d)
  • flake.*: add Nix development environment. This change adds a Nix flake capable of setting up an environment for building Gio programs for Linux and Android, on top of the platforms that only needs Go (Windows, WASM). (a3f14754)
  • cmd: update Gio version. (75d487fa)
  • flake.nix: remove emulator from environment. The emulator needs android system images to run, each of which takes up a lot of space. Remove emulator from the Nix flake environment and let something else provide emulator support, maybe even a custom Nix devShell. (b6ee0264)
  • flake.nix: enable Vulkan support. (01757791)
  • io/router,app: move Tab-to-focus conversion to app.Window. This is a refactor to make it easier to add higher level logic to focus moves. A follow-up will add automatic scrolling to bring focused widgets into view. (920e6dd0)
  • f32,gpu,op/clip: add f32.Rectangle method for converting to image.Rectangle. Creating an image.Rectangle from a f32.Rectangle is used by two packages in Gio and about to be used for a third. Add a Round method to f32.Rectangle to avoid duplicating the implementation. (1f11a5a1)
  • io/router: use integer coordinates for bounds. There is no need for floating point coordinates, except for transforming bounds and hit testing. (e72c46f1)
  • widget: include the Editor key handler in the editor clip area. A meaningful clip area for a key handler will matter when we start auto-scrolling to move focused handlers into view. (b2d10c2f)
  • io/router: add ScrollGesture. (6389b1a3)
  • gesture: don’t rely on Enter events to determine validity of click. We’re about to not emit Enter and Leave events for touch input, and this change changes the Click gesture to no longer rely on those events to determine whether a Release is inside its bounds. (2069d5cb)
  • app,io/router: scroll focused widgets into view. A focused widget may be partially or completely off-screen in which case the user will have difficulty interacting with it. This change attempts to scroll the focused widget into view by issuing synthetic scroll events. (4326fee7)
  • io/router: use areas to determine targets for synthetic clicks. Before this change, semantic clicks would be delivered according to the center of the targeted widget, which could result in a different widget receiving the click. Or in worst case, no widget in case the center is not visible because of clipping. (e8603ba5)
  • io/router: merge pointerQueue.deliverScrollEvents and deliverEvent. (d34544cc)
  • layout: default List scroll bounds to infinity. Before, List would only report the remaining scrollable area of the visible children when positioned close to either end. Now, List always report infinite scroll bounds unless it is positioned at an extremum. (a699fb89)
  • layout: layout one invisible child at each end of a List. A recent change added automatic scrolling to move focused widgets into view. This change modifies List to layout an extra child at each of its ends, to enable focus to move to them and trigger automatic scrolling of the list. (508330e8)
  • layout: compute Position.Offset correctly for ScrollToEnd Lists. (afd39a6b)
  • io/router,app: scroll a bit when reaching the end in a focus direction. List was recently changed to include an extra child at each end, to automatically scroll when reaching the end of a focus direction. However, if List includes unfocusable children that strategy may fail. This change adds another fallback where app.Window will scroll a constant amount in the focus direction, to reveal more children. (a1b5ff05)
  • layout: don’t clip List children. Clipping all children once to the entire List area is enough. The change was motivated by #389 where individual child clips would make it harder for focus scroll heuristics to work. (36919ef7)
  • io/router: account for parent clip areas when scrolling focus into view. Fixes: #389 (bd7f5043)
  • app: clip client area. On Wayland, app.Window provides fallback window decorations but clients are not prohibited from drawing over the decorations or capturing events. This change adds a clip operation to ensure no unwanted interaction between client content and decorations. (f0753733)
  • io/router: don’t panic on focus moves when there is nothing to focus. (69f982e2)

Chris Waldon:

  • widget/material: make clickable respect constraints. This change makes material.Clickable propagate the constraints it is invoked with to the widget being made clickable. Without this, the internal use of layout.Stack resets the minimum constraints to zero. This has the confusing effect of breaking a working layout when you decide to wrap one element in a Clickable, which I think is sufficiently surprising that we should eliminate the footgun. (cf787a1a)
  • widget: optimize painting editor selection. This commit introduces logic to skip painting the selection rectangle on lines prior to the line containing the beginning of the selection. (1ad78565)
  • widget: remove unneeded editor flicker logic. We cannot find a way to trigger this flickering condition anymore, and so we’re removing the logic guarding against it. (b0ab5ae0)
  • system: define new Locale type. This commit adds a Locale struct that captures language and layout flow direction for the system. This information can be leveraged by text shaping and layout code to make better choices. (512900c9)
  • layout: add Locale to Context. This commit adds a system.Locale to the layout.Context, providing an easy means to plumb language information throughout an application. (db82d123)
  • font/gofont: add font collection using the new shaper. This commit adds a font collection that uses the new text shaper so that constructing material.Themes atop it is equally simple to using the old shaper. (938179d2)
  • widget: drop debug prints from tests. This commit removes some lingering editor debug prints from the test code. (8833a673)
  • ci: test non-cgo packages in 32-bit mode. This commit runs Gio’s test harness in 32-bit mode as well as 64-bit. This helps catch bugs in Gio and its dependencies where integer overflow causes build or runtime problems. (3fb522ca)
  • deps,font/opentype: update dependencies to fix 32-bit build. This commit updates to a newer version of textlayout and switches to a fork of the UAX library that builds properly on 32-bit machines. This should fix 32-bit Gio compilation for the time being. I hope to switch back to npillmayer’s UAX as soon as he has time to review the pending pull requests. (3406a6da)


  • gpu: prevent texture to be larger than MaxTextureSize. Change eeb2febfea01cda47bd46e76f08b3f80347fce46 added extra space to FBO sizes to avoid re-creating them often. However, the size could end up higher than the GPU supports. This change caps the size. (eec78223)

Egon Elbre:

  • internal/stroke: optimize arc drawing. Arc with a small angle doesn’t need many segments. (3fd23136)


There have been 1 commits to gio-x since the last newsletter.

Non-breaking changes:

Chris Waldon:

  • deps,richtext: update gio and adapt richtext to new shaper. This commit tweaks the richtext package to match the new text shaper API in Gio core. (bc7801f)


A couple of small changes went into the examples repository this month:

Elias Naur:

  • go.*: bump Gio version. (bc47932)

Chris Waldon:

  • deps: update gio and gio-x. These updates pick up support for RTL languages and complex scripts. (b6b2f1a)

Elias Naur:

  • cmd/giouiorg: return 404 for unknown vainity import paths. (437538b)
  • content/doc/install: add Nix setup instructions. (65d74fc)