Skip to main content

CustomNode

CustomNode is the main escape hatch when the built-in widget set and higher-level helpers are not enough.

It lets you attach a custom lowerer, and optionally a custom render object, to a node in the widget tree. That means you can emit your own intermediate representation subtree, participate in hit-testing and event handling, and add custom painting behavior.

Most app authors should not start here. If ordinary layout widgets solve the problem, use them. If you only need lightweight custom drawing, use canvas(...). Reach for CustomNode only when you truly need a new kind of node behavior.

Example

use std::sync::Arc;

use fission::core::ui::{CustomNode, Node};

let node = Node::Custom(CustomNode {
debug_tag: "TimelineSurface".into(),
lowerer: Some(Arc::new(MyTimelineLowerer)),
render_object: None,
});

In this example, MyTimelineLowerer would implement LowerDyn and emit the node subtree needed for the custom surface.

Field table

FieldTypeMeaningNotes / default behavior
debug_tagStringHuman-readable label for the custom node.Useful when inspecting output or debugging.
lowererOption<Arc<dyn LowerDyn>>Object-safe lowering implementation that emits the node subtree.Required in practice. Lowering panics if no lowerer is set.
render_objectOption<Arc<dyn CustomRenderObject>>Optional runtime object for custom hit-testing, event handling, input method editor handling, and painting.None means the node is lowering-only.

What problems it solves

CustomNode exists for the cases where app or library authors need capabilities the normal widget catalog does not expose directly. Examples include advanced text surfaces, bespoke chart primitives, or domain-specific interactive canvases.

The split between lowerer and render_object matters. The lowerer describes what nodes should exist in the intermediate representation (IR). The render object is only necessary when the node also needs runtime behavior such as custom hit-testing or painting.

When not to use it

Do not use CustomNode just to avoid writing a normal widget. A named widget that returns ordinary Row, Column, Container, Text, and related nodes will usually be easier to maintain, easier to teach, and easier to test.

Also do not use CustomNode when CanvasLowerer or canvas(...) already fits the problem. Those helpers are narrower, which is usually a good thing.

Specific advice

Keep the custom surface boundary small. The more of your app that depends on CustomNode, the harder it becomes for teammates to reason about ordinary layout, semantics, and test behavior.

If you add a render_object, document its semantics and event model for your team. Once you leave the standard widget path, behavior becomes less obvious to future readers.

Node, canvas(...), and CanvasLowerer.