Important
Paper is a very new library. While an earlier iteration is actively used within the Prowl Game Engine, this specific library hasn't yet established a track record of reliability in production environments.
Paper is an open-source, MIT-licensed immediate-mode UI library built for the Prowl Game Engine. It provides a lightweight, flexible way to create highly reactive user interfaces with minimal effort.
Prowl.Demo.mp4
Paper follows the immediate-mode GUI (IMGUI) paradigm, where UI elements are created and configured on every frame. Unlike retained-mode systems, it doesn't maintain persistent widget objects, making code simpler and more direct. Here's a basic example:
// Create a simple button
Paper.Box("MyButton")
.Size(100)
.BackgroundColor(Color.ForestGreen)
.Rounded(8)
.Text(Text.Center("Click Me", myFont, Color.White))
.OnClick((rect) => Console.WriteLine("Button clicked!"));
Which produces this button you can click on:
Unlike Retained, you don't need to find elements or store references to change values. For example, If you want to animate the roundness it is as simple as
.Rounded(YourAnimatedFloat)
- General:
- Cross-Platform! Windows, Linux & Mac!
- Immediate Mode Architecture
- 100% C#!
- Highly Portable
- Very easy to integrate
- OpenTK Example
- Raylib Example (Less than 500 loc including shaders!)
- With more to come!
- DirectX, Web, Unity
- Fluent API
- Flexible Layout System
- Rows, Columns & Custom Positioning
- Pixel, Percentage, Stretch or Auto for Positioning and Sizing
- A Powerful Built-in Animation System
- Many built-in Easing functions
- Easily provide your own Easing functions
- Automatic transitions for each individual Property of any node at any time
- Rich Event Handling
- Comprehensive Mouse & Keyboard support
- Parent-Child Event Bubbling
- OnClick, OnDragStart, OnHover and many more
- Transformations
- Scale, Rotate, Translate & Skew any Element
- Vector Graphics
- Highly Performant
- Hardware accelerated
- Anti-Aliased
- Scissoring
- MoveTo, LineTo, CurveTo, Fill, Stroke, etc
- Draw custom shapes at any time any where
dotnet add package Prowl.Paper
You will need a Renderer, Theres examples in the repository under Samples for OpenTK and Raylib, with more to come.
Paper.Initialize(yourRenderer, screenWidth, screenHeight);
// Resize with
Paper.SetResolution(screenWidth, screenHeight);
// Load a font
var fontSystem = new FontSystem();
fontSystem.AddFont(File.ReadAllBytes("path/to/font.ttf"));
var myFont = fontSystem.GetFont(24);
Paper is very easy to use, Simply call BeginFrame, Do your UI then call EndFrame
// In your main loop:
void RenderUI()
{
// Begin the UI frame
Paper.BeginFrame(deltaTime);
// Define your UI
using (Paper.Column("MainContainer")
.BackgroundColor(240, 240, 240)
.Enter())
{
// A header
using (Paper.Box("Header")
.Height(60)
.BackgroundColor(50, 120, 200)
.Text(Text.Center("My Application", myFont, Color.White))
.Enter()) { }
// Content area
using (Paper.Row("Content").Enter())
{
// Sidebar
Paper.Box("Sidebar")
.Width(200)
.BackgroundColor(220, 220, 220);
// Main content
Paper.Box("MainContent");
}
}
// End the UI frame
Paper.EndFrame();
}
That should result in the following UI:
Paper provides a mighty layout engine based on the Morphorm library.
// Row container (horizontal layout)
using (Paper.Row("MyRow")
.Enter())
{
// Children will be arranged horizontally
}
// Column container (vertical layout)
using (Paper.Column("MyColumn")
.Enter())
{
// Children will be arranged vertically
}
// Custom positioning
using (Paper.Box("CustomPositionedElement")
.PositionType(PositionType.SelfDirected) // Absolute positioning
.Left(100)
.Top(50)
.Enter())
{
// This element is positioned exactly at (100, 50) relative to its parent
}
Paper provides a very powerful yet simple way to both draw and animate your UI
Tip
Styling is executed in order of calls, so in this case, if YourCondition is true, even if it's being hovered, If(YourCondition) will take priority since it comes after.
Also .Hovered, .Active and so on, are all just automatic versions of .If() like .If(Paper.IsNodeHovered(TheNode)).
// Basic styling
using (Paper.Box("StyledElement")
.BackgroundColor(Color.Blue)
.BorderColor(Color.White)
.BorderWidth(2)
.Rounded(8)
.Enter()) { }
// State-based styling
using (Paper.Box("InteractiveElement")
.BackgroundColor(Color.Gray)
.Hovered
.BackgroundColor(Color.LightGray)
.End()
.Active
.BackgroundColor(Color.DarkGray)
.End()
.If(YourCondition)
.BackgroundColor(Color.Gray)
.End()
.Enter()) { }
// Transitions/animations
using (Paper.Box("AnimatedElement")
.BackgroundColor(Color.Red)
.Hovered
.BackgroundColor(Color.Green)
.End()
.Transition(GuiProp.BackgroundColor, 0.3, Paper.Easing.SineInOut)
.Enter()) { }
// Various built-in easing functions
Paper.Easing.Linear
Paper.Easing.EaseIn
Paper.Easing.EaseOut
Paper.Easing.EaseInOut
Paper.Easing.CubicIn
Paper.Easing.ElasticOut
// ... and many more
Events will bubble upwards through their parents. So hovering a child will also call OnHover for the parent.
Paper.Box("InteractiveElement")
.OnClick((rect) => HandleClick())
.OnHover((rect) => ShowTooltip())
.OnEnter((rect) => PlayHoverSound())
.OnLeave((rect) => HideTooltip())
.OnDragStart((rect) => StartDragging())
.OnDragging((start, rect) => UpdateDragPosition(start))
.OnDragEnd((start, total, rect) => FinishDragging())
8000
.OnScroll((delta, rect) => Scroll(delta))
To integrate Paper's input system with your project, you need to forward input events from your project to PaperUI. Here's a simplified example using Raylib:
// Call this every frame before you draw your UI
void UpdatePaperUIInput()
{
// Update mouse position
Paper.SetPointerPosition(mousePos);
// Forward mouse button events
if (IsMouseButtonPressed(MouseButton.Left))
Paper.SetPointerState(PaperMouseBtn.Left, mousePos, true);
if (IsMouseButtonReleased(MouseButton.Left))
Paper.SetPointerState(PaperMouseBtn.Left, mousePos, false);
// Repeat for Right & Middle
// Forward mouse wheel events
float wheelDelta = GetMouseWheelMove();
if (wheelDelta != 0)
Paper.SetPointerWheel(wheelDelta);
// Forward text input
int key = GetCharPressed();
while (key > 0)
{
Paper.AddInputCharacter(((char)key).ToString());
key = GetCharPressed();
}
// Forward key states
// keyMappings being an array storing the mapping from a PaperKey enum to your Projects Key Enum
foreach (var keyMapping in keyMappings)
{
if (IsKeyPressed(keyMapping.EngineKey))
Paper.SetKeyState(keyMapping.PaperKey, true);
else if (IsKeyReleased(keyMapping.EngineKey))
Paper.SetKeyState(keyMapping.PaperKey, false);
}
}
There's so much more I couldn't possibly fit it all into this Readme file. A more complete documentation & tutorial will be coming in the near future!
Check our Contributing guide to see how to be part of this team.
Distributed under the MIT License. See LICENSE for more information.