
6 people contributed this month with 106 commits.
Huge thanks to Paul Hatchman who contributed the new “Widgetpedia”!
This companion to the demo showcases all the builtin widgets and lets you play with the various options for each widget.
A great way to search and understand the widgets available out of the box.
Try it at the top of the demo window!
Big thanks to Elaine Gibson who contributed an entire new wio backend!
“wio” is a new platform abstraction library, and the backend currently uses an OpenGL renderer.
Check out the new zig build wio-app!
Michael Belousov contributed a build-time utility to convert svg icons to tvg at build time. See build.zig svgPathToTvgPath and the new build command svg2tvg. Thank you!
The tab index of a widget controls its keyboard navigation focus order. These used to be global inside each subwindow.
TabIndexGroup (dvui.tabIndexGroup) now provides a scope for tab index values:
This provides a way to rearrange the tab index order of widgets locally without needing to know or change indexes globally.
It also gives a mechanism for a compound widget function to accept a single tab index value but internally have a complex ordering.
This new widget can help performance in places where widgets are off-screen (like in a scroll area), but need to be run for sizing.
CacheSizeWidget (or dvui.cacheSize()) will maintain its size but you can call uncached() to know if you need to run the children. This is now used in the demo “Scrolling” section.
New utility for overlapping widgets to change the rendering order.
The problem is the first widget processes events first, but will be painted over by later widgets. This results in events going to the widget that is visually behind other widgets.
RenderFrontToBack is a new helper that collects rendering and defers it so that if all the overlapping widgets use it, the first widget to run will be painted last. Events will now be handled by the widget visually in the front.
It’s used in the demo “Scroll Canvas” section.
Thank you to everyone who asked questions, filed issues, and contributed!
BoxWidget (dvui.box) is used to layout a single line of widgets vertically or horizontally. It is also used for general spacing/positioning.
Children with normal layout are split into 2 categories:
A child with gravity (in the box direction) of 0 or 1 is “packed”. All packed children are lined up in the box direction.
The box’s min size is the sum of the packed min sizes in the box direction.
If the box has extra space, it is allocated equally among all packed children that are expanded in the box direction. Setting .equal_space = true means the total box space is divided among all packed children.
Tip: use gravity of 1 to pack the box “at the far end”. This is a good way to swap the order of widgets without having to change the execution order.
A child with gravity (in the box direction) between 0 and 1 is “unpacked”. These are positioned within box’s contentRect without regard to any other children in the box.
This is useful to position a single child inside a larger box.
Boxes rarely have both packed and unpacked children.
Layout of dvui widgets comes in 3 flavors.
This is the most common. The parent uses the child’s Options:
And gives the child a Rect inside the parent’s contentRect. The Rect is entirely up to the parent widget. See Box Layout.
A widget’s Rect includes its content rect, padding, border, and margin.
Tip: negative margins can be used to overlap sibling widgets, usually to “collapse” a duplicated border.
Advanced Tip: a child can adjust its Rect by calling .data().rectSet(). This is how the wiggle button works, and is invisible to the parent.
A non-expanded parent is always tightly packed around its normal children (using their min sizes). Options.max_size_content clamps how big this packing can get. Especially useful for constraining textLayout, textEntry, and flexBox.
Tip: textLayout, textEntry, and flexBox adjust their height according to their width. Min width is calculated assuming no line breaking. Min height is calculated based on the current width.
Note that a widget can expand past max_size_content. See min_size_content is not the minimum
Tip: when drawing a dynamic element (like a progress bar), don’t size the element with .min_size_content, because that can affect its parent’s min size. Instead size a container, and directly draw the dynamic element (using Rect.fill or Path.stroke).
.rect in OptionsThis is used for situations like the “scroll canvas” demo, where children know their own position. It’s also used in infinite scrolling situations like the icon browser.
Children that pass .rect are invisible to their parent, but still use the parent’s contentRect as coordinates.
If expanded, the child’s Rect size in the expanded direction will be the parent’s contentRect size.
If the .rect passed has zero width/height, it will use the child’s min width/height.
Advanced Tip: if you have overlapping widgets (like in the scroll canvas demo) use dvui.RenderFrontToBack so events will be processed by the widget visually on top.
Floating...WidgetThe floating widgets (FloatingMenuWidget, FloatingTooltipWidget, FloatingWidget, and FloatingWindowWidget) use strategy 2 and pass .rect to opt out of normal layout.
In addition they create new subwindows, which operate as separate layers:
Use one of the floating widgets to show content that logically pops above and separate.
Examples include:
9 people contributed this month with 111 commits.
Huge thanks to Jakob Ingvast who contributed an entire new GLFW OpenGL backend!
glfw-opengl-app and glfw-opengl-ontopPaul Hatchman contributed a new GroupBox widget (“fieldset” in web speak). It combines a title with an outline around a group of widgets. Thank you!
See it in the demo > “Basic Widgets” > “Slider examples in a group box”.
Font.size is now the height of “M” in logical pixels. This gives us more consistent sizing between fonts with different ascent/descent values. It also brings dvui more in line with other software.
In particular, the Noto fonts have larger ascent values than other fonts. Also new, setting Font.line_height_factor to a value less than 1 will remove space from a font’s ascent. This is useful to visually center Noto font labels in buttons.
Some texture target improvements:
dvui.Texture.Target.clear() clears to fully transparentdvui.Texture.Target.destroyLater() destroys at end of framedvui.Texture.fromTargetTemp() gets a temporary (dvui-owned) texture from a target that can be drawnThese give more control to applications that want to reuse a target texture.
Most backends treat textures and target textures fairly interchangeably, but not all. To deal with this, we are trying out having two functions to get a normal texture from a target:
.fromTarget() destroys the target texture.fromTargetTemp() leaves the target texture aloneThank you to everyone who asked questions, filed issues, and contributed!
3 people contributed this month with 65 commits.
Giant thanks to Paul Hatchman for adding text run support to our Accesskit integration!
Screen readers can now navigate normally through textLayout and textEntry content.
This is the last large piece needed for decent screen reader support. Wohoo!
While we will continue work in this area for bugs and enhancements, this is a huge milestone.
Big thanks to Arnold Loubriat for helping us use Accesskit properly and for loads of testing!
Shout out to Ethin Probst for excellent reporting and testing!
dvui implements a form of garbage collection for textures and data. If you don’t reference a texture or call dataGet/Set for a frame, we free that memory. But sometimes you want to keep things around a bit longer.
Now we provide functions textureRetain, dataRetain, and retainClear.
These give a way to keep dvui from freeing these things, while associating them with a “retain key”. That key can be passed later to retainClear to tell dvui to stop retaining them.
This was motivated by the demo’s stuttering when reshowing the demo buttons (which trigger regenerating a bunch of textures). It’s now fixed by:
dataSetDeinitFunction which calls retainClearSo the flow is:
These features all come together to allow retaining data to match the lifetime of some higher level widget.
autoPosition() to center (after a resize)Thank you to everyone who asked questions, filed issues, and contributed!
6 people contributed this month with 56 commits.
Font selection got a major upgrade. Until now we had to refer to an individual font ttf.
Now Font acts like a query that searches for a matching Font.Source:
.font = .theme(.heading).font = Font.theme(.heading).larger(2).font = dvui.themeGet().font_mono.withWeight(.bold).withStyle(.italic)dvui.Theme has 4 fonts (body, heading, title, mono) and grew an embedded_fonts field so that font sources can be tied to the theme that requires them.
For now we still only have a single pool of Font.Source that is internal to dvui. In the future we hope to extend this to be able to query for system fonts.
We are now bundling the tree-sitter library, and TextEntryWidget gained a tree_sitter option.
The usage code is responsible for providing:
dvui.OptionsRight now an example is only in the main window of the “app” example (like zig build sdl3-app), but eventually we will figure out a way to integrate it into the demo window.
This integrates with .cache_layout = true to provide incremental edit parse updates and good performance on large documents.
Any feedback would be welcome!
Thank you to everyone who asked questions, filed issues, and contributed!