Skip to content

Add Window Resize Handling #7

@thomasnemer

Description

@thomasnemer

Part of #1: Scene-Based Architecture

Overview

Implement proper window resize handling throughout the component hierarchy, ensuring that UI layouts automatically reflow when the window size changes.

Current Problems

  • Components have fixed positions after initial layout
  • Window resize doesn't trigger re-layout
  • No way to specify responsive behavior
  • Manual resize handling required in each component

Solution

Implement a resize event propagation system where:

  1. Window resize events are captured by Scene
  2. Scene updates its bounds and notifies children
  3. Components with layouts recalculate positions
  4. Responsive properties control resize behavior

Implementation Details

Scene Resize Handling

class Scene : public Component {
    void onWindowResize(int newWidth, int newHeight) {
        // Update scene bounds
        setBounds(0, 0, newWidth, newHeight);
        
        // Notify renderer
        if (renderer) {
            renderer->onWindowResize(newWidth, newHeight);
        }
        
        // Propagate to children
        propagateResize();
    }
    
    void propagateResize() {
        // Trigger re-layout
        performLayout();
        
        // Notify all children
        for (auto& child : children) {
            child->onParentResize(width, height);
        }
    }
};

Component Resize Support

class Component {
protected:
    bool needsLayout = true;
    ResizeBehavior resizeBehavior = ResizeBehavior::SCALE;
    
public:
    enum class ResizeBehavior {
        FIXED,      // Keep original size
        SCALE,      // Scale with parent
        FILL,       // Fill available space
        MAINTAIN_ASPECT  // Keep aspect ratio
    };
    
    virtual void onParentResize(int parentWidth, int parentHeight) {
        switch (resizeBehavior) {
            case ResizeBehavior::FILL:
                setBounds(0, 0, parentWidth, parentHeight);
                break;
            case ResizeBehavior::SCALE:
                // Scale proportionally
                float scaleX = parentWidth / (float)originalParentWidth;
                float scaleY = parentHeight / (float)originalParentHeight;
                setBounds(x * scaleX, y * scaleY, 
                         width * scaleX, height * scaleY);
                break;
            case ResizeBehavior::MAINTAIN_ASPECT:
                // Calculate maintaining aspect ratio
                maintainAspectRatio(parentWidth, parentHeight);
                break;
            case ResizeBehavior::FIXED:
                // Do nothing
                break;
        }
        
        // Mark for re-layout
        needsLayout = true;
        
        // Propagate to children
        for (auto& child : children) {
            child->onParentResize(width, height);
        }
    }
    
    void render() override {
        if (needsLayout) {
            performLayout();
            needsLayout = false;
        }
        // ... existing render code
    }
};

Panel Auto-Resize

template<typename LayoutT>
class Panel : public Component {
    void onParentResize(int parentWidth, int parentHeight) override {
        // Panels typically fill parent
        if (resizeBehavior == ResizeBehavior::FILL) {
            auto [px, py, pw, ph] = parent->getContentBounds();
            setBounds(px, py, pw, ph);
        } else {
            Component::onParentResize(parentWidth, parentHeight);
        }
        
        // Re-layout children
        if (layout) {
            performLayout();
        }
    }
};

Responsive Constraints

struct ResponsiveConstraints {
    // Breakpoints
    struct Breakpoint {
        int minWidth;
        LayoutConfiguration config;
    };
    std::vector<Breakpoint> breakpoints;
    
    // Minimum/maximum sizes
    int minWidth = 0, minHeight = 0;
    int maxWidth = INT_MAX, maxHeight = INT_MAX;
    
    // Aspect ratio
    float aspectRatio = 0.0f;  // 0 = no constraint
};

class Component {
    ResponsiveConstraints constraints;
    
    void applyConstraints(int& w, int& h) {
        w = std::clamp(w, constraints.minWidth, constraints.maxWidth);
        h = std::clamp(h, constraints.minHeight, constraints.maxHeight);
        
        if (constraints.aspectRatio > 0) {
            h = w / constraints.aspectRatio;
        }
    }
};

Acceptance Criteria

  • Scene captures window resize events
  • Resize propagates through component hierarchy
  • Components with layouts recalculate positions
  • ResizeBehavior enum controls resize response
  • Panels auto-fill parent on resize
  • Min/max size constraints respected
  • Aspect ratio constraints supported
  • Demo app UI reflows properly on resize
  • No manual resize handling needed in user code

Testing

  • Resize window horizontally - components should reflow
  • Resize window vertically - components should reflow
  • Maximize window - UI should expand properly
  • Minimize then restore - UI should maintain state
  • Extreme sizes - constraints should prevent UI breakage

Example Usage

// Components automatically handle resize
auto panel = std::make_unique<Panel<FlexLayout>>(renderer);
panel->setResizeBehavior(ResizeBehavior::FILL);
panel->setConstraints({.minWidth = 200, .minHeight = 100});

// Responsive breakpoints
panel->addBreakpoint(800, {
    .direction = FlexDirection::COLUMN  // Stack vertically on small screens
});
panel->addBreakpoint(1200, {
    .direction = FlexDirection::ROW  // Side-by-side on large screens
});

Dependencies

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions