Introduction

Introduction to Pax

Pax is a library for building web & native applications alongside visual creative tools

A quick tour

How it works

Pax is made up of two pieces:

  1. The interface declaration: .pax file
  2. The application logic: .rs file

The interface declaration (or template for short) is a declarative way to specify the visual components of the user interface. Pax's templates can be created/edited via our design tool or by hand. See Designability to learn more about how it works.

Application logic is written in an accompanying Rust file (or Typescript, coming soon). This logic is triggered via handlers that are specified in templates and can set state that flows into templates.

Example

Writing Pax is intended to feel familiar, and the language borrows many ideas from prior art.

Following is a simple Pax component called IncrementMe:

increment-me.pax
<Group x=50% y=50% width=120px height=120px @click=self.increment >
    <Text text={self.num_clicks + " clicks"} id=text />
    <Rectangle
        fill={rgb(ticks, 75, 150)}
        corner_radii={RectangleCornerRadii::radii(10.0,10.0,10.0,10.0)}
    />
</Group>
 
@settings {
    @pre_render: handle_pre_render,
    #text {
        style: {
                font: {Font::system("Times New Roman", FontStyle::Normal, FontWeight::Bold)},
                font_size: 22px,
                fill: WHITE,
                align_vertical: TextAlignVertical::Center,
                align_horizontal: TextAlignHorizontal::Center,
                align_multiline: TextAlignHorizontal::Center
        }
    }
}

In this template we define a button that contains text and a rectangle. We use a group to combine them into an entity and attach a click handler to it. The text reads state self.num_clicks and we set the fill (color) of the rectangles using self.ticks as the red parameter of rgb(r,g,b). The last piece to note is the lifecycle handler we enabled in the settings block.

Based on this template our application logic must define the state of this component (num_clicks, ticks), the increment function, and the handle_pre_render function.

lib.rs
//File: lib.rs
#![allow(unused_imports)]
 
use pax_engine::api::*;
use pax_engine::*;
use pax_std::components::Stacker;
use pax_std::components::*;
use pax_std::primitives::*;
use pax_std::types::text::*;
use pax_std::types::*;
 
#[pax]
#[main]
#[file("lib.pax")]
pub struct Example {
    pub ticks: Property<usize>,
    pub num_clicks: Property<usize>,
}
 
impl Example {
    pub fn handle_pre_render(&mut self, ctx: &NodeContext) {
        let old_ticks = self.ticks.get();
        self.ticks.set((old_ticks + 1) % 255);
    }
 
    pub fn increment(&mut self, ctx: &NodeContext, args: Event<Click>) {
        let old_num_clicks = self.num_clicks.get();
        self.num_clicks.set(old_num_clicks + 1);
    }
}

In the accompanying rust file, we define the state struct and it's associated functions that we use in the template.

Here's the example component running:

The above IncrementMe component could be mounted as its own app, or could be composed into other Pax components.

We will give a brief introduction to Pax: its goals, how to use it, and details of is design and implementation.

Last updated on