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: f32curve: f32fn get_color(self) -> vec4 { ... }: Overridable color logic.DrawLine: Draw line (inherits from DrawQuad).
color: #RRGGBBAAline_width: f64 (set in Rust)DrawScrollShadow: Draw scroll shadow.
shadow_size: f32Sdf2d 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: booldraw_bg: <DrawColor> (or override to other Draw* type)walk, layoutscroll_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, layoutenabled: 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: booldraw_text, draw_bg, draw_cursor, draw_selectionwalk, 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: EnumVariantdraw_bg: <DrawRadioButton> (can set radio_type: Round/Tab)<View> or <ButtonGroup>.<Slider>: Slider control.
text: "string"min, max, default, step, precisionbind: "path.to.data"text_input: <TextInput> (built-in text input)draw_bg: <DrawSlider><DropDown>: Dropdown menu.
labels: ["...", ...]values: [Enum::Val1, ...]selected_item: usizepopup_menu: <PopupMenu>popup_menu_position: OnSelected/BelowInputbind: "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: boolscroll_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/Verticalalign: FromA/FromB/Weighteda: <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 Views 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 LiveIds 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.