prowlengine/prowl/prowl-paper

Trust tier 3
  • community
Source check pending

Checking that the SKILL.md file is still in the repository.

Paper, the immediate mode UI library Prowl uses for OnGui game UI and the editor. Use when writing any non trivial Paper code - layouts, sizing, styling, hover and press states, transitions and animation, events and focus, text fields, scrolling, popups and layers, custom canvas drawing - or when Paper UI lays out, clicks or animates unexpectedly.

Install

npx vskill@latest install prowlengine/prowl/prowl-paper

SKILL.md

Paper UI

Paper is immediate mode with a flexbox style layout engine. Read the prowl-ui skill first for where Paper fits in a Prowl game. This skill is the library itself.

csharp
using Prowl.PaperUI; // Paper, GuiProp, Layer, PaperCursor, PaperKey, LayoutType, PositionType
using Prowl.PaperUI.Events; // ClickEvent, DragEvent, ScrollEvent, KeyEvent
using Prowl.PaperUI.LayoutEngine; // UnitValue, ElementHandle
using Prowl.Scribe; // FontFile, TextWrapMode
using Prowl.Vector; // Color, Rect, Float2, Easing
using Prowl.Quill; // Canvas, in paper.Draw
using TextAlignment = Prowl.PaperUI.TextAlignment; // Prowl.Scribe has one too

The frame model

  • Each frame Prowl calls BeginFrame, then every component's OnGui(Paper paper), then EndFrame. EndFrame does styles and transitions, layout, events, then rendering.
  • The element tree is rebuilt from nothing every frame. An element not declared this frame does not exist, and its stored state, transitions and text field contents are deleted.
  • Every event callback runs at the end of the frame, after your whole OnGui returned and after layout. Callbacks only change state. The next frame's OnGui reads that state. Copy loop variables before capturing them (int index = i;).
  • Keep UI state (open flags, selection, scroll offsets, drag state) in your component's fields.

Ids

  • paper.Box(string id, int intId = 0), Row(...), Column(...), Grid(...), Overlay(...) return a fluent builder.
  • The real id hashes the parent's id, the PushID scope, the string, the int and the caller's line number. The same id twice in one frame throws "Element already exists with this ID".
  • In a loop, pass the index: paper.Box("row", i).
  • A helper method that calls paper.Box("button") collides when called twice under one parent, since the line is the same. Give it an int id parameter or vary the string.
  • Every component's `OnGui` shares one Paper at the root, so two instances of the same component collide. Wrap the body in paper.PushID(InstanceID) and paper.PopID().
  • Hover, focus, storage and transitions follow the id across frames. An id built from a changing value loses all of them.

Hierarchy

using (builder.Enter()) { ... } makes the element the parent of everything declared inside. paper.CurrentParent is the current parent's handle.

Layout

Defaults: Width and Height are Stretch, the layout is a Column, margins and padding are 0, children align to the start.

Units (UnitValue):

  • A plain number is pixels: .Width(200).
  • UnitValue.Stretch(weight) takes a share of the leftover space. UnitValue.Percentage(p) is a percent of the parent's inner size. UnitValue.Auto fits the content.
  • They combine: UnitValue.Percentage(50) - UnitValue.Pixels(8).

How sizing works:

  • Along the layout direction, every child first gets its pixel, percent and content size. The leftover space is split between stretch weights, clamped to min and max. With too little space, children overflow. Nothing clips unless .Clip().
  • Across the layout direction, a stretch child fills the parent. Others keep their size and follow AlignItems or AlignSelf.
  • `AlignItems(LayoutAlignment.Center)` and `JustifyContent(LayoutJustification.Center)` do nothing to stretch children, because they already fill the space. Give children a pixel or Auto size first.
  • Inside an Auto sized container, a pure stretch child counts as 0 and collapses. In a Height(UnitValue.Auto) column, give every child a pixel height or Height(UnitValue.Auto).
  • Sizes include padding. Padding shrinks the area for text and children.
  • MinWidth, MaxWidth, MinHeight, MaxHeight and Padding accept only pixels and percent. Stretch or Auto there throws during EndFrame.
  • AspectRatio(w / h) derives one Auto axis from the other.

Spacing and argument order (not CSS order):

  • Padding(left, right, top, bottom), Padding(all), Padding(horizontal, vertical). Margin takes the same orders.
  • Left, Right, Top, Bottom set margins, not positions. Position(left, top) sets the left and top margins.
  • A stretch margin is a flexible spacer: .Margin(UnitValue.Stretch()) centres a fixed size element.
  • Gap(px) is between children. LineGap(px) is between wrapped lines and grid rows.

