diff --git a/.claude/commands/take-issue.md b/.claude/commands/take-issue.md index 018f980..35aa980 100644 --- a/.claude/commands/take-issue.md +++ b/.claude/commands/take-issue.md @@ -3,22 +3,27 @@ description: Implements an issue from the backlog argument-hint: [issue-id] --- you will, in this order: + 1. use gh cli to read the issue #$1. 2. ensure all issues required before working on this one are resolved. + 2.1 if not, suggest the first unresolved dependency issue to be worked on first and stop here. -3. check if the issue has a size label. + +1. check if the issue has a size label. + 3.1 if not, assess the complexity and add one of the following labels: - * 'size: xs' - Less than half a day - * 'size: s' - Half a day - * 'size: m' - One day - * 'size: l' - Two days (broken into subtasks) - * 'size: xl' - More than two days (broken into subtasks) -4. if the size is 'l' or 'xl', check if the issue is already broken into sub-tasks. +* 'size: xs' - Less than half a day +* 'size: s' - Half a day +* 'size: m' - One day +* 'size: l' - Two days (broken into subtasks) +* 'size: xl' - More than two days (broken into subtasks) +1. if the size is 'l' or 'xl', check if the issue is already broken into sub-tasks. 4.1 if not, break the issue into smaller sub-tasks and create new github issues for each sub-task, linking them to the original issue, advise to work on the first sub-task to be worked on and stop here. 4.2 if yes, check each sub taks has its own github issue. 4.2.1 if not, create the missing github sub task issues and stop here. 4.2.2 if yes, advise to work on the first sub-task to be worked on and stop here. -5. if the size is 'xs', 's' or 'm', create a branch named after the issue's id and title. -6. delegate the implementation to a subagent. -7. iterate with the subagent until the issue's requirements are met. -8. create a pull request for the implementation. + +2. if the size is 'xs', 's' or 'm', create a branch named after the issue's id and title. +3. delegate the implementation to a subagent. +4. iterate with the subagent until the issue's requirements are met. +5. create a pull request for the implementation. diff --git a/.mise.toml b/.mise.toml index 7e2262a..ee34837 100644 --- a/.mise.toml +++ b/.mise.toml @@ -1,5 +1,6 @@ [tools] cmake = "3.28.6" +"github:rvben/rumdl" = "latest" hk = "1.18.3" ninja = "1.13.1" pkl = "0.29.1" @@ -145,6 +146,20 @@ run = """ clang-format -i """ +[tasks.markdown-check] +description = "Check markdown files for linting issues" +depends = ["check-deps"] +run = """ +rumdl check +""" + +[tasks.markdown-fix] +description = "Auto-fix markdown linting issues" +depends = ["check-deps"] +run = """ +rumdl fmt +""" + [tasks.demo] description = "Build and run the Prong demo application" depends = ["build-examples"] diff --git a/CLAUDE.md b/CLAUDE.md index e5c0529..fe064fe 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -7,6 +7,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co Prong is a modern C++20 UI framework from BombFork designed for high-performance applications. It's header-mostly, uses CRTP (Curiously Recurring Template Pattern) for zero-cost abstractions, and is both renderer-agnostic and window-agnostic. **Key Design Principles:** + - Header-mostly architecture (minimal implementation files) - CRTP-based component system for compile-time polymorphism - Zero dependencies for core functionality @@ -16,6 +17,7 @@ Prong is a modern C++20 UI framework from BombFork designed for high-performance ## Build Commands ### Standard Build + ```bash mkdir build && cd build cmake .. -DPRONG_BUILD_EXAMPLES=ON -DPRONG_BUILD_TESTS=ON @@ -23,10 +25,12 @@ cmake --build . ``` ### Build Options + - `PRONG_BUILD_EXAMPLES` - Build example applications (default: ON) - `PRONG_BUILD_TESTS` - Build unit tests (default: ON) ### Installation + ```bash sudo cmake --install . ``` @@ -56,6 +60,7 @@ This eliminates virtual function call overhead while maintaining clean abstracti ### Component System The `Component` class (`include/bombfork/prong/core/component.h`) is the foundation: + - **Parent/child relationships**: Components form a tree structure with automatic ownership via `std::unique_ptr` - **Event propagation**: Events flow down from parent to children, with children handling first (topmost rendered components get priority) - **Coordinate systems**: Uses a relative coordinate system with caching - see detailed section below @@ -66,6 +71,7 @@ The `Component` class (`include/bombfork/prong/core/component.h`) is the foundat ### Coordinate System Prong uses a **relative coordinate system** where child positions are always relative to their parent's origin. This design provides several benefits: + - **Intuitive positioning**: Child components don't need to know their parent's position - **Automatic updates**: Moving a parent automatically moves all children - **Layout flexibility**: Layout managers work with local coordinates only @@ -160,6 +166,7 @@ The `Component::handleEvent()` method automatically converts global screen coord #### Cache Invalidation The global coordinate cache is automatically invalidated when: + - A component's position changes via `setPosition()` or `setBounds()` - A component is added to a new parent - Cache invalidation automatically cascades to all descendants @@ -177,6 +184,7 @@ You should never need to manually invalidate the cache. ### Renderer Abstraction The `IRenderer` interface (`include/bombfork/prong/rendering/irenderer.h`) provides: + - Frame lifecycle: `beginFrame()`, `endFrame()`, `present()` - Drawing primitives: `drawRect()`, `drawSprite()`, `drawText()` - Batching support: `drawSprites()` for efficient multi-sprite rendering @@ -187,6 +195,7 @@ All rendering must go through the `IRenderer` interface. Components receive rend ### Event System Prong uses a **hierarchical event propagation model** where events flow through the component tree: + - **Event handling**: Components override `handleEventSelf()` to respond to events - **Automatic propagation**: Events propagate from parent to children automatically via `Component::handleEvent()` - **Hit testing**: Uses `Component::containsEvent()` for positional event checking @@ -203,6 +212,7 @@ Components can specify how they respond to parent resize events through resize b #### Unified Resize Behavior The `ResizeBehavior` enum provides unified control over both axes: + - `FIXED`: Keep original size and position (default) - `SCALE`: Scale proportionally with parent - `FILL`: Fill available parent space @@ -215,6 +225,7 @@ component->setResizeBehavior(Component::ResizeBehavior::FILL); #### Per-Axis Resize Behavior For more control, use `AxisResizeBehavior` to set independent horizontal and vertical behavior: + - `AxisResizeBehavior::FIXED`: Keep original size on this axis - `AxisResizeBehavior::SCALE`: Scale proportionally with parent on this axis - `AxisResizeBehavior::FILL`: Fill available parent space on this axis @@ -226,10 +237,12 @@ panel->setAxisResizeBehavior(Component::AxisResizeBehavior::FIXED, ``` **Important**: When using FlexLayout, per-axis behavior is usually preferred: + - For horizontal FlexLayout (ROW): Use `FIXED` or `SCALE` horizontally (let FlexLayout control width), `FILL` vertically - For vertical FlexLayout (COLUMN): Use `FILL` horizontally, `FIXED` or `SCALE` vertically (let FlexLayout control height) Example from demo app: + ```cpp // Left panel in horizontal FlexLayout: fixed width (200px), fills height leftPanel->setAxisResizeBehavior(Component::AxisResizeBehavior::FIXED, @@ -243,6 +256,7 @@ centerPanel->setAxisResizeBehavior(Component::AxisResizeBehavior::FILL, #### Responsive Constraints Combine resize behavior with constraints for bounded resizing: + ```cpp Component::ResponsiveConstraints constraints; constraints.minWidth = 200; @@ -255,6 +269,7 @@ component->setConstraints(constraints); ### Layout System Layout managers (`include/bombfork/prong/layout/`) use CRTP and provide: + - **FlexLayout**: Flexbox-inspired with direction, justify, align, gap, and grow/shrink factors - **GridLayout**: CSS Grid-inspired with rows/columns and gaps - **DockLayout**: Docking panels (top, bottom, left, right, center fill) @@ -262,12 +277,14 @@ Layout managers (`include/bombfork/prong/layout/`) use CRTP and provide: - **FlowLayout**: Automatic wrapping layout Layout managers implement: + - `measureLayout()`: Calculate required space for components - `layout()`: Position and size components within available space ### Theming System Located in `include/bombfork/prong/theming/`: + - **ThemeManager**: Singleton managing global themes, thread-safe - **Color**: RGBA color with utility methods and named constants @@ -276,6 +293,7 @@ Themes use semantic color names. ## Implementation Files Only these modules require `.cpp` files (all in `src/`): + - `core/coordinate_system.cpp` - World ↔ Screen transformations - `core/async_callback_queue.cpp` - Thread-safe callback management - `theming/theme_manager.cpp` - Global theme state @@ -285,6 +303,7 @@ All UI components (Button, Panel, ListBox, TextInput, Dialog, Toolbar, Viewport) ## Namespace Structure All code lives under `bombfork::prong` with subnamespaces: + - `bombfork::prong::core` - Component base classes - `bombfork::prong::components` - All UI widgets (Button, Panel, ListBox, TextInput, Dialog, Toolbar, Viewport) - `bombfork::prong::layout` - Layout managers @@ -300,10 +319,12 @@ All code lives under `bombfork::prong` with subnamespaces: The `TextInput` component requires two platform-specific interfaces for full functionality: **IClipboard** (`include/bombfork/prong/events/iclipboard.h`): + - Provides clipboard access for copy/paste operations - Must be injected via `textInput->setClipboard(clipboard)` **IKeyboard** (`include/bombfork/prong/events/ikeyboard.h`): + - Converts platform-specific key codes to Prong's agnostic `Key` enum - Must be injected via `textInput->setKeyboard(keyboard)` @@ -355,6 +376,7 @@ The mock implementations provide simple in-memory storage for testing without re ## C++20 Features The codebase requires C++20 and uses: + - Concepts for template constraints - Ranges for algorithms - Three-way comparison operator (spaceship) @@ -366,6 +388,7 @@ GCC 10+, Clang 13+, or MSVC 2019+ required. ## CMake Integration When used as a library via FetchContent: + ```cmake FetchContent_Declare(prong GIT_REPOSITORY https://github.com/bombfork/prong.git diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index acfc534..d261da7 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -56,6 +56,7 @@ mise build ``` This command: + - Configures CMake with Ninja generator - Builds the core library - Builds all tests @@ -129,6 +130,7 @@ This builds and executes all unit tests. All tests must pass before submitting a Header dependencies are verified automatically by IWYU and is integrated to cmake and git hooks. IWYU ensures that: + - Headers only include what they directly use - No transitive include dependencies - Header files are self-contained @@ -138,6 +140,7 @@ The git hooks will automatically check this before commits. ### Demo Application Testing The demo app (`examples/demo_app/`) is used for: + - UX testing and validation - Manual feature testing - Gathering user feedback @@ -160,6 +163,7 @@ Prong follows a **header-only architecture** where possible. Only add `.cpp` imp - Platform-specific code requiring conditional compilation **Existing `.cpp` files:** + - `src/core/coordinate_system.cpp` - World ↔ Screen transformations - `src/core/async_callback_queue.cpp` - Thread-safe callback management - `src/theming/theme_manager.cpp` - Global theme state @@ -193,6 +197,7 @@ class LayoutManager { Prong uses a **relative coordinate system** where child positions are always relative to their parent: **DO:** + ```cpp // Position children relative to parent panel->setPosition(100, 50); // Panel at (100, 50) in parent space @@ -201,6 +206,7 @@ panel->addChild(std::move(button)); // Button now at global (120, 60) ``` **DON'T:** + ```cpp // Never use global coordinates when positioning children int globalX, globalY; @@ -209,6 +215,7 @@ button->setPosition(globalX + 20, globalY + 10); // WRONG! ``` **Rendering:** + - Use `getGlobalX()` and `getGlobalY()` in `render()` methods - Global coordinates are cached automatically - Never manually track or store global positions @@ -376,6 +383,7 @@ mise demo ### 4. Commit Git hooks will automatically: + - Run clang-format on changed files - Run iwyu checks - Validate commit message format @@ -405,6 +413,7 @@ Then create a Pull Request on GitHub with: **Title:** Clear, concise description of changes **Description should include:** + - Summary of changes - Motivation and context - Related issue numbers (e.g., "Fixes #123", "Closes #456") @@ -413,6 +422,7 @@ Then create a Pull Request on GitHub with: - Any breaking changes **Example PR description:** + ```markdown ## Summary Add responsive resize behavior to Panel component to support dynamic layouts. @@ -449,6 +459,7 @@ Prong uses [hk](https://github.com/jdx/hk) to manage git hooks. The configuratio ### Pre-commit Hook The pre-commit hook automatically: + - Runs `clang-format` on all staged C++ files (`**/*.cpp`, `**/*.h`) - Checks formatting with `mise run format-check` - Automatically fixes formatting issues with `mise run format` when `fix = true` diff --git a/README.md b/README.md index 1430ffe..3036d72 100644 --- a/README.md +++ b/README.md @@ -101,7 +101,7 @@ For detailed information, see [docs/coordinate_system.md](docs/coordinate_system ### Core Components -``` +```text bombfork::prong:: ├── core/ # Base component system │ ├── Scene # Root scene component (entry point for UI hierarchy) @@ -315,6 +315,7 @@ void mouseButtonCallback(GLFWwindow* w, int btn, int action, int mods) { ``` **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 diff --git a/docs/api/QUICK_REFERENCE.md b/docs/api/QUICK_REFERENCE.md index e616556..31966e7 100644 --- a/docs/api/QUICK_REFERENCE.md +++ b/docs/api/QUICK_REFERENCE.md @@ -5,7 +5,9 @@ Condensed reference for all Prong APIs. See individual files for detailed docume ## Core Classes ### Component + Base class for all UI elements. + ```cpp class Component { virtual void update(double deltaTime) = 0; @@ -19,7 +21,9 @@ class Component { ``` ### Scene + Root container and event coordinator. + ```cpp class Scene : public Component { Scene(IWindow* window, IRenderer* renderer); @@ -30,7 +34,9 @@ class Scene : public Component { ``` ### ComponentBuilder + Fluent API for component construction. + ```cpp auto component = create(/* constructor args */) .withSize(width, height) @@ -40,7 +46,9 @@ auto component = create(/* constructor args */) ``` ### Event + Unified event structure. + ```cpp struct Event { enum class Type { MOUSE_PRESS, MOUSE_RELEASE, MOUSE_MOVE, MOUSE_SCROLL, KEY_PRESS, KEY_RELEASE, CHAR_INPUT }; @@ -56,6 +64,7 @@ struct Event { ## UI Components ### Button + ```cpp auto button = create