Sharp, modern C++20 UI framework from BombFork
Prong is a header-mostly, CRTP-based UI framework designed for high-performance applications requiring flexible layouts, theming support, and renderer-agnostic rendering. Built with modern C++20, it provides zero-cost abstractions while maintaining clean, expressive APIs.
- 🔱 CRTP-Based Component System - Zero-overhead polymorphism with compile-time dispatch
- 📐 Flexible Layout Managers - Flex, Grid, Dock, Stack, and Flow layouts
- 🎨 Advanced Theming - Hot-reload, semantic colors, multiple themes
- 🖼️ Renderer-Agnostic - Works with OpenGL, Vulkan, or custom renderers
- 🪟 Window-Agnostic - GLFW, SDL, or native window APIs
- ⚡ Zero Dependencies - Header-only where possible, minimal implementation files
- 🚀 Modern C++20 - Concepts, ranges, and latest language features
- 📦 CMake Integration - Easy integration via FetchContent or find_package
include(FetchContent)
FetchContent_Declare(
prong
GIT_REPOSITORY https://github.com/bombfork/prong.git
GIT_TAG v1.0.0
)
FetchContent_MakeAvailable(prong)
target_link_libraries(your_app PRIVATE bombfork::prong)Prong uses a Scene-based architecture where the Scene is the root of your UI hierarchy:
#include <bombfork/prong/core/scene.h>
#include <bombfork/prong/components/button.h>
#include <bombfork/prong/components/panel.h>
using namespace bombfork::prong;
// Create window and renderer (use your preferred implementations)
IWindow* window = createMyWindow();
IRenderer* renderer = createMyRenderer();
// Create scene (root of UI hierarchy)
Scene scene(window, renderer);
scene.attach();
// Build UI - components automatically inherit renderer from parent
auto panel = std::make_unique<Panel<>>();
panel->setPosition(50, 50);
panel->setSize(300, 200);
auto button = std::make_unique<Button<>>("Save");
button->setClickCallback([]() {
std::cout << "Save clicked!" << std::endl;
});
panel->addChild(std::move(button));
scene.addChild(std::move(panel));
// Main loop
while (!window->shouldClose()) {
// Convert window events to Event structs and pass to scene
// (in window callbacks: scene.handleEvent(event))
scene.updateAll(deltaTime);
scene.renderAll();
scene.present();
}
scene.detach();Prong uses a relative coordinate system where child components are positioned relative to their parent's origin. This provides:
- Intuitive positioning - Children don't need to know their parent's position
- Automatic updates - Moving a parent automatically moves all children
- Performance - Global screen coordinates are cached automatically
// Position children relative to parent
panel->setPosition(100, 200); // Panel at screen position (100, 200)
button->setPosition(50, 75); // Button at (50, 75) relative to panel
panel->addChild(std::move(button)); // Button now at screen position (150, 275)For detailed information, see docs/coordinate_system.md.
bombfork::prong::
├── core/ # Base component system
│ ├── Scene # Root scene component (entry point for UI hierarchy)
│ ├── Component # CRTP base class with relative coordinates
│ ├── CoordinateSystem # World ↔ Screen transformations (for viewports)
│ └── AsyncCallbackQueue # Thread-safe callback management
├── components/ # Reusable UI widgets
│ ├── Button # Clickable button
│ ├── Panel # Container panel
│ ├── ListBox # Scrollable list
│ └── TextInput # Text input field
├── layout/ # Layout managers (CRTP)
│ ├── FlexLayout # Flexbox-inspired layout
│ ├── GridLayout # CSS Grid-inspired layout
│ ├── DockLayout # Docking panel layout
│ └── StackLayout # Vertical/horizontal stacking
├── theming/ # Theming system
│ ├── Color # Color representation
│ ├── ThemeManager # Global theme management
│ └── AdvancedTheme # Advanced theme with hot-reload support
├── events/ # Event handling
│ ├── Event # Unified event structure
│ ├── IClipboard # Clipboard abstraction interface
│ ├── IKeyboard # Keyboard abstraction interface
│ └── IWindow # Window abstraction interface
├── generic/ # Generic UI components
│ ├── Dialog # Modal dialogs
│ ├── Toolbar # Toolbars
│ └── Viewport # Scrollable viewport
└── rendering/ # Rendering abstractions
└── IRenderer # Renderer interface
Prong uses the Curiously Recurring Template Pattern (CRTP) for zero-cost abstraction:
template<typename DerivedT>
class FlexLayout : public LayoutManager<DerivedT> {
// Layout logic with compile-time polymorphism
void layout(std::vector<Component*>& children) {
auto* derived = static_cast<DerivedT*>(this);
// Use derived class methods without virtual dispatch
}
};
class MyFlexLayout : public FlexLayout<MyFlexLayout> {
// Custom flex layout behavior
};Prong works with any rendering backend through the IRenderer interface:
class MyOpenGLRenderer : public bombfork::prong::rendering::IRenderer {
public:
// Implement required methods
bool beginFrame() override { /* OpenGL setup */ }
void endFrame() override { /* OpenGL cleanup */ }
void drawRect(int x, int y, int w, int h, float r, float g, float b, float a) override {
// OpenGL rectangle drawing
}
// ... other methods
};Note: Prong's core is renderer-agnostic. Example renderer implementations (OpenGL) are provided in examples/adapters/ for demonstration purposes but are not part of the core framework.
Prong is window-library agnostic through the IWindow interface:
class MyGLFWAdapter : public bombfork::prong::events::IWindow {
GLFWwindow* window;
public:
void getSize(int& w, int& h) const override {
glfwGetWindowSize(window, &w, &h);
}
void* getNativeHandle() override { return window; }
// ... other methods
};Note: Example window adapters (GLFW) are provided in examples/adapters/ and examples/common/glfw_adapters/ for demonstration purposes. These are not part of the core framework - you implement the interfaces for your chosen windowing system.
Prong provides a flexible theming system through ThemeManager and AdvancedTheme:
using namespace bombfork::prong::theming;
// Register and use built-in themes (Light, Dark, High Contrast)
ThemeManager::getInstance().registerBuiltinThemes();
ThemeManager::getInstance().setCurrentTheme("dark");
ThemeManager::getInstance().setCurrentTheme("light");
ThemeManager::getInstance().setCurrentTheme("high-contrast");Create custom themes programmatically:
#include <bombfork/prong/theming/advanced_theme.h>
#include <bombfork/prong/theming/theme_manager.h>
// Create a custom theme
auto customTheme = std::make_unique<AdvancedTheme>("custom", "My Custom Theme");
// Define colors
customTheme->setColor("primary", Color(52, 152, 219)); // #3498db
customTheme->setColor("background", Color(44, 62, 80)); // #2c3e50
customTheme->setColor("text", Color(236, 240, 241)); // #ecf0f1
// Register and activate
ThemeManager::getInstance().registerTheme(std::move(customTheme));
ThemeManager::getInstance().setCurrentTheme("custom");
// Access colors
const Color& bg = ThemeManager::getInstance().getCurrentTheme()->getColor("background");Register callbacks to respond to theme changes:
ThemeManager::getInstance().addChangeCallback([](const ThemeChangeEvent& event) {
std::cout << "Theme changed from " << event.oldThemeId
<< " to " << event.newThemeId << std::endl;
// Update UI components as needed
});using namespace bombfork::prong::layout;
FlexLayout<MyApp> layout;
layout.setDirection(FlexDirection::ROW);
layout.setJustify(FlexJustify::SPACE_BETWEEN);
layout.setAlign(FlexAlign::CENTER);
layout.setGap(10.0f);
layout.layout(components, {800, 600});GridLayout<MyApp> grid;
grid.setColumns(3);
grid.setRows(2);
grid.setGap(5.0f, 5.0f);
grid.layout(components, {900, 600});DockLayout<MyApp> dock;
dock.dockTop(toolbar, 40); // Toolbar at top, 40px height
dock.dockBottom(statusBar, 25); // Status bar at bottom, 25px height
dock.dockLeft(sidebar, 200); // Sidebar on left, 200px width
dock.fillCenter(mainView); // Fill remaining spaceProng uses a hierarchical event system where events flow through the Scene and down the component tree:
#include <bombfork/prong/core/scene.h>
#include <bombfork/prong/core/event.h>
// Scene is the entry point for all window events
Scene scene(window, renderer);
// Add components
auto button = std::make_unique<Button<>>("Click Me");
button->setClickCallback([]() {
std::cout << "Clicked!\n";
});
scene.addChild(std::move(button));
// In your window callbacks, convert platform events to Prong Event structs
void mouseButtonCallback(GLFWwindow* w, int btn, int action, int mods) {
double mx, my;
glfwGetCursorPos(w, &mx, &my);
Event event;
event.type = (action == GLFW_PRESS) ? Event::Type::MOUSE_PRESS
: Event::Type::MOUSE_RELEASE;
event.localX = static_cast<int>(mx);
event.localY = static_cast<int>(my);
event.button = btn;
event.modifiers = mods;
scene.handleEvent(event); // Scene propagates to children
}Key Features:
- Scene is the entry point for all events from the window system
- Events automatically propagate to children with coordinate conversion
- Children rendered last (topmost) receive events first
- Components must be enabled and visible to receive events
- Override
handleEventSelf()in your components for custom event handling
See the examples/ directory for complete applications:
- hello_prong - Minimal button and panel example
- layout_demo - Demonstrates all layout managers
- theming_demo - Theme switching and customization
- custom_renderer - Implementing a custom renderer
# Clone repository
git clone https://github.com/bombfork/prong.git
cd prong
# Build with CMake
mkdir build && cd build
cmake .. -DPRONG_BUILD_EXAMPLES=ON -DPRONG_BUILD_TESTS=ON
cmake --build .
# Install (optional)
sudo cmake --install .For reproducible builds across different platforms, use the containerized build system with mise tasks:
# Build the Docker image
mise docker-build
# Build the library
mise docker-build-lib
# Build and run tests
mise docker-build-tests
mise docker-test
# Build examples
mise docker-build-examples
# Run clang-format
mise docker-format
# Open interactive shell
mise docker-shellThe mise Docker tasks provide a convenient, consistent interface and handle all volume mounts and image management automatically. See docs/docker.md for complete Docker build system documentation.
- C++20 compatible compiler (GCC 10+, Clang 13+, MSVC 2019+)
- CMake 3.14+
- clang-format (for code formatting)
- include-what-you-use (for header dependency checking)
- No runtime dependencies (header-only core)
- Docker 20.10+ with BuildKit support
- mise (for running Docker tasks)
MIT License - see LICENSE file for details.
Contributions are welcome! Please see CONTRIBUTING.md for guidelines.
See our GitHub Issues for planned features and enhancements. Notable goals include:
- Accessibility support (screen readers, keyboard navigation)
- Animation system
- Drag-and-drop framework
- Virtual scrolling for large lists
- Web backend (WebAssembly + WebGPU)
For the complete list and to suggest new features, visit the issue tracker.
Developed by the BombFork team for high-performance game tools and editors.
Prong - Sharp UI for sharp developers ⚡🔱