summaryrefslogtreecommitdiffhomepage
path: root/packages/kernel/src/ui_substrate.cpp
diff options
context:
space:
mode:
authorAdam Malczewski <[email protected]>2026-06-14 16:00:49 +0900
committerAdam Malczewski <[email protected]>2026-06-14 16:00:49 +0900
commit41abc47bfb4a5098bff611e3e241d2b63788cbec (patch)
tree9df638f56c16c2cc6cf9dac0648cb50687ff0929 /packages/kernel/src/ui_substrate.cpp
parent89c40575c353dfd3c9fcaf60d2bd45d3fa2b6792 (diff)
downloadunbox-41abc47bfb4a5098bff611e3e241d2b63788cbec.tar.gz
unbox-41abc47bfb4a5098bff611e3e241d2b63788cbec.zip
kernel: add request_frames() frame callback + UiSurface::transition_timing()
Two additive primitives for C++-driven, RCSS-tunable animation: - Host::request_frames(cb) -> FrameRequest: a per-frame callback (RAII handle) run before tick_all each frame; the kernel schedules frames continuously while >=1 request is alive and stops at rest. Fills the missing animation timer. - UiSurface::transition_timing(element_id, property): reads the RCSS-authored transition duration + easing, returning RmlUi's tween wrapped as a pure std::function (no RmlUi types cross the contract) so an extension can drive its own animation with hot-reloadable, designer-tunable timing/easing.
Diffstat (limited to 'packages/kernel/src/ui_substrate.cpp')
-rw-r--r--packages/kernel/src/ui_substrate.cpp61
1 files changed, 61 insertions, 0 deletions
diff --git a/packages/kernel/src/ui_substrate.cpp b/packages/kernel/src/ui_substrate.cpp
index 4c9efe8..9b9593d 100644
--- a/packages/kernel/src/ui_substrate.cpp
+++ b/packages/kernel/src/ui_substrate.cpp
@@ -3,6 +3,7 @@
#include "file_watcher.hpp"
#include "rmlui_renderer_gl3.h"
+#include <RmlUi/Core/Animation.h> // Transition / TransitionList / Tween
#include <RmlUi/Core/Context.h>
#include <RmlUi/Core/Core.h>
#include <RmlUi/Core/DataModelHandle.h>
@@ -12,7 +13,10 @@
#include <RmlUi/Core/Event.h>
#include <RmlUi/Core/Factory.h>
#include <RmlUi/Core/ID.h>
+#include <RmlUi/Core/Property.h>
+#include <RmlUi/Core/StyleSheetSpecification.h> // GetPropertyId
#include <RmlUi/Core/SystemInterface.h>
+#include <RmlUi/Core/Tween.h>
#include <RmlUi/Core/Variant.h>
// The kernel owns GL; system EGL/GLES headers are allowed here (same as the
@@ -2150,6 +2154,63 @@ void SurfaceHandle::dirty() {
}
}
+auto SurfaceHandle::transition_timing(std::string_view element_id, std::string_view property) const
+ -> std::optional<TransitionTiming> {
+ const Surface& s = *surface_;
+ if (s.document == nullptr) {
+ return std::nullopt; // not loaded yet => no computed values
+ }
+ Rml::Element* el = s.document->GetElementById(Rml::String(element_id));
+ if (el == nullptr) {
+ return std::nullopt;
+ }
+ // The computed `transition` property: a TransitionList (none/all + entries).
+ const Rml::Property* prop = el->GetProperty(Rml::PropertyId::Transition);
+ if (prop == nullptr) {
+ return std::nullopt;
+ }
+ const Rml::TransitionList list = prop->Get<Rml::TransitionList>();
+ if (list.none) {
+ return std::nullopt;
+ }
+
+ // Resolve the requested property name (e.g. "transform") to RmlUi's id, then
+ // find the matching per-property transition. An `all` transition applies to
+ // every property and serves as the fallback if no exact entry exists; an
+ // exact entry wins over `all`.
+ const Rml::PropertyId want = Rml::StyleSheetSpecification::GetPropertyId(Rml::String(property));
+ const Rml::Transition* match = nullptr;
+ if (want != Rml::PropertyId::Invalid) {
+ for (const Rml::Transition& t : list.transitions) {
+ if (t.id == want) {
+ match = &t;
+ break;
+ }
+ }
+ }
+ Rml::Tween tween;
+ double duration = 0.0;
+ double delay = 0.0;
+ if (match != nullptr) {
+ tween = match->tween;
+ duration = static_cast<double>(match->duration);
+ delay = static_cast<double>(match->delay);
+ } else if (list.all && !list.transitions.empty()) {
+ // `all foo 0.2s ease` parses to a single entry flagged all=true; reuse
+ // its timing/tween for the requested property.
+ const Rml::Transition& t = list.transitions.front();
+ tween = t.tween;
+ duration = static_cast<double>(t.duration);
+ delay = static_cast<double>(t.delay);
+ } else {
+ return std::nullopt;
+ }
+
+ // Wrap RmlUi's Tween BY VALUE — Tween::operator()(float) is the evaluator;
+ // capturing it keeps RmlUi types out of the contract entirely.
+ return TransitionTiming{duration, delay, [tween](float t) { return tween(t); }};
+}
+
// ---- PreviewHandle (public Preview impl) ------------------------------------
PreviewHandle::~PreviewHandle() { substrate_->impl_->destroy_preview(state_); }