v0.11.0
The teksu! DSL reaches the code applications are actually made of: a call to
your own fn row(..) -> impl Widget is a child, where before only a stock
widget was. Alongside it the container API loses its twin methods, so a slot has
one name and that name takes a WidgetId or a widget.
Underneath, the GPU floor drops to what the renderer actually needs. A machine
with no Vulkan driver could not open a window at all; it now falls back to
OpenGL, which is what an older GPU has.
Added
teksu
- A lowercase identifier that continues into a call, a method chain or an index
at body position is a child:VStack { my_row(x) }and
VStack { row(1).spacing(4.0) }compile. A lowercase identifier standing
alone is still the argument-free property. - A keyword-rooted head is a child too:
self.row(x),Self::header(),
crate::ui::header(),super::row(). #{ expr }carries a widget value as well as aWidgetId, which is how a
Rust struct literal is passed at body position:#{ Card { title: t } }.
Widgets
on_changeonCheckbox,Toggle,RadioButtonandSlider, taking the
value the activation produced and anEventContext, so flipping one can send an intent, set the
theme or open a window — things a bareSignalwrite cannot do, because an
observer receives only&T. They fire for the pointer, forSpace, for an
assistive-technologyClick, and, for a checkbox inside a data-view row, for
Spaceon that row. They do not fire for programmatic writes to the bound
signal: there is no event in flight to carry, and the signal remains the
source of truth. A tristate checkbox reports abooltoo, since activation
cyclesChecked↔Uncheckedonly.RadioButtonreports only a real change,
so re-activating the selected button is silent.Sliderreports every value a
drag produces, but not a write that changes nothing (a drag past the end, a
snap onto the grid point already held); it has no commit-on-release callback,
so once-per-interaction work still belongs on the signal.StandardListItem::on_checkbox_toggleand theStandardTreeItemforwarder,
which make the embedded checkbox'son_changereachable — the canonical row
builds its own checkbox, so it was the one place the callback could not be
installed. To read check state,CheckedModelremains the answer: it is the
source of truth and it survives row recycling, which a per-row callback does
not.child_opton every container that haschild, 38 of them, up from 7. A
bareteksu!ifnow works inside a single-child wrapper, not only inside a
stack.- 45 accumulator builders gained the plural twin that a
forloop needs:
tabs,static_tabs,panes,items,actions,lines,rails,
docks,radios,full_width_rows,add_childrenand others.
Core
- Behaviour change. The row-activation marker a widget publishes with
BuildContext::set_keyboard_toggle, and the fallback passed to
EventContext::row_space_activate, are nowRc<dyn Fn(&mut EventContext)>
rather thanRc<dyn Fn()>.Spaceon a data view's focused row therefore
runs with a context, which is what lets a row checkbox fireon_changeon
that path instead of only under the pointer. Box<W>implementsWidgetfor anyW: Widget + ?Sized, so a boxed widget
goes wherever a widget goes and adds no arena node.HandlerSet::merge_undercomposes two handler sets, the later declaration
winning.
Documentation
- The
teksu!reference leads with the real widget catalog: worked examples for
ListView,TreeView,TableView,TabWidget,FormLayout,MenuList,
Toolbar,SwitcherandDockingLayout, and a section listing only what
genuinely does not work, with the compiler's own text. - The spec gains a Why there is no v4 section: what the DSL was measured to
cost, the four charges against it that measurement did not support, the three
silent-wrong-program bugs and the one grammar rule that did stand, and the two
exits that stay available if the question reopens. It replaces the standalone
decision note, which is removed. - The binding trap is written down in both documents: binding names are one flat
namespace per block, so two bindings sharing a name alias, both attach sites
resolving to the later widget while the earlier one is built and attached
nowhere. - A root
NOTICEfile records what the theme presets derive from and on what
terms: the WinUI theme-resource values under Microsoft's MIT licence, the
Material 3 baseline tokens from documentation Google publishes under CC-BY-4.0,
the macOS preset's mix of published and measured numbers, and every bundled
font with its licence, its copyright line, and whether it is embedded by
default. - The trademark policy gains a Third-party trademarks section: it names the
owners of macOS, Fluent, Material Design and Int UI, states that Teksilo is
neither affiliated with nor endorsed by any of them, and separates the
fluenttheme preset from Project Fluent, the unrelated localization system
behind Teksilo's translations. - The two bundled-font license files are corrected.
roboto-LICENSE.txtcarried
a summary of Apache-2.0 -- four of its nine clauses, paraphrased, with the
redistribution conditions collapsed to one line -- where section 4(a) requires
a copy; it now carries the complete text.noto-LICENSE.txtnamed one project
and one year for two faces; it now carries the copyright line each font's own
name table declares,notofonts/arabic2022 andnotofonts/hebrew2024.
Changed
- CI runs
cargo teksilo-fmt --checkovercratesandexamples. FormLayout::linestakesimpl IntoTeksiChildin both columns; the field
column used to requireimpl Widget, so a loop holding ids could not use it.teksu-language-spec-v3.mdis the design rationale and names
teksu-macro-reference.mdnormative for behaviour; fourteen divergences from
the implementation are corrected, and appendices A.2, A.3, A.4 and A.6 are
marked superseded where they describe an API that has since been removed.
Removed
Behaviour change, breaking.
-
StandardTreeItem::on_toggle/on_toggle_rcare renamed
on_chevron_toggle/on_chevron_toggle_rc. They are the expand / collapse
control, and the name became ambiguous the moment a row gained
on_checkbox_toggle. -
add_child,child_id,child_boxedand every*_idslot twin, 80 methods.
Call the slot by its own name instead:.child(id),.content(id),
.header(id),.pane(id). Every widget-accepting slot method takes
impl IntoTeksiChild, which is implemented forWidgetIdand for every
Widget. Not affected:shortcut_id,static_tab_with_id, the alternate
constructors (from_id,new_id,around_id,custom_id,
with_child_id), andBreadcrumb::item_id. -
WidgetBuilder::dim_when_inactiveanddim_when_inactive_default. Wrap
instead:DimWhenInactive::new().factor(f).child(w). -
Checkbox::labels_hidden(bool)is nowCheckbox::labelled_externally(), the
name and shapeTogglealready used for the same thing: the control's
accessible name comes from an ancestor, so it renders no label of its own. -
FormLayout::line_idsno longer adds one row from two ids; it takes an
iterator of(label_id, field_id)pairs and adds a row per pair. The
single-row form is gone becauselinetakesimpl IntoTeksiChildand accepts
aWidgetIddirectly.
Fixed
Platform
- Teksilo opens a window on a machine with no Vulkan driver. OpenGL is the
only backend such a machine has left — an older GPU, a VM whose guest driver
stops at GL — and it was never handed the platform's display connection, so it
could render offscreen but never present to a window. Startup died before the
first frame withincompatible_surface_backends: GL. Confirmed fixed on both
Wayland and X11 with the Vulkan driver removed. - Adapter selection is a search rather than a single request. An adapter that
enumerates but cannot open a device no longer ends the process, and an
explicit software fallback is tried before giving up — the resilience the
offscreen test device already had, and the window path did not. - wgpu's own environment variables take effect:
WGPU_BACKEND,
WGPU_POWER_PREFand the rest were silently ignored, leaving no way to move
off a backend whose driver is the problem. - The remaining "no GPU" failure names the backends tried, the errors each gave,
and what to install, in place of aDebug-printed wgpu struct.
Rendering
- Neither atlas asks for a texture the device cannot allocate. Both grow toward
a 4096-pixel ceiling that is this renderer's own, not a fact about the
hardware, and a downlevel device can sit below it; the path atlas now caps its
growth to what the device reports, and a glyph atlas that arrives oversized is
skipped rather than failing the frame.
Core
- Behaviour change. A builder method with no inherent twin on
WidgetWithHandlerswrapped an already-wrapped widget, and only the outer
handler set reached the node, so.on_tap(cb).clips_children_on(true)never
fired. Handler sets at any depth now arrive. - Chaining past
dim_when_inactiveretargeted the rest of the chain at the
wrapper, soVStack::new().dim_when_inactive(0.7).child(a).child(b)built
DimWhenInactive > band lost the stack anda. The method is gone;
teksilo-teksu-guardfails the build if aWidgetBuildermethod returns a
foreign wrapper again. - ArrowLeft reaches a text editor again when a second one is mounted beside
it. The dispatch root reads the inline-start arrow as "back toward the
parent overlay" once two or more non-host overlays are stacked, and it counted
every band alike. Each mountedRichTextEditorkeeps one full-viewport
affordance host in theTextAffordanceband for its selection handles, so two
editors on one page — a prose column and the synopsis next to it — read as a
submenu over its parent menu: the key tore down an affordance host and
returned, and no editor in that window saw an ArrowLeft for as long as both
were up. The count now considersOverlayBand::Standardoverlays only, the
same lineOverlayBand::dismissed_by_outside_pressalready draws for presses.
A submenu over a mounted affordance still closes on the back key.
teksu
teksilo-fmtre-indented a multi-line expression child by the block's own
indent on every run, walking a multi-line argument list further right each
time.
Full Changelog: v0.10.0...v0.11.0