Tweaks
Sometimes the widget Day gives you is exactly right except for one platform-specific detail: you want the standard button, but with AppKit’s toolbar bezel; the standard slider, but with XAML’s tick marks. Writing a whole custom piece for two method calls is disproportionate, so Day has tweaks: a supported way to reach the real native widget behind a built-in piece and configure it, while Day keeps owning layout, lifecycle, and everything else. A piece with a tweak applied is a Tweaked Piece: same widget, same handle, a little more configured.
The showcase’s Tweaks page (in the gallery) demonstrates everything on this page.
Applying a tweak
The portable entry point is a modifier that runs once at mount, after the native widget exists:
button("Save").tweak(|node| {
// `node` is the realized node; per-toolkit accessors turn it into a native handle.
})
Each toolkit crate adds a typed extension trait over that, which is what you’ll normally use. The closure gets the native widget and its concrete class name, so a tweak knows exactly what it’s poking:
use day_appkit::AppKitExt; // exists only in the appkit build
button("Save").appkit(|view, class, _mtm| { // class == "NSButton"
if let Some(btn) = view.downcast_ref::<objc2_app_kit::NSButton>() {
unsafe { btn.setBezelStyle(objc2_app_kit::NSBezelStyle::Toolbar) };
}
})
.gtk(|mdc, class| …), .uikit(|view, class, mtm| …), and .android(|view, class, jni_env| …)
follow the same shape with each platform’s own types. Qt, XAML, and ArkUI sit behind C shims, so
their accessors hand out the raw native pointer (plus the class) instead, with a short
bring-your-own-C++ recipe (each tier is spelled out in the
tweaks reference).
That class name is what keeps a tweak from breaking silently. On the typed tiers it’s the live widget’s runtime
class, so if a piece ever has more than one native backing (a plain label as UILabel, a
link-bearing one as UITextView), the tweak can match on the class instead of guessing a
downcast. On the raw tiers, where Rust can’t introspect an opaque pointer, it’s the metadata your
C++ needs: pass it across the shim and guard the cast, rather than blindly reinterpreting the
pointer as the wrong control.
Two rules cover most of what can go wrong. Day re-applies the properties it manages (a
button’s title, a slider’s value) on its next update, so tweak the properties Day doesn’t touch
(bezels, tick marks, selectability) and they’re stable. And if a native call changes the widget’s
intrinsic size, tell layout with day::invalidate_size(node), because Day can’t see mutations it
didn’t make.
Reaching a widget later
A mount-time hook covers configuration; for imperative access afterward (from an event handler,
say), capture a NativeRef:
let save_ref = NativeRef::new();
column((
button("Save").native_ref(&save_ref),
button("Flash the save button").action(move || {
save_ref.with(|node| { /* per-toolkit accessor on `node` */ });
}),
))
The ref clears automatically when the piece unmounts, so a late timer or async completion is a
safe None, never a dangling widget. Reads are reactive, too: a label whose closure calls
save_ref.node() re-renders when the referenced piece mounts or disappears.
Packaged tweaks
Anything worth reusing is worth packaging: a day-tweak-* crate wraps the per-toolkit calls in
one modifier and no-ops on toolkits it doesn’t cover, so the app using it stays completely
free of #[cfg]. Three in-tree examples span the range from trivial to fully cross-platform:
use day_tweak_button_bezel::{Bezel, ButtonBezelTweak};
use day_tweak_label_selectable::LabelSelectableTweak;
use day_tweak_slider_tickmarks::{SliderTickmarksTweak, Tickmarks};
button("Save").bezel(Bezel::Toolbar); // AppKit only; stock elsewhere
label("Copy me").selectable(); // AppKit, GTK, Android
slider(v).tickmarks(Tickmarks::count(11).snap(true)); // six toolkits, incl. its own C++
The tick-marks crate is the one to study when you write your own: it configures a native feature
on six toolkits through every access tier Day has (objc2, gtk4-rs, JNI, and its own compiled
Qt C++, WinRT C++, and ArkUI NDK code), and it documents per-platform reality plainly (Material
sliders always snap when stepped; UIKit has no native tick API, so there it’s a no-op). Publishing
one is publishing a crate: consumers add a dependency, and day build wires the per-toolkit
features automatically.
The tweaks reference has the full per-toolkit matrix, the native-code recipes, and the mechanics underneath. For a new widget rather than a configured existing one, you want a native piece instead.