Replies: 2 comments 1 reply
|
The idea is to have the ability to attach a listener to an artboard to be notified of a rive Node being updated position and dimensions after a reflow of the layout (and the caller will update any component affected, offering better flexibility at the expense of more code to handle it), or to allow to attach child Component directly to the artboard as node replacements (which will be less code but less flexibility). |
|
This is a draft spec for the idea i had in mind (we could support both): RIVE-based Responsive Layouts Design SpecContextRive's editor supports Rive Layouts: a Flex-based layout system that positions components relative to each other and reflows them as the artboard resizes. Designers can wireframe an entire UI in Rive, watch it respond to resizes in the editor, hook state machine transitions to resizes for layout animations, and export a Today The vendored Rive runtime (v0.1.155) is built with Decisions Summary
ArchitectureModified filesNo new files; Model A — node bounds listenersThe caller registers a callback per named node. After every update pass the Model B — node replacementThe caller attaches a Reflow detection (shared)Layout recalculation happens inside Detection points in
Bounds computation// artboard-space rect for a node
auto* node = artboard->find<rive::Component> (name.toStdString());
// layout nodes report real size; others fall back to a unit rect
float w = node->is<rive::LayoutComponent>() ? node->as<rive::LayoutComponent>()->layoutWidth() : 1.0f;
float h = node->is<rive::LayoutComponent>() ? node->as<rive::LayoutComponent>()->layoutHeight() : 1.0f;
// world transform in artboard coords, then map through viewTransform into component coordsNode world transform is read from APIclass YUP_API Artboard : public Component
{
public:
//==============================================================================
/** Callback invoked when a named node's bounds change after a layout reflow.
@param artboard The artboard that emitted the change.
@param nodeName The name of the node whose bounds changed.
@param bounds The new node bounds in component coordinates.
*/
using NodeBoundsCallback = std::function<void (Artboard& artboard, const String& nodeName, const Rectangle<float>& bounds)>;
//==============================================================================
/** Registers a listener for a named artboard node.
The callback is invoked whenever a layout reflow changes the node's bounds
(component coordinates). Pass an empty callback to remove the listener.
@param nodeName The name of the node in the .riv file.
@param callback The callback to invoke on bounds changes.
*/
void setNodeBoundsListener (StringRef nodeName, NodeBoundsCallback callback);
/** Removes the listener registered for the given node. */
void clearNodeBoundsListener (StringRef nodeName);
/** Removes all node bounds listeners. */
void clearAllNodeBoundsListeners();
/** Returns the current bounds of a named node in component coordinates.
@param nodeName The name of the node in the .riv file.
@return The node bounds, or an empty rectangle if the node is unknown.
*/
Rectangle<float> getNodeBounds (StringRef nodeName) const;
//==============================================================================
/** Attaches a component to a named artboard node.
The component's bounds are driven by the node's layout: whenever a reflow
changes the node's bounds, the artboard calls setBounds() on the component.
Ownership of the component remains with the caller.
@param nodeName The name of the node in the .riv file.
@param component The component to attach; must outlive the attachment.
@return True if the node exists and the component was attached.
*/
bool attachComponentToNode (StringRef nodeName, Component* component);
/** Detaches a component from a named node, stopping bounds updates. */
bool detachComponentFromNode (StringRef nodeName, Component* component);
/** Detaches every attached component. */
void detachAllComponents();
private:
void notifyNodeBoundsChanged();
Rectangle<float> computeNodeBounds (rive::Component* node) const;
HashMap<String, NodeBoundsCallback> nodeBoundsListeners;
HashMap<String, Component*> attachedComponents;
HashMap<String, Rectangle<float>> lastNodeBounds;
};Note: responsive behavior requires the artboard fit mode that lets Rive own the size — use Example// Wireframe designed in Rive with Rive Layouts, exported as ui.riv
auto file = ArtboardFile::load (File ("ui.riv"), *getFactory());
auto artboard = std::make_unique<Artboard> ("root", file);
artboard->setLayout (Artboard::Layout::layout);
// Model B: drop a real YUP component onto the "knob-panel" node
artboard->attachComponentToNode ("knob-panel", knobPanel.get());
// Model A: fine-grained control over a header that repositions on resize
artboard->setNodeBoundsListener ("header", [] (Artboard&, const String&, const Rectangle<float>& bounds)
{
// e.g. move the header, or fire an animation tied to the reflow
});
addAndMakeVisible (*artboard); |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
This follows a discussion on Discord. The idea would be to support a particular approach to UI design & development that uses the Rive Layout system for positioning of components relative to each other. Roughly, the user could:
.rivfile.setBoundsmethods etc get called whenever Rive reflows.All reactions