Containers:

  • LayoutType(LayoutType.Row), or use paper.Row. ReverseLayout(). WrapContent() wraps onto new lines.
  • JustifyContent(Start|Center|End|SpaceBetween|SpaceAround|SpaceEvenly) and AlignItems(Start|Center|End|Stretch).
  • Grid: paper.Grid("g").Columns(3). Rows take the tallest child, so grid children need a pixel or Auto height.
  • Overlay: paper.Overlay("o") stacks children on the same box.
  • Visible(false) keeps the element (and its state) but it takes no space, does not draw and is not hit.

Free placement:

  • PositionType(PositionType.SelfDirected) takes an element out of the flow and places it in its parent's content box (the screen for top level elements).
  • AnchorLeft/Right/Top/Bottom(px) are pixel offsets from that box. Anchoring both edges of an axis sets the size on that axis.
  • Anchors take pixels, percentages of that box, or both: AnchorLeft(UnitValue.Percentage(50, -100)) is 50 percent minus 100 pixels. Stretch or Auto anchors throw. Stretch margins also centre a fixed size element.
  • ClampToScreen() keeps a popup on screen.

Styling

  • Colors are Prowl.Vector.Color with floats 0 to 1. Integer arguments pick the byte overload, so new Color(1, 0, 0, 1) is nearly black. Write 1f.
  • BackgroundColor, BorderColor, BorderWidth, Rounded(r) or Rounded(tl, tr, br, bl), BoxShadow(x, y, blur, spread, color), BackdropBlur(radius), Opacity(0..1) (for the whole subtree).
  • Gradients replace the background color: BackgroundLinearGradient(x1, y1, x2, y2, c1, c2) with coordinates as fractions 0 to 1 of the element, also radial and box gradients.
  • BackgroundImage(texture) and Image(texture, tint, rotation, pivot, ImageScaleMode.Fit) take a Prowl Texture2D. The rotation is in degrees.
  • Text: .Text(string, FontFile), FontSize (default 16), TextColor (default white), Alignment(TextAlignment.MiddleCenter), Wrap(TextWrapMode.Wrap) (default no wrap), TextTruncate(), LineHeight, LetterSpacing. Plain Left, Center and Right alignments are top aligned, use the Middle ones to centre vertically. Text is not clipped unless .Clip().
  • Rich text: .Text(text, font).RichText() turns on tags like <b>bold</>, <i>, <#f80>colour</>, <size 1.5>, <wave>. Pass bold, italic and mono fonts to RichText for those styles.
  • Transforms are visual only and do not move layout: Translate(x, y), Scale(s), Rotate(degrees), TransformOrigin(0.5f, 0.5f).
  • Cursor(PaperCursor.Pointer) sets the mouse cursor while over the element.

State blocks style an element only while a state is on:

csharp
.BackgroundColor(idle)
.Hovered.BackgroundColor(hover).End()
.Active.BackgroundColor(pressed).End()
.Focused.BorderColor(accent).End()
.If(selected).BackgroundColor(accent).End()
  • Always close a block with `.End()`, otherwise every later setter is part of the block.
  • Hovered is on for every ancestor of the hovered element too. States come from the previous frame.
  • Other blocks: ParentFocused, Breakpoint(name), MinViewportWidth(px), Portrait, Landscape.

Reusable styles: static readonly StyleTemplate Card = new StyleTemplate().BackgroundColor(...).Rounded(8); then .Style(Card). Named styles exist (paper.DefineStyle, .Style("name")) but allocate on every use.

Animation

Paper runs on real time, so transitions and the helpers below keep animating while Time.TimeScale is 0, and a pause menu can fade in.

  • .Transition(GuiProp.BackgroundColor, seconds, Easing.CubicOut) animates a property toward whatever value is declared this frame. Declare it every frame. Easings are in Prowl.Vector.Easing. Width, margins and other layout values animate too.
  • Transforms snap back unless the resting value is declared. Write .Scale(1f).Active.Scale(0.95f).End(), not only the Active part.
  • Elements snap on the frame they first appear.
  • Helpers on paper return animated values: AnimateBool(open, 0.25f, Easing.CubicInOut) (0 to 1), AnimateFloat(target, speed), AnimateSpring, AnimateColor, OneShot(trigger), Pulse(period), Shake(trigger, intensity, decay, frequency). They keep state on the current parent, keyed by the calling line. At the top level of OnGui that parent is the root, shared by every component, so prefer calling them inside the `Enter` scope of your own element that exists every frame, and pass id: when calling one from a loop or helper.

