live_design!{ ... }
: Macro for defining UI structure, styles, and components.use link::widgets::*;
: Import Makepad's built-in Widget library.use link::theme::*;
: Import constants defined in the current theme (colors, font sizes, spacing, etc.).use link::shaders::*;
: Import standard Shader library (such as Sdf2d
, Pal
, Math
).ComponentName = {{RustStructName}} { ... }
: Bind DSL component to Rust struct. ComponentName
is the name used in DSL, RustStructName
is the corresponding Rust struct name.instance_name = <ComponentName> { ... }
: Instantiate a component. instance_name
is the LiveId
for this instance, which can be referenced in Rust code via id!(instance_name)
.<BaseComponent> { ... }
: Inherit from BaseComponent
and override or add properties.propertyName: value
: Set property value.propertyName = { ... }
: Set nested property (typically used for draw_bg
, draw_text
, layout
, walk
, animator
, etc.).id!(...)
: (In Rust code) Used to get a LiveId
.live_id!(...)
: (In DSL) Used to reference defined LiveId
(not commonly used, typically just use the name directly).(CONSTANT_NAME)
: Reference a constant defined in theme or elsewhere.dep("path/to/resource")
: Declare a resource dependency (font, image, SVG). Path is typically relative to Cargo.toml
or uses crate://self/
. Pay attention to specifying the actual resource path when packaging.Walk
& Layout
)walk
: Controls the element's own layout within its parent container.
width
, height
: (Size) Define dimensions.
Fill
: Fill available space in parent container.Fit
: Adapt size based on content.Fixed(value)
or value
(e.g., 100
): Fixed pixel value.All
: Occupy all space in parent container, ignoring padding.margin
: (Margin) External spacing.
{top: v, right: v, bottom: v, left: v}
v
(shorthand, equal on all sides)<THEME_MSPACE_1>
(reference theme constant)abs_pos: vec2(x, y)
: Set absolute position (typically used with Overlay
layout).layout
: Controls how child elements are laid out within this element.
flow
: (Flow) Layout direction for child elements.
Right
(default): Horizontal from left to right.Down
: Vertical from top to bottom.Overlay
: Stacked on top of each other.RightWrap
: Horizontal arrangement, wrapping to new line when space is insufficient.spacing: value
: Gap between child elements (according to flow
direction).line_spacing: value
: (RightWrap
, Down
flow) Spacing between lines.padding
: (Padding) Internal spacing.
{top: v, right: v, bottom: v, left: v}
v
(shorthand, equal on all sides)<THEME_MSPACE_2>
(reference theme constant)align
: (Align) Alignment of child elements within parent container.
{x: 0.0, y: 0.0}
(top left){x: 0.5, y: 0.5}
(center){x: 1.0, y: 1.0}
(bottom right)clip_x: bool
, clip_y: bool
: (default true
) Whether to clip content that exceeds boundaries.scroll: vec2(x, y)
: Scroll offset for content.Alignment Coordinates in Live DSL (align
)
Scope: Within layout
property block, used to control how a parent container aligns its child elements.
Type: Align { x: f64, y: f64 }
Coordinate System: Normalized Relative Coordinates, range typically 0.0 to 1.0.
Meaning:
align.x
: Controls child element alignment in parent container's available horizontal space.
0.0
: Left Align0.5
: Center Align1.0
: Right Alignalign.y
: Controls child element alignment in parent container's available vertical space.
0.0
: Top Align0.5
: Center Align1.0
: Bottom AlignKey Points:
align
applies to child elements as a whole (or multiple child elements as a group, depending on flow
). It determines child elements' position in the parent container's remaining space.align
has visible effect only when parent container's space is larger than child elements' dimensions. If child elements use width: Fill
or height: Fill
, there's typically no remaining space in that direction, so alignment has no meaning.clip_x/clip_y: false
).Example:
Draw*
Types)DrawColor
: Draw solid color background.
color: #RRGGBBAA
or (THEME_COLOR_...)
fn pixel(self) -> vec4 { ... }
: Overridable pixel shader.DrawQuad
: Basic primitive for drawing quadrilaterals. Other Draw*
types typically inherit from it. Includes basic vertex transformation and clipping logic.
draw_depth: f32
(default 1.0
): Controls drawing depth/layer.draw_zbias: f32
(typically used internally): Fine-tune depth to avoid Z-fighting.DrawText
/ DrawText2
: Draw text.
text_style: <TextStyleName> { ... }
: Set text style.color: #RRGGBBAA
or (THEME_COLOR_...)
wrap: Word / Line / Ellipsis
(Ellipsis may not be fully supported)font_scale: f64
(default 1.0
)brightness: f32
(legacy, may be deprecated)curve: f32
(legacy, may be deprecated)fn get_color(self) -> vec4 { ... }
: Overridable color logic (commonly used for animation).DrawIcon
: Draw SVG icon.
svg_file: dep("...")
or svg_path: "..."
color: #RRGGBBAA
or (THEME_COLOR_...)
(icon color)brightness: f32
curve: f32
fn get_color(self) -> vec4 { ... }
: Overridable color logic.DrawLine
: Draw line (inherits from DrawQuad
).
color: #RRGGBBAA
line_width: f64
(set in Rust)DrawScrollShadow
: Draw scroll shadow.
shadow_size: f32
Sdf2d
in fn pixel
)Use Sdf2d
for vector drawing in the fn pixel
function.
let sdf = Sdf2d::viewport(self.pos * self.rect_size);
SDF Drawing (Sdf2d
in fn pixel
) - Basic Shapes
Within the fn pixel
function, you can use the Sdf2d
object to define and combine shapes. Here are commonly used basic shape functions and their descriptions:
sdf.circle(cx: float, cy: float, radius: float)
cx
, cy
: x and y coordinates of the circle center.radius
: Circle radius.sdf.dist
to the signed distance from the current pixel to the circle boundary. Inside is negative, outside is positive. sdf.shape
is updated to min(sdf.shape, sdf.dist)
.sdf.rect(x: float, y: float, w: float, h: float)
x
, y
: x and y coordinates of the rectangle's bottom-left corner.w
, h
: Width and height of the rectangle.sdf.dist
to the signed distance from the current pixel to the rectangle boundary. sdf.shape
is updated to min(sdf.shape, sdf.dist)
.sdf.box(x: float, y: float, w: float, h: float, radius: float)
x
, y
: x and y coordinates of the rectangle's bottom-left corner.w
, h
: Width and height of the rectangle.radius
: Radius for all four corners.sdf.dist
to the signed distance from the current pixel to the rounded rectangle boundary. sdf.shape
is updated to min(sdf.shape, sdf.dist)
.sdf.box_x(x: float, y: float, w: float, h: float, r_left: float, r_right: float)
r_left
, right-top and right-bottom corners use r_right
.x
, y
: x and y coordinates of the rectangle's bottom-left corner.w
, h
: Width and height of the rectangle.r_left
: Radius for the two left corners.r_right
: Radius for the two right corners.sdf.dist
to the signed distance from the current pixel to the specific rounded rectangle boundary. sdf.shape
is updated to min(sdf.shape, sdf.dist)
.sdf.box_y(x: float, y: float, w: float, h: float, r_top: float, r_bottom: float)
r_top
, left-bottom and right-bottom corners use r_bottom
.x
, y
: x and y coordinates of the rectangle's bottom-left corner.w
, h
: Width and height of the rectangle.r_top
: Radius for the two top corners.r_bottom
: Radius for the two bottom corners.sdf.dist
to the signed distance from the current pixel to the specific rounded rectangle boundary. sdf.shape
is updated to min(sdf.shape, sdf.dist)
.sdf.box_all(x: float, y: float, w: float, h: float, r_left_top: float, r_right_top: float, r_right_bottom: float, r_left_bottom: float)
x
, y
: x and y coordinates of the rectangle's bottom-left corner.w
, h
: Width and height of the rectangle.r_left_top
: Radius for the left-top corner.r_right_top
: Radius for the right-top corner.r_right_bottom
: Radius for the right-bottom corner.r_left_bottom
: Radius for the left-bottom corner.sdf.dist
to the signed distance from the current pixel to the highly customized rounded rectangle boundary. sdf.shape
is updated to min(sdf.shape, sdf.dist)
.sdf.hexagon(cx: float, cy: float, radius: float)
cx
, cy
: x and y coordinates of the hexagon center.radius
: Distance from center to any vertex.sdf.dist
to the signed distance from the current pixel to the hexagon boundary. sdf.shape
is updated to min(sdf.shape, sdf.dist)
.sdf.hline(y: float, half_thickness: float)
y
: The y coordinate of the horizontal line's center.half_thickness
: Half the thickness of the line (total thickness is 2 * half_thickness
).sdf.dist
to the signed distance from the current pixel to the horizontal line boundary. sdf.shape
is updated to min(sdf.shape, sdf.dist)
.sdf.move_to(x: float, y: float)
(x, y)
coordinates without drawing any line. It updates sdf.start_pos
and sdf.last_pos
.x
, y
: Coordinates of the new path starting point.line_to
or close_path
operations.sdf.line_to(x: float, y: float)
sdf.last_pos
) to the specified (x, y)
coordinates. This line segment becomes part of the current shape. It updates sdf.last_pos
.x
, y
: Coordinates of the line segment endpoint.sdf.dist
and sdf.shape
to reflect the distance to this line segment. It also updates sdf.clip
for path filling.sdf.close_path()
sdf.last_pos
) back to the starting point of the current subpath (sdf.start_pos
).sdf.arc2(cx: float, cy: float, radius: float, start_angle: float, end_angle: float)
cx
, cy
: x and y coordinates of the arc center.radius
: Arc radius.start_angle
: Starting angle of the arc (in radians).end_angle
: Ending angle of the arc (in radians).sdf.dist
to the signed distance from the current pixel to the arc boundary. sdf.shape
is updated to min(sdf.shape, sdf.dist)
.Important Notes:
These functions only define the shape boundaries (by calculating a Signed Distance Field).
To actually see these shapes, you must call a drawing function after defining the shape, such as sdf.fill(color)
, sdf.stroke(color, width)
, sdf.glow(color, width)
, etc.
sdf.shape
stores the minimum SDF value of the currently defined shape (for union
), while sdf.old_shape
is used for boolean operations (intersect
, subtract
, gloop
).
sdf.dist
stores the SDF value of the most recently defined basic shape.
Boolean Operations: (typically called after drawing shapes)
sdf.union()
: Combine current shape and old_shape
.sdf.intersect()
: Take intersection of current shape and old_shape
.sdf.subtract()
: Subtract current shape from old_shape
.Fill/Stroke:
sdf.fill(color)
: Fill current shape and reset shape state.sdf.fill_keep(color)
: Fill current shape, preserve shape state.sdf.fill_premul(premultiplied_color)
: Fill with premultiplied Alpha and reset.sdf.fill_keep_premul(premultiplied_color)
: Fill with premultiplied Alpha and preserve state.sdf.stroke(color, width)
: Stroke current shape and reset shape state.sdf.stroke_keep(color, width)
: Stroke current shape, preserve shape state.Effects:
sdf.glow(color, width)
: Add glow and reset shape state.sdf.glow_keep(color, width)
: Add glow, preserve shape state.sdf.gloop(k)
: Smooth blend with old_shape
(Metaball effect).Transforms: (affect subsequent drawing)
sdf.translate(dx, dy)
sdf.rotate(angle_rad, pivot_x, pivot_y)
sdf.scale(factor, pivot_x, pivot_y)
Clear: sdf.clear(color)
: Cover current result with specified color.
Result: sdf.result
(final vec4
color).
Shader Coordinates (self.pos
, self.geom_pos
, Sdf2d::viewport
parameters)
Scope: Within fn pixel
and fn vertex
functions in draw_*
blocks.
Type: vec2
(typically f32
type)
Coordinate System: Normalized Local Coordinates, range typically 0.0 to 1.0.
Meaning:
self.pos
(in fn pixel
): Represents the current pixel being calculated, in normalized coordinates within its drawing rectangle (self.rect_size
).
vec2(0.0, 0.0)
.vec2(1.0, 1.0)
.self.geom_pos
(in fn vertex
): Represents the current vertex being processed, in normalized coordinates within its input geometry (typically a unit quad).
GeometryQuad2D
, vertices are typically (0,0)
, (1,0)
, (1,1)
, (0,1)
.rect_pos
and rect_size
.Sdf2d::viewport(pos: vec2)
parameter: This pos
parameter is typically pixel coordinates or coordinates relative to some local origin, not normalized.
Sdf2d::viewport(self.pos * self.rect_size)
. Here, self.pos
is 0-1 normalized coordinate, multiplied by self.rect_size
to convert it to pixel-level local coordinates relative to the top-left corner of the current drawing rectangle. SDF functions (such as sdf.circle(cx, cy, r)
) expect this kind of pixel-level local coordinate.Key Points:
vertex
function return value and transformation matrices).self.pos
is essential in fn pixel
as it allows calculating color, pattern, or shape based on the pixel's relative position within the Widget rectangle.self.geom_pos
is used in fn vertex
to map the basic geometry (typically a 0-1 quad) to the target rectangle on screen.Sdf2d
functions are typically relative to the coordinate system based on when Sdf2d::viewport
was created (typically self.pos * self.rect_size
producing local pixel coordinates).Example:
Summary:
Concept | Scope | Type | Coordinate System | Meaning |
---|---|---|---|---|
align | layout block | Align | Normalized Relative (0-1) | Controls child elements alignment in parent's remaining space |
self.pos | fn pixel | vec2 | Normalized Local (0-1) | Current pixel's relative position within its drawing rectangle |
self.geom_pos | fn vertex | vec2 | Normalized Geometry (0-1) | Current vertex's relative position in input geometry |
Sdf2d parameters | fn pixel | float /vec2 | Local Pixel/Arbitrary (depends on viewport) | Coordinates for defining SDF shapes |
Understanding the distinction between these coordinate systems is crucial for precise control of Makepad UI layout and visual effects. align
is used for macro layout, while self.pos
in Shaders is used for micro, pixel-level drawing.
animator
)The animator
block defines Widget states and transitions between these states. It allows you to create interactive visual feedback in a declarative way.
animator
: Block containing all animation definitions.track_name
: Name of an animation track (LiveId), typically corresponding to a logical state (like hover
, focus
, open
, selected
, visible
, etc.). This name will be referenced in Rust code via id!(track_name)
.state_name = { ... }
)Inside each track_name
, typically at least two states are defined, most commonly on
and off
, or custom state names.
default: on | off
: Specifies the default state of this animation track when Widget initializes.state_name = { ... }
: Defines a specific state (such as on
, off
, down
, open
, close
, etc.).Within each specific state definition block (like on = { ... }
), the following parameters can be set to control how the animation transitions into this state:
from: { ... }
: Defines how to animate from other states to current state.
all: PlayMode
: Sets default transition animation for all source states not explicitly specified.other_state_name: PlayMode
: Sets animation for transition from specific other_state_name
state to current state. This overrides the all
setting.PlayMode
: Defines animation playback mode and duration:
Forward { duration: seconds }
: Animate forward from current value to target value, lasting seconds
seconds.Backward { duration: seconds }
: Animate backward from target value to current value (less commonly used directly, typically handled automatically by Forward
).Snap
: Immediately jump to target value, no animation.Loop { duration: seconds, end: value }
: Loop animation, duration
is time for one cycle, end
is loop point (typically 1.0
).ReverseLoop { ... }
: Reverse loop.BounceLoop { ... }
: Back-and-forth loop (ping-pong effect).ease: EaseType { ... }
: Specifies easing function, controlling animation speed curve.
Linear
, InQuad
, OutQuad
, InOutQuad
, InExp
, OutExp
, InOutExp
, ExpDecay {d1:v, d2:v}
(simulates physical decay).In/Out/InOut
+ Cubic
, Quart
, Quint
, Sine
, Circ
, Elastic
, Back
, Bounce
.Pow { begin: v, end: v }
, Bezier { cp0: v, cp1: v, cp2: v, cp3: v }
.apply: { ... }
: Defines which #[live]
properties should have what target values when animation reaches this state.
property: value
: Sets property property
's final value to value
.property: [{ time: t, value: v }, ...]
: Defines keyframe animation.
time
: Normalized time (0.0 to 1.0).value
: Value the property should have at this time point.time
is typically 0.0
, representing value at animation start (typically the from
state's value, but can be overridden).time
is typically 1.0
, representing value at animation end.draw_bg: { hover: 1.0 }
or walk: { margin: { left: 10.0 } }
.redraw: true
: Marks this animation track as needing continuous redrawing while playing. This is typically necessary for animations that change visually.cursor: MouseCursor
: (typically in on
state) Sets mouse cursor style (e.g., Hand
, Default
) when animation enters this state.handle_event
, based on user interaction (like Hit::FingerHoverIn
, Hit::FingerDown
) or other logic, call self.animator_play(cx, id!(track_name.state_name))
or self.animator_toggle(cx, condition, animate, id!(track.on), id!(track.off))
to trigger state transitions.on
) definition and looks up the corresponding from
rule based on current state to determine animation duration
and ease
.apply
block based on time, duration
, and ease
function.#[live]
fields in the Rust struct.redraw: true
or animator_handle_event
returns must_redraw()
, Widget redraw is requested.draw_walk
, Widget uses #[live]
field values updated by animation for drawing.Example: Button Hover and Down States
In Makepad, for implementing smooth visual animations (e.g., gradual changes in UI element position, size, color, opacity, etc.), it's strongly recommended to use the NextFrame
event mechanism rather than Timer
.
The Animator
system internally uses NextFrame
to drive animation updates and redraws.
NextFrame
(for animation)
self.redraw(cx)
or self.area.redraw(cx)
, Makepad sends an Event::NextFrame
event to that Widget (and other Widgets needing redraw) at the beginning of the next render cycle.NextFrame
event in handle_event
(if let Some(ne) = self.next_frame.is_event(event)
), calculate the next animation state based on time difference (ne.time
), update #[live]
properties or instance
variables, then call self.redraw(cx)
again to request the next frame update.Animator
system automates this process: animator_handle_event
internally checks if animation is in progress, if so, calculates next frame values, applies them, and returns must_redraw()
as true
, which typically triggers self.redraw(cx)
being called externally.NextFrame
events are tightly coupled with Makepad's render loop, ensuring animations are synchronized with screen refresh for smoother visual effects.NextFrame
is sent (unless there's another reason for redraw).animator_play
), update state in NextFrame
event (or let Animator
handle it), then request redraw.Timer
(for timed logic)
cx.start_timer(id, interval, repeats)
or cx.start_timeout(id, interval)
.Event::Timer
event with the triggered timer ID.Timer
event in handle_event
(if self.my_timer.is_event(event).is_some()
) and executes appropriate logic.Core Idea: Distinguish "Animation Process" from "Delayed Trigger"
Animation Process: Refers to the entire process of a property value smoothly transitioning from A to B. For example:
abs_pos: vec2(-1000.0, 10.0)
) to on-screen (abs_pos: vec2(60.0, 10.0)
).For this kind of smooth, frame-by-frame visual transition, you should rely on Animator
and NextFrame
. Animator
will calculate property intermediate values at each frame (triggered by NextFrame
), update it, then request next frame redraw, until animation completes. You should not use Timer
to manually control this kind of frame-by-frame update, as Timer
triggering intervals are unrelated to screen refresh rate, causing jerky animation.
Delayed Trigger: Refers to executing a one-time operation after a period of time. For example:
For this kind of triggering an action at a specific time point, you should use Timer
. cx.start_timeout(duration)
is designed for this. When timer triggers (my_timer.is_event(event).is_some()
), you perform the corresponding action, like calling self.close(cx)
to start closing animation.
For example: the RobrixPopupNotification
component popup animation in robrix project:
self.open(cx)
, you use self.animator.animator_play(cx, id!(mode.open))
to start the slide-in animation. This animation process is driven by Animator
and NextFrame
. No Timer
needed here.self.open(cx)
, you use self.view(id!(content.progress)).animator_play(cx, id!(mode.progress))
to start progress bar animation. This animation process is also driven by Animator
and NextFrame
. No Timer
needed here.Timer
(self.animation_timer = cx.start_timeout(2.5);
) in self.open(cx)
is correct. When this Timer
triggers after 2.5 seconds (if self.animation_timer.is_event(event).is_some()
), you call self.close(cx)
.self.close(cx)
(whether triggered by timer or close button click), you use self.animator.animator_play(cx, id!(mode.close))
to start the slide-out animation. This animation process is again driven by Animator
and NextFrame
. No Timer
needed here.Therefore:
Animator
+ NextFrame
for smooth visual transition animations (sliding, fading, scaling, etc.).Timer
(especially cx.start_timeout
) to implement triggering a one-time action after a fixed delay (like starting a closing animation).Best Practices:
hover
, focus
, open
, selected
).default
: Set appropriate initial state.from
rules: Define animation behavior for different state transitions to make transitions more natural.redraw: true
: Ensure visual changes are rendered.instance
variables: Define instance
variables in draw_*
shaders (e.g., instance hover: float
), modify these variables in animator
's apply
block, then use mix()
or other logic in fn pixel
or fn get_color
to change appearance based on these instance variables.NextFrame
mechanism. The simplest way is to use Makepad's built-in Animator
system, which encapsulates NextFrame
-based animation logic.Timer
when you need precise time intervals for non-rendering related logic. For example:
PopupNotification
using Timer
to auto-close after a fixed time is an appropriate use case.Error Message Pattern | Possible Causes | Common Scenarios & Tips |
---|---|---|
Enum variant X not found for field Y | 1. Enum variant name misspelled or case incorrect.2. Using a variant not supported by that enum type. | Check spelling of Flow (Right, Down, Overlay, RightWrap), Size (Fill, Fit, Fixed, All), Axis (Horizontal, Vertical), CheckType , RadioType , ImageFit , etc. Consult documentation or Rust definition. |
Identifier X not found | 1. Referenced LiveId (component instance name, constant name) misspelled or case incorrect.2. Referenced LiveId not defined in current or parent scope.3. Forgot use link::... import. | Check instance names, constant names (e.g., THEME_COLOR_TEXT ) are correct. Ensure defined before use. Check use statements. |
field X not found on struct Y | 1. Field name X misspelled or case incorrect.2. Component Y (or its base) doesn't have #[live] field named X .3. Trying to set property at wrong level (e.g., setting text_style directly on <Button> instead of inside draw_text ). | Check property name spelling. Consult component's Rust definition or documentation for available properties and their hierarchy. E.g., text styles typically in draw_text = { text_style = { ... } } . |
Cannot find value X to override | 1. Trying to override (<Base> { X: ... } ) a property X that doesn't exist in base class Base .2. Property X path or name is incorrect. | Confirm base class actually has the property you're trying to override, and path is correct. E.g., overriding background color might be <Base> { draw_bg: { color: ... } } . |
Value type mismatch for field X | Assigning wrong type of value to property X . | Check assignment types: numbers (100 , 12.5 ), colors (#f00 , (THEME_COLOR_...) ), strings ("text" ), enums (Fill , Down ), booleans (true , false ), dependencies (dep("...") ), objects ({...} ). |
Cannot cast value X to type Y | Same as above, type mismatch. | Same as above. |
Expected type X found Y | Same as above, type mismatch. | Same as above. |
Unexpected token / Expected '}' / Expected ':' / Expected ',' | DSL syntax error. | Check brackets {} () <> matching, property separators , , property name-value separators : . Use editor syntax highlighting. |
Cannot parse value | Value assigned to property has incorrect format (e.g., malformed color, malformed number, incorrect enum name). | Check color format (#RGB , #RGBA , #RRGGBB , #RRGGBBAA ), number format, enum spelling. |
Unexpected field X | 1. Using field name X not supported by component.2. Wrong structure level, putting property in wrong object. | Confirm component supports that field. Check curly brace {} hierarchy is correct. |
Cannot find widget class Y | 1. Component type <Y> name misspelled or case incorrect.2. Forgot use link::widgets::*; or other component use statement.3. Custom component not properly registered in Rust (live_register ). | Check component name spelling. Confirm widgets imported. For custom components, ensure registered in live_register . |
Expected object value for field Z | Field Z expects an object value (wrapped in {...} ), but assigned other type (like number, string). | Typically happens with layout , walk , draw_bg , draw_text , animator properties, ensure using {} to wrap their inner properties. |
Cannot find dependency / File not found at path X | Resource path in dep("...") incorrect or file missing. | Check crate://self/ prefix is correct. Confirm file path and name are accurate, and file exists in project. |
(Shader compilation errors) | MPSL code errors in fn pixel or fn vertex . | Check Shader syntax, variable names, function calls, types. Reference GLSL syntax and Makepad built-in functions (Sdf2d , Pal , Math ). |
Name collision between X splat and Y | #[derive(Live)] macro detected naming collision. Typically a #[live] field with same name as a field implicitly handled by attribute macro (like #[walk] ). | Remove or rename conflicting #[live] field. E.g., if using #[walk] walk: Walk , don't also define #[live] abs_pos: DVec2 . |
the trait bound X: LiveApply is not satisfied | Typically a chain reaction from failed #[derive(Live)] , causing LiveApply trait not to be implemented, which in turn affects #[derive(Widget)] . | Resolve the root cause of #[derive(Live)] failure (typically naming collisions or other macro processing errors). |
no function or associated item named '...' found for struct X | Same as above, failed #[derive(Live)] causing LiveNew trait methods (like new , live_design_with , live_type_info ) not to be implemented. | Resolve the root cause of #[derive(Live)] failure. |
Debugging Tips:
#[derive(Live)]
part) is the most authoritative way.<View>
: Basic container.
show_bg: bool
draw_bg: <DrawColor>
(or override to other Draw* type)walk
, layout
scroll_bars: <ScrollBars>
optimize: None/DrawList/Texture
<Label>
: Display text.
text: "string"
draw_text: <DrawText2>
walk
, layout
(typically width:Fit, height:Fit
)<Button>
: Clickable button.
text: "string"
draw_text: <DrawText2>
draw_icon: <DrawIcon>
draw_bg: <DrawQuad>
(or override)icon_walk
, label_walk
, walk
, layout
enabled: bool
<LinkLabel>
: Button with underline, for links.
<Button>
.url: "string"
(set in Rust)open_in_place: bool
<TextInput>
: Text input field.
text: "string"
empty_text: "string"
is_password
, is_read_only
, is_numeric_only: bool
draw_text
, draw_bg
, draw_cursor
, draw_selection
walk
, layout
<CheckBox>
/ <Toggle>
: Checkbox/switch.
text: "string"
draw_bg: <DrawCheckBox>
(can set check_type: Check/Radio/Toggle/None
)bind: "path.to.data"
<RadioButton>
: Radio button.
text: "string"
value: EnumVariant
draw_bg: <DrawRadioButton>
(can set radio_type: Round/Tab
)<View>
or <ButtonGroup>
.<Slider>
: Slider control.
text: "string"
min
, max
, default
, step
, precision
bind: "path.to.data"
text_input: <TextInput>
(built-in text input)draw_bg: <DrawSlider>
<DropDown>
: Dropdown menu.
labels: ["...", ...]
values: [Enum::Val1, ...]
selected_item: usize
popup_menu: <PopupMenu>
popup_menu_position: OnSelected/BelowInput
bind: "path.to.data"
<Image>
: Display bitmap.
source: dep("...")
fit: Stretch/Horizontal/Vertical/Smallest/Biggest/Size
<Icon>
: Display SVG icon.
draw_icon: { svg_file: dep("..."), color: ... }
icon_walk
<ScrollBars>
: Scrollbar container (typically used in <View>
's scroll_bars
property).
show_scroll_x
, show_scroll_y: bool
scroll_bar_x
, scroll_bar_y: <ScrollBar>
<PortalList>
/ <PortalList2>
: High-performance list.
next_visible_item
and item
.auto_tail: bool
<Html>
/ <Markdown>
: Render formatted text.
body: "string"
<PageFlip>
/ <SlidesView>
: Page/slide switcher.
active_page: live_id!(page_id)
/ current_slide: value
<Dock>
: Dockable panel system.
root
, Tabs
, Tab
structure.<Splitter>
: Draggable separator.
axis: Horizontal/Vertical
align: FromA/FromB/Weighted
a: <Widget>
, b: <Widget>
<Modal>
: Modal dialog.
content: <Widget>
<PopupNotification>
/ RobrixPopupNotification
: Non-modal popup notification.
content: <Widget>
<AdaptiveView>
: Responsive view switcher.
Desktop = <Widget>
, Mobile = <Widget>
<CachedWidget>
: Cache child Widget.
child_id = <Widget>
<View>
/<ViewBase>
and VariantsAdvanced Usage:
fn pixel
or fn vertex
directly in draw_bg
to create complex background effects, gradients, patterns, or visual changes responding to state.optimize: Texture
or DrawList
): For View
s containing a lot of static or infrequently changing content, can significantly improve performance. Texture
mode draws content to a texture, DrawList
mode caches draw commands.flow
, align
, spacing
, padding
, margin
, and Size
modes (Fill
, Fit
, Fixed
) to build complex, responsive layout structures.View
itself doesn't directly handle many interactions, it can capture FingerDown
, FingerHover
events and, combined with animator
, change instance
variables in draw_bg
for visual feedback.<ScrollXView>
, <ScrollYView>
, <ScrollXYView>
, or add scroll_bars: <ScrollBars> {}
to a regular <View>
to create scrollable areas.Example (Custom Shader & Optimization):
<View>
is the only built-in Widget that can have the event_order
property set.
<View>
Component:
DSL/Widgets/View.md
) explicitly lists event_order ([EventOrder](#eventorder))
as a field.EventOrder
enum allows you to specify event propagation order among its child components:
Up
(default): From last child to first (similar to HTML DOM bubbling).Down
: From first child to last.List(Vec<LiveId>)
: In order of LiveId
s specified in the list.Other Built-in Components:
<Button>
, <Label>
, <TextInput>
, <Slider>
, etc.) typically don't directly expose the event_order
property for users to set in DSL.<View>
as part of their base structure, but their own event handling logic is typically fixed or depends on the parent <View>
's event_order
.<Button>
might internally have a <View>
for layout of icon and text, but you can't directly set event_order
on the <Button>
to change the order in which icon and text receive events. You'd need to modify the internal live_design!
definition of <Button>
(if it's custom) or accept its default behavior.<Dock>
, <PortalList>
, <Window>
, they have their own specific event dispatching logic to manage their children (Tabs, List Items, Child Windows), typically not controlled through a simple event_order
property.Summary:
<View>
is the only core built-in Widget that can directly have event_order
configured in live_design!
.<View>
with specific event_order
.<ButtonFlat>
, <H1>
, etc.), they typically inherit from base Widgets (like <Button>
, <Label>
), and their event handling order is primarily determined by their internal structure and parent container.If you need fine-grained control over event order for child elements of a specific component (not <View>
), you might need to:
<View>
and set that <View>
's event_order
.<View>
with specific event_order
in its internal live_design!
definition.<Label>
Advanced Usage:
fn get_color
or fn pixel
in draw_text
to implement text color gradients, special effects, or color changes based on state.<Html>
or <Markdown>
Widgets, which internally use <TextFlow>
, which in turn uses <Label>
(or its DrawText) for underlying drawing.hover_actions_enabled: true
, then listen for LabelAction::HoverIn/HoverOut
in Rust code, typically used for displaying Tooltips.Example (Custom Color Shader):
<Button>
Advanced Usage:
draw_bg
, draw_text
, draw_icon
with fn pixel
or fn get_color
to completely change button appearance and state feedback.<View>
and other Widgets inside <Button>
to create complex button layouts with multiple elements (icon, text, status indicators) (requires Button's flow
not to be Overlay
, or custom drawing logic).animator
for complex visual transitions between pressed, hover, disabled states.ButtonAction::Clicked/Pressed/Released
and execute different logic based on action_data
.Example (Custom Background and Animation): (see Advanced example in documentation)
<LinkLabel>
Advanced Usage:
<Button>
, can override draw_bg
, draw_text
like Button to change underline style, text color, and hover/pressed effects.url
property in Rust code based on application state.clicked
action in Rust to perform additional application logic besides opening URL.Example (Custom Underline and Colors):
<TextInput>
Advanced Usage:
draw_bg
, draw_text
, draw_cursor
, draw_selection
to completely control input field visual style, including background, border, cursor, selection highlight.is_numeric_only
, more complex validation (like email format, max length) needs to be done in Rust's handle_actions
by listening to TextInputAction::Changed
and processing.bind
property to synchronize input field value with Rust data structure.TextInputAction::KeyDownUnhandled
in Rust to handle key events not processed internally by TextInput.set_is_read_only
or set_is_password
in Rust code based on conditions.Example (Custom Background and Cursor): (see Advanced example in documentation)
<CheckBox>
/<Toggle>
Advanced Usage:
draw_bg
with fn pixel
to draw any shape or icon instead of default check mark, circle, or switch slider. check_type: None
can completely remove default drawing, letting you use draw_icon
or other child Widgets to represent state.<CheckBoxCustom>
, override draw_icon
's fn get_color
or fn pixel
to change icon color or appearance based on active
(checked state), hover
, focus
instance variables.bind
property to synchronize checkbox's boolean state with data model.Example (Custom Toggle Appearance):
<RadioButton>
Advanced Usage:
draw_bg
with fn pixel
to create custom radio button appearance (e.g., different selection mark, background shape). radio_type: Tab
creates tab-style radio buttons.<View>
and listen for RadioButtonAction::Clicked
in Rust, then manually deselect other RadioButtons in same group (using RadioButtonSet
or manual iteration).<RadioButtonCustom>
or <RadioButtonImage>
variants, configure draw_icon
or image
property. Can combine with animator
to change icon/image appearance based on active
state.bind
and value
to synchronize selected value to data model.Example (Tab Style & State Management):
<Slider>
Advanced Usage:
draw_bg
with fn pixel
to completely change slider track and handle appearance, including shape, color, gradients. You can use the slide_pos
(0.0-1.0) instance variable to determine drawing position.draw_bg: { bipolar: 1.0 }
to make value bar draw from center to both sides, useful for representing -1 to 1 or similar ranges.text_input
styling, or listen for SliderAction::Slide
in Rust and manually update a separate <Label>
or <TextInput>
.step
and precision
to control slider's discrete values and display format.Example (Custom Rotary Appearance): (see RotarySolid example in documentation)
<DropDown>
Advanced Usage:
draw_bg
and draw_text
to change button style itself.popup_menu: <PopupMenu> { ... }
to fully customize popup menu background, border, and menu items (menu_item: <PopupMenuItem> { ... }
) appearance (background, text, selection mark).labels
and values
lists in Rust code, then call redraw(cx)
to update dropdown options.bind
to synchronize selected value
to data model.Example (Custom Popup Menu Items):
<Image>
/<Icon>
/<ImageBlend>
Advanced Usage:
draw_bg
(Image) or draw_icon
(Icon) with fn pixel
to apply filters, color adjustments, blending effects, or other image processing.animator
to change opacity
, image_scale
, image_pan
(DrawImage/DrawWebView) or draw_icon
instance
variables to achieve fade-in/out, scaling, panning, or color animation. ImageBlend
has built-in blend
animator for cross-fade.load_image_dep_by_path
, load_image_file_by_path
, load_image_file_by_path_async
, or set_texture
in Rust to dynamically change displayed image.Example (ImageBlend Switch): (see ImageBlend example in documentation and App code)
<PortalList>
/<PortalList2>
ListItemTypeA
, ListItemTypeB
) in live_design!
, then in Rust's next_visible_item
loop, decide based on data whether to call list.item(cx, item_id, live_id!(ListItemTypeA))
or list.item(cx, item_id, live_id!(ListItemTypeB))
for each item_id
.handle_event
(via ScrollBarsAction
or comparing first_id
and range_end
), then asynchronously load more data and update range_end
.max_pull_down
and detection of first_scroll > 0.0
to implement pull-to-refresh interaction.item()
returns existing item. If using reuse_items: true
, need to reset state when reused item is obtained.<Dock>
/<Splitter>
/<Tab>
/<TabBar>
Dock
's dock_items
state in Rust code (add/remove Tab
and Tabs
definitions), then call redraw(cx)
. Need careful management of LiveId
.Tab
component's draw_bg
, draw_name
, draw_icon
styles.TabBar
's draw_bg
, draw_fill
, draw_drag
styles.Splitter
's draw_bg
style.Dock
's dock_items
HashMap
to save and restore user-customized layout. Need to handle LiveId
conflicts (as shown in PR).<Html>
/<Markdown>
/<TextFlow>
draw_normal
, draw_italic
, draw_bold
and other DrawText2
properties, as well as draw_block
(DrawFlowBlock
) colors and fn pixel
to change rendering style.code_layout
, quote_layout
, list_item_layout
and other Layout
properties.link: <MyLink>
to use custom link component (needs to inherit from LinkLabel
or Button
).<Button>
) in Html
or Markdown
, and handle their events in Rust. This typically requires manual handling of custom tags in draw_walk
.<Modal>
/<PopupNotification>
Modal
's bg_view
or PopupNotification
's draw_bg
to change background appearance (e.g., different blur effect, color, or fully transparent).content
can be any complex Widget combination.animator
to change popup/dismiss animation effects (e.g., fade-in/out, scaling, different easing functions).open(cx)
and close(cx)
in Rust based on application logic.handle_actions
.<AdaptiveView>
set_variant_selector
to provide complex logic to select view variant based on multiple factors (not just screen size, but also platform, device features, application state).retain_unused_variants: true
to preserve non-active view state, avoiding re-initialization when switching back. Be mindful of memory usage.<CachedWidget>
Best practice is typically to define as much as possible declaratively in DSL, reserving complex logic and state management for Rust code.
Focus on features and built-in functions available in fn pixel
and fn vertex
functions within draw_*
blocks in live_design!
.
fn pixel(self) -> vec4
: Pixel shader function. Calculates and returns final color (RGBA) for current pixel. self
contains uniform
, instance
, varying
variables and built-in variables like pos
(normalized 0-1 coordinates), rect_pos
, rect_size
.fn vertex(self) -> vec4
: Vertex shader function. Calculates and returns final clip space position of vertex. Typically needs to set varying
variables to pass to pixel
function. self
contains uniform
, instance
, varying
and built-in variables like geom_pos
(geometry normalized 0-1 coordinates), rect_pos
, rect_size
, camera_projection
, camera_view
, view_transform
, draw_clip
, view_clip
, draw_zbias
, draw_depth
.self
: Within shader functions, self
contains all available uniform
, instance
, varying
variables and built-in variables. Can be accessed directly via self.propertyName
.float
: Single-precision floating point.vec2
, vec3
, vec4
: 2/3/4-dimensional float vectors.mat2
, mat3
, mat4
: 2x2, 3x3, 4x4 float matrices.int
, ivec2
, ivec3
, ivec4
: Integer and vectors.bool
, bvec2
, bvec3
, bvec4
: Boolean and vectors.texture2d
: 2D texture sampler.textureOES
: (Android-specific) OES texture sampler, typically for video.self.pos
(vec2, pixel shader): Current pixel's normalized coordinates (0.0 to 1.0) within rect_size
.self.geom_pos
(vec2, vertex shader): Input geometry (typically quad) normalized vertex coordinates (0.0 to 1.0).self.rect_pos
(vec2): Current drawing rectangle's top-left screen coordinates.self.rect_size
(vec2): Current drawing rectangle's dimensions (width, height).self.draw_clip
(vec4): Drawing clip region (xy=min, zw=max).self.view_clip
(vec4): View clip region (xy=min, zw=max).self.view_shift
(vec2): View scroll offset.self.camera_projection
(mat4): Camera projection matrix.self.camera_view
(mat4): Camera view matrix.self.view_transform
(mat4): View transform matrix.self.draw_depth
(float): Base drawing depth.self.draw_zbias
(float): Depth offset.self.dpi_factor
(float): Current DPI factor.uniform name: type
: Defined in DSL, shared by all instances (overridable).instance name: type
: Defined in DSL, unique per instance (commonly used for animation/state).varying name: type
: Defined in DSL, for passing data between vertex
and pixel
.let name: type = value;
: Declare local variable in function.var name: type = value;
: Declare mutable local variable in function.#RGB
, #RGBA
, #RRGGBB
, #RRGGBBAA
: Hexadecimal colors.vec4(r, g, b, a)
: RGBA in 0.0 to 1.0 range.(THEME_COLOR_...)
: Reference theme color constant.Sdf2d
, Pal
, Math
, GaussShadow
)Sdf2d
(Signed Distance Field 2D): For vector drawing.
Sdf2d::viewport(pos: vec2) -> Self
: Create SDF context.sdf.clear(color: vec4)
: Cover result with color.sdf.dist
and sdf.shape
)
sdf.circle(cx, cy, radius)
sdf.rect(x, y, w, h)
sdf.box(x, y, w, h, radius)
sdf.box_x(x, y, w, h, r_left, r_right)
sdf.box_y(x, y, w, h, r_top, r_bottom)
sdf.box_all(x, y, w, h, r_lt, r_rt, r_rb, r_lb)
sdf.hexagon(cx, cy, radius)
sdf.hline(y, half_thickness)
sdf.move_to(x, y)
sdf.line_to(x, y)
sdf.close_path()
sdf.arc2(cx, cy, radius, start_angle_rad, end_angle_rad)
sdf.arc_round_caps(cx, cy, radius, start_angle, end_angle, thickness)
sdf.arc_flat_caps(...)
sdf.union()
: shape = min(dist, old_shape)
sdf.intersect()
: shape = max(dist, old_shape)
sdf.subtract()
: shape = max(-dist, old_shape)
sdf.fill(color: vec4) -> vec4
: Fill and reset shape.sdf.fill_keep(color: vec4) -> vec4
: Fill and preserve shape.sdf.fill_premul(premultiplied_color: vec4) -> vec4
: Premultiplied Alpha fill and reset.sdf.fill_keep_premul(premultiplied_color: vec4) -> vec4
: Premultiplied Alpha fill and preserve.sdf.stroke(color: vec4, width: float) -> vec4
: Stroke and reset shape.sdf.stroke_keep(color: vec4, width: float) -> vec4
: Stroke and preserve shape.sdf.glow(color: vec4, width: float) -> vec4
: Add glow and reset shape.sdf.glow_keep(color: vec4, width: float) -> vec4
: Add glow, preserve shape.sdf.gloop(k: float)
: Smooth blend with old_shape
(Metaball).sdf.blend(k: float)
: Linear blend.sdf.translate(dx, dy)
sdf.rotate(angle_rad, pivot_x, pivot_y)
sdf.scale(factor, pivot_x, pivot_y)
sdf.result
(final vec4
color).Pal
(Palette): Color processing functions.
Pal::premul(color: vec4) -> vec4
: Convert to premultiplied Alpha.Pal::hsv2rgb(hsv: vec4) -> vec4
: HSV to RGB conversion.Pal::rgb2hsv(rgb: vec4) -> vec4
: RGB to HSV conversion.Pal::iqX(t: float) -> vec3
: Inigo Quilez's palette functions (X=0..7).Math
: Math functions.
Math::rotate_2d(v: vec2, angle_rad: float) -> vec2
: Rotate 2D vector.Math::random_2d(v: vec2) -> float
: Generate pseudo-random number (0-1) based on input vector.GaussShadow
: Gaussian blur shadow calculation.
GaussShadow::box_shadow(lower: vec2, upper: vec2, point: vec2, sigma: float) -> float
: Calculate rectangle shadow blur value.GaussShadow::rounded_box_shadow(lower: vec2, upper: vec2, point: vec2, sigma: float, corner: float) -> float
: Calculate rounded rectangle shadow blur value.(Based on files in builtin
directory in documentation)
abs
, ceil
, clamp
, degrees
, distance
, dot
, exp
, exp2
, faceforward
, floor
, fract
, inversesqrt
, length
, log
, log2
, max
, min
, mix
, mod
, normalize
, pow
, radians
, reflect
, refract
, sign
, smoothstep
, sqrt
, step
.acos
, asin
, atan
, cos
, sin
, tan
.cross
, matrixCompMult
, transpose
, inverse
.all
, any
, equal
, greaterThan
, greaterThanEqual
, lessThan
, lessThanEqual
, not
, notEqual
.sample2d(sampler: texture2d, coord: vec2) -> vec4
: Sample 2D texture.sample2d_rt(sampler: texture2d, coord: vec2) -> vec4
: (Makepad-specific) May be for render target texture sampling, behavior similar to or with specific optimizations compared to sample2d
.sample2dOES(sampler: textureOES, coord: vec2) -> vec4
: (Android-specific) Sample OES texture.dFdx(p)
, dFdy(p)
: Calculate partial derivatives of p
with respect to screen x/y coordinates (Fragment Shader only).self
: Know what built-in variables are available in self
(pos
, rect_pos
, rect_size
, etc.) and what uniform
/instance
/varying
variables you defined in DSL.self.pos
: Typically 0-1 normalized coordinates, relative to self.rect_size
.self.geom_pos
: 0-1 geometry coordinates, typically used in vertex shader.Sdf2d::viewport(self.pos * self.rect_size)
: Converts normalized coordinates to pixel-level coordinates relative to current drawing rectangle, common starting point for SDF drawing.Sdf2d
: For vector graphics, prioritize Sdf2d
provided functions, typically more convenient and optimized than manual calculations.fill
/stroke
reset sdf.shape
, while fill_keep
/stroke_keep
don't. Boolean operations (union
, intersect
, subtract
) update sdf.shape
and sdf.old_shape
.Pal::premul
for conversion, fill_premul
/fill_keep_premul
for direct handling. Returning sdf.result
is typically already premultiplied.pixel
function.varying
to calculate values in vertex
that can be interpolated between pixels.instance
variables to receive values from animator
and use mix()
or other logic in pixel
or vertex
function to change appearance based on these values.return #f00;
) to test code block execution.return vec4(value, 0.0, 0.0, 1.0);
).View
's debug
property.Core Idea: Proportional Positioning
Imagine a parent container, like a <View>
, with certain width and height. When you place a child element in it, if the parent container's space is larger than the child element, the child element needs to know where to be placed in this "remaining space".
"Normalized Relative Coordinates" is a way to describe this position, not using specific pixel values but using proportions.
0.0
represents the starting end (left or top).1.0
represents the ending end (right or bottom).0.5
represents the middle.Specifically for align: {x: f64, y: f64}
:
align.x
(horizontal direction):
0.0
: Align child element's left edge with parent container's available space's left edge.0.5
: Align child element's horizontal center with parent container's available space's horizontal center.1.0
: Align child element's right edge with parent container's available space's right edge.0.25
: Place child element at position 1/4 from the left.0.75
: Place child element at position 3/4 from the left.align.y
(vertical direction):
0.0
: Align child element's top edge with parent container's available space's top edge.0.5
: Align child element's vertical center with parent container's available space's vertical center.1.0
: Align child element's bottom edge with parent container's available space's bottom edge.0.25
: Place child element at position 1/4 from the top.0.75
: Place child element at position 3/4 from the top.Available Space (Available Space):
This is key to understanding relative coordinates. Available space refers to what's left in the parent container after placing all non-aligned child elements (e.g., elements arranged by flow: Right
or flow: Down
) and accounting for its own padding
, remaining for placing aligned child elements (typically those with width: Fit
, height: Fit
or width: Fixed
, height: Fixed
).
flow: Right
: Available horizontal space is parent container width minus all child element widths, spacing, and parent container left/right padding. Available vertical space is typically parent container height minus its top/bottom padding.flow: Down
: Available vertical space is parent container height minus all child element heights, spacing, and parent container top/bottom padding. Available horizontal space is typically parent container width minus its left/right padding.flow: Overlay
: Available space is typically the entire internal area of parent container minus padding.Why Use Normalized Relative Coordinates?
Size::Fill
: When a child element uses Size::Fill
, it occupies all available space, making align
in that direction typically ineffective since there's no remaining space for alignment.Example Understanding:
In this example:
align: {x: 0.5, y: 0.5}
means button's center point should align with available space's center point.align.x
= 10 + 120 * 0.5 = 70.align.y
= 10 + 50 * 0.5 = 35.(70, 35)
relative to parent container's top-left corner.In summary, normalized relative coordinates provide a resolution-independent, proportion-based way to define child element alignment within parent container's available space.