Tweaks
A tweak configures the native widget behind a built-in piece. For example, you can apply AppKit’s toolbar bezel to a button or enable tick marks on a XAML slider. Day continues to manage the widget’s layout and lifecycle. A Tweaked Piece uses the same widget and handle with additional configuration.
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, and that trait is the normal entry point. The closure gets the native widget and its concrete class name, so a tweak knows exactly what it is configuring:
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(|widget, class| …), .uikit(|view, class, mtm| …), and .android(|view, class, jni_env| …)
follow the same shape with each platform’s 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).
The class name 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 so the pointer is never reinterpreted as
the wrong control.
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
To reuse a tweak across apps, package it: 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 free of #[cfg]. Three examples in the repository show different platform configurations:
use day_tweak_button_bezel::{Bezel, ButtonBezelTweak};
use day_tweak_tooltip::TooltipTweak;
use day_tweak_slider_tickmarks::{SliderTickmarksTweak, Tickmarks};
button("Save").bezel(Bezel::Toolbar); // AppKit only; stock elsewhere
button("Save").tooltip("Save your changes (⌘S)"); // AppKit, GTK, Android; no-op elsewhere
slider(v).tickmarks(Tickmarks::count(11).snap(true)); // six toolkits, incl. its own C++
The tick-marks crate demonstrates configuring a native feature on
six toolkits through objc2, gtk4-rs, JNI, and compiled Qt C++,
WinRT C++, and ArkUI NDK code. It documents each platform’s behavior (Material sliders always
snap when stepped; UIKit has no native tick API, so there it’s a no-op). Publish the
crate as a Cargo package. Consumers add it as a dependency, and day build enables the corresponding toolkit features.
The tweaks reference has the full per-toolkit matrix, the native-code recipes, and the mechanics underneath. To add a new widget, write a native piece.