Events

EventFires
OnPress, OnRelease, OnHeldleft button down, up, every held frame. The pressed element keeps getting them when the pointer leaves
OnClickrelease over the element that was pressed, if it did not become a drag
OnDoubleClick, OnRightClicksecond press within 0.25s, right button press
OnDragStart, OnDragging, OnDragEndafter 5 pixels of movement. e.Delta this frame, e.TotalDelta
OnScrolle.Delta
OnHover, OnEnter, OnLeavepointer over, entering, leaving
OnKeyPressed, OnTextInput, OnFocusChangefocused element only
OnPostLayout((handle, rect) => ...)the final rect, every frame
  • Click events carry PointerPosition, RelativePosition (from the element's top left), NormalizedPosition, ElementRect and Button.
  • Clicks, drags and scrolls bubble to every ancestor. A button inside a clickable row fires both. Call e.StopPropagation(), or .StopEventPropagation() on the element.
  • Every element is hit tested and focusable by default, even with no handlers. IsNotInteractable() lets the pointer through that element, but its children stay clickable, so use it on HUD containers. IsNotFocusable() opts out of focus. Opacity(0) still takes the pointer.
  • Handlers add up: calling .OnClick twice adds two handlers.
  • Tab order: .TabIndex(0) makes an element reachable with Tab.
  • paper.SetFocus(handle), paper.ClearFocus(). Clicking empty space does not clear focus.
  • Paper never blocks Prowl's game input. paper.WantsCapturePointer (pointer over any interactable element) and paper.WantsCaptureKeyboard (a text field is focused) only report it. Store the Paper from OnGui in a field and check them in Update.

Widgets

Paper has no buttons, sliders, toggles, dropdowns, tabs, tooltips or scroll views. Build them from boxes, as in the examples below. It does have:

  • .TextField(value, font, onChange, textColor, placeholder, placeholderColor) and .TextArea(...) on any box. Write the new value back to your field in onChange. While focused the field keeps its own text. Font size comes from .FontSize() and colour from the arguments, .TextColor() does nothing. Enter does not submit, add .OnKeyPressed for that. TextInputSettings adds MaxLength, ReadOnly, MaskChar, CharFilter and more.
  • MarkdownBuilder (namespace Prowl.PaperUI.Markdown) renders markdown. Keep the builder in a field.
  • F12 toggles Paper's dev tools in a running game.

Popups and layers

  • Siblings draw in order, so later ones are on top and get hit first.
  • .Layer(Layer.Overlay) or .Layer(Layer.Topmost) draws above everything lower and escapes the clipping of ancestors. Children go with it.
  • A dropdown or tooltip is a child of its trigger with PositionType(PositionType.SelfDirected).Position(0, triggerHeight).Layer(Layer.Topmost).ClampToScreen().StopEventPropagation(), shown while a field says it is open.

Scrolling

This is the start of the file. Read the full SKILL.md on GitHub.

Security

  • Security scanPassed, 90 of 100Pattern checks on the skill's files.
  • Model reviewPassed

Quality evaluation

  • Task evaluationNot runNo evaluation yet. Run one in Skill Studio.

Source

Popularity

7-day trend score
0

Not refreshed yet.

Works with

Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.

  • Claude Code
  • Cursor
  • GitHub Copilot
  • Windsurf
  • Codex
  • Gemini CLI
  • Cline
  • Amp
  • Roo Code
  • Goose
  • Continue
  • Kilo Code
  • Junie
  • Trae
  • OpenHands
  • Qwen Code
  • AI Agent
  • Tabnine Chat
  • Sourcegraph Cody
  • Amazon Q
  • Firebase Genkit
  • Sweep
  • Devin
  • Phind
  • Mentat
  • Claude Desktop
  • Aider
  • GPT Pilot
  • Plandex
  • bolt.new
  • v0
  • Replit Agent
  • Anse
  • TypingMind
  • BetterChatGPT
  • LibreChat
  • Warp AI
  • Pieces
  • Fig AI
Embed badge
vskill VERIFIED badge
[![vskill VERIFIED](https://verified-skill.com/api/v1/skills/prowlengine/prowl/prowl-paper/badge)](https://verified-skill.com/skills/prowlengine/prowl/prowl-paper)

Something wrong with this skill or its page? Tell us.