prowlengine/prowl/prowl-paper
- community
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-paperSKILL.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.
The frame model
- Each frame Prowl calls
BeginFrame, then every component'sOnGui(Paper paper), thenEndFrame. 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
OnGuireturned and after layout. Callbacks only change state. The next frame'sOnGuireads 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
PushIDscope, 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 anint idparameter 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)andpaper.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.Autofits 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
AlignItemsorAlignSelf. - `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 orHeight(UnitValue.Auto). - Sizes include padding. Padding shrinks the area for text and children.
MinWidth,MaxWidth,MinHeight,MaxHeightandPaddingaccept 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).Margintakes the same orders.Left,Right,Top,Bottomset 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 usepaper.Row.ReverseLayout().WrapContent()wraps onto new lines.JustifyContent(Start|Center|End|SpaceBetween|SpaceAround|SpaceEvenly)andAlignItems(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.Colorwith floats 0 to 1. Integer arguments pick the byte overload, sonew Color(1, 0, 0, 1)is nearly black. Write1f. BackgroundColor,BorderColor,BorderWidth,Rounded(r)orRounded(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)andImage(texture, tint, rotation, pivot, ImageScaleMode.Fit)take a ProwlTexture2D. 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. PlainLeft,CenterandRightalignments are top aligned, use theMiddleones 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 toRichTextfor 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:
- 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 inProwl.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
paperreturn 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 passid:when calling one from a loop or helper.
Events
| Event | Fires | |
|---|---|---|
OnPress, OnRelease, OnHeld | left button down, up, every held frame. The pressed element keeps getting them when the pointer leaves | |
OnClick | release over the element that was pressed, if it did not become a drag | |
OnDoubleClick, OnRightClick | second press within 0.25s, right button press | |
OnDragStart, OnDragging, OnDragEnd | after 5 pixels of movement. e.Delta this frame, e.TotalDelta | |
OnScroll | e.Delta | |
OnHover, OnEnter, OnLeave | pointer over, entering, leaving | |
OnKeyPressed, OnTextInput, OnFocusChange | focused element only | |
OnPostLayout((handle, rect) => ...) | the final rect, every frame |
- Click events carry
PointerPosition,RelativePosition(from the element's top left),NormalizedPosition,ElementRectandButton. - 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
.OnClicktwice 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) andpaper.WantsCaptureKeyboard(a text field is focused) only report it. Store the Paper from OnGui in a field and check them inUpdate.
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 inonChange. 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.OnKeyPressedfor that.TextInputSettingsaddsMaxLength,ReadOnly,MaskChar,CharFilterand more.MarkdownBuilder(namespaceProwl.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
- SKILL.md in the repositoryCheckingProwl.Editor/Resources/Agent/Skills/prowl-paper/SKILL.md
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
[](https://verified-skill.com/skills/prowlengine/prowl/prowl-paper)