diff --git a/.agent/plans/layout_separation_float_plan.md b/.agent/plans/layout_separation_float_plan.md new file mode 100644 index 000000000..b655ee38d --- /dev/null +++ b/.agent/plans/layout_separation_float_plan.md @@ -0,0 +1,61 @@ +# UI Layout Phase 6: CSS Float & Clear Plan + +This document outlines the architectural plan for implementing CSS `float` and `clear` support within the decoupled layout system, leveraging the `Graphics::RichText` engine for mixed content formatting. + +**AGENT DIRECTIVE (CRITICAL):** You MUST compile and run the unit tests (`bin/unit_tests/eepp-unit_tests-debug`) after EVERY step. Do NOT proceed to the next step if there is even a 1-pixel difference in visual layout tests. Take a git stash snapshot (`git stash push -m "Phase 6.X passed" && git stash apply`) upon passing a step to keep a checkpoint while continuing to work. **If you need to restore a stash, use `git stash apply` instead of `git stash pop` so the stable snapshot is never lost.** + +--- + +## IMPLEMENTATION HAZARDS (READ BEFORE CODING) +1. **Keyword Collision:** `Float` is a C++ type (`typedef float Float`). When defining the CSS enum, you MUST name it `CSSFloat` to avoid compiler collisions. +2. **Y-Coordinate Interleaving:** `RichText::updateLayout` currently breaks lines independently of their Y position, and only computes Y coordinates *after* all lines are formed. Because floating elements alter the available horizontal width at specific Y coordinate ranges, you will have to calculate `curY` *during* the block iteration, keeping track of active floats to restrict `curX` and `maxWidth`. +3. **Out-Of-Flow Precedence:** Floating elements are *not* out-of-flow in the same way `position: absolute` elements are. `absolute` elements are ignored by layouters, whereas `float` elements strictly participate in and influence the block formatting context (they take up space and push text around). Do not mark them as `isOutOfFlow() = true` in `UIRichText::rebuildRichText`. + +--- + +## Phase 6: Float and Clear implementation + +**Step 6.1: CSS Enums and Properties** +- In `csslayouttypes.hpp`, define: + ```cpp + enum class CSSFloat { None, Left, Right }; + enum class CSSClear { None, Left, Right, Both }; + ``` + And their helper parsing functions (`CSSFloatHelper::fromString`, etc.). +- In `propertydefinition.hpp`, ensure `PropertyId::Float` and `PropertyId::Clear` exist (if not, add them, avoiding conflicts). +- In `UIHTMLWidget`, add `mFloat` and `mClear` members (defaulting to `None`). +- In `UIHTMLWidget::applyProperty`, parse the `Float` and `Clear` properties. Call `notifyLayoutAttrChange()` when they change. +- **Validation:** Compile and run all tests. Must pass. (Snapshot) + +**Step 6.2: Extend RichText API** +- In `include/eepp/graphics/richtext.hpp`, update `RichText::addCustomSize`: + ```cpp + void addCustomSize( const Sizef& size, bool isBlock, CSSFloat floatType = CSSFloat::None, CSSClear clearType = CSSClear::None ); + ``` +- Update `CustomBlock` struct to store `floatType` and `clearType`. +- In `UIRichText::rebuildRichText`, extract `getCSSFloat()` and `getCSSClear()` from the child widget (defaulting to `None` if the child isn't an HTML widget). Pass these to `richText.addCustomSize`. +- **Validation:** Compile and run all tests. (Snapshot) + +**Step 6.3: Core RichText Layout Algorithm (The Tricky Part)** +- In `RichText::updateLayout()`, introduce Y-coordinate awareness during the main loop: + - Create tracking lists: `std::vector leftFloats; std::vector rightFloats;` + - Introduce `Float curY = 0;` + - Before placing *any* block (text or custom), process `clear`: if the block has `clear: left`, advance `curY` past the `bottom` of all `leftFloats`. (Same for `right` and `both`). Reset `curX` and push a new `RenderParagraph` if `curY` changed. + - Compute `availableLeft(curY)` and `availableRight(curY, mMaxWidth)`. Your `curX` must never be less than `availableLeft`. + - **If the block is a float:** + - Place it immediately at `availableLeft` (if left) or `availableRight - width` (if right). + - Add its bounding box `{x, curY, width, height}` to the respective float list. + - Do *not* advance `curX` for the normal inline flow. + - Do *not* trigger a new line for normal flow text (floats are pulled out of the inline line box). + - **If the block is normal text/inline:** + - Adjust `LineWrap::computeLineBreaksEx` to respect the narrowed `mMaxWidth` computed from `availableRight - availableLeft`. *(Note: You may need to handle the case where text wraps below a float and reclaims full width. This can be done by processing text in line-height chunks if constrained by a float).* +- Make sure `curY` is updated when normal lines wrap. +- **Validation:** This is the most complex step. Ensure all existing tests pass exactly (0 pixels difference) before writing float-specific tests. (Snapshot) + +**Step 6.4: Float/Clear Layout Tests** +- In `src/tests/unit_tests/uihtml_position_tests.cpp` (or a new `uihtml_float_tests.cpp`), write robust tests for: + - Text wrapping around a `float: left` block. + - Two consecutive `float: left` blocks stacking horizontally. + - A block with `clear: both` jumping below all floats. + - `BlockLayouter` correctly locating the `CustomBlock` widgets where `RichText` positioned the floats. +- **Validation:** Compile and run all tests. Must pass. (Snapshot) diff --git a/.agent/plans/layout_separation_form_plan.md b/.agent/plans/layout_separation_form_plan.md new file mode 100644 index 000000000..cb16df768 --- /dev/null +++ b/.agent/plans/layout_separation_form_plan.md @@ -0,0 +1,55 @@ +# UI Layout Phase 8: Form Action and Navigation Plan + +This document outlines the architectural plan for implementing HTML `
` submissions, input value extraction, and expanding `UISceneNode` to support interceptable navigation requests (GET/POST). + +**AGENT DIRECTIVE (CRITICAL):** You MUST compile and run the unit tests (`bin/unit_tests/eepp-unit_tests-debug`) after EVERY step. Do NOT proceed to the next step if there is even a 1-pixel difference in visual layout tests. Take a git stash snapshot (`git stash push -m "Phase 8.X passed" && git stash apply`) upon passing a step to keep a checkpoint while continuing to work. **If you need to restore a stash, use `git stash apply` instead of `git stash pop` so the stable snapshot is never lost.** + +--- + +## Phase 8: Form and Navigation implementation + +**Step 8.1: Extend Navigation System** +- The current `UISceneNode::openURL(URI)` and `setURLInterceptorCb` only handle simple URIs, which cannot represent `POST` requests or request bodies. +- In `include/eepp/ui/uiscenenode.hpp`, create: + ```cpp + struct NavigationRequest { + URI uri; + std::string method{ "GET" }; + std::string body; + std::map extraHeaders; + }; + ``` +- Add `void navigate( const NavigationRequest& request );` to `UISceneNode`. +- Add `void setNavigationInterceptorCb( std::function cb );`. +- Update `openURL(URI)` to wrap `navigate(NavigationRequest{uri})` for backward compatibility. +- In `navigate()`, if `mNavigationInterceptorCb` returns `true`, return early. Else if `mURLInterceptorCb` returns `true`, return early. Else, fallback to `Engine::instance()->openURI()`. +- **Validation:** Compile and run all tests. (Snapshot) + +**Step 8.2: Retrieve Values from Form Elements** +- Form submission requires querying the value of input elements. +- Add `virtual String getValue() const { return String(); }` to `UIWidget`. +- Override `getValue()` in the appropriate classes: + - `HTMLInput`: return `getText()` for text, or `"on"`/`""` for checkboxes/radio buttons based on `isChecked()`. + - `HTMLTextArea` (and `UITextEdit`): return `getText()`. + - `UIDropDownList` (and `UIComboBox`): return the selected item's text. +- Add `virtual String getName() const { return getAttribute("name"); }` or rely on `getAttribute("name")` to get the field identifier. +- **Validation:** Write unit tests to verify `getValue()` for text, checkbox, and dropdowns. Compile and run all tests. (Snapshot) + +**Step 8.3: Implement UIHTMLForm** +- Create `UIHTMLForm` class inheriting from `UIHTMLWidget` (or `UIRichText` if treating as a block container). +- Add members: `mAction` (URI), `mMethod` (String, default "GET"), `mEnctype` (String). +- Override `applyProperty` to capture `action`, `method`, and `enctype`. +- Implement `void submit()`. + - `submit()` iterates over all child widgets recursively. + - If a widget has a non-empty `name` attribute (using `getAttribute("name")`), it collects its `getValue()`. + - It URL-encodes the keys and values. + - If `mMethod == "GET"`, it appends the URL-encoded query string to `mAction` and calls `navigate()`. + - If `mMethod == "POST"`, it puts the URL-encoded string into the `body` of `NavigationRequest`, sets `method = "POST"`, and calls `navigate()`. +- In `uiwidgetcreator.cpp`, update `registeredWidget["form"]` to instantiate `UIHTMLForm::New`. +- **Validation:** Compile and run all tests. (Snapshot) + +**Step 8.4: Form Submission Triggers & Testing** +- In `UIHTMLForm`, listen for `Event::OnMouseClick` on any child widget. If the target is a submit button (e.g., `HTMLInput` with `type="submit"`, or a `UIPushButton` with `type="submit"`), prevent the default action and call `submit()`. +- Listen to `Event::OnPressEnter` inside text inputs within the form to trigger `submit()`. +- Write a unit test simulating a form with inputs and a submit button. Attach a `NavigationInterceptorCb` to the scene node, simulate a click on the submit button, and verify the intercepted `NavigationRequest` contains the correct URI and encoded body. +- **Validation:** Compile and run all tests. Must pass. (Snapshot) diff --git a/.agent/plans/layout_separation_list_style_plan.md b/.agent/plans/layout_separation_list_style_plan.md new file mode 100644 index 000000000..179bac412 --- /dev/null +++ b/.agent/plans/layout_separation_list_style_plan.md @@ -0,0 +1,57 @@ +# UI Layout Phase 7: CSS List Style Type Plan + +This document outlines the architectural plan for implementing the CSS `list-style-type` property within the decoupled layout system. This replaces the current CSS background-image hacks with proper text-based list markers. + +**AGENT DIRECTIVE (CRITICAL):** You MUST compile and run the unit tests (`bin/unit_tests/eepp-unit_tests-debug`) after EVERY step. Do NOT proceed to the next step if there is even a 1-pixel difference in visual layout tests. Take a git stash snapshot (`git stash push -m "Phase 7.X passed" && git stash apply`) upon passing a step to keep a checkpoint while continuing to work. **If you need to restore a stash, use `git stash apply` instead of `git stash pop` so the stable snapshot is never lost.** + +--- + +## IMPLEMENTATION HAZARDS (READ BEFORE CODING) +1. **Property Inheritance:** `list-style-type` must be an inherited property so that setting it on `