/* OmniaFramework - A collection of useful functionality Copyright (C) 2026 OmniaX-Dev This file is part of OmniaFramework. OmniaFramework is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. OmniaFramework is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OmniaFramework. If not, see . */ #pragma once #include #include #include namespace ogfx { namespace gui { class Widget; // ============================================================================ // Layout — abstract base. // // A Layout is owned by a Widget (the "container") and is responsible for // positioning and sizing that widget's children inside the container's // content bounds (i.e. inside padding, after content offset). // // Two responsibilities: // - arrange(parent): set position + size on every laid-out child. // - measure(parent): report how much space this layout *wants* given // its current children. Used so a Layout can drive // a parent's contentExtents (and therefore scrollbars), // and so a parent Layout can ask a child container // "how big would you like to be?". // ============================================================================ class Layout { public: virtual ~Layout(void) = default; // Place children. Called from Widget::relayout(). virtual void arrange(Widget& parent) = 0; // Compute desired size of this layout's content. Should NOT mutate // children. Used by Widget::getContentExtents() override below and // by parent layouts asking for preferred size. virtual Vec2 measure(const Widget& parent) const = 0; // Spacing between adjacent items along the main axis (Box) or // between cells (Grid). FillLayout ignores it. inline f32 getSpacing(void) const { return m_spacing; } inline void setSpacing(f32 s) { m_spacing = s; } protected: // Helpers used by concrete layouts to honor a child's hint // without each subclass re-implementing the same min/max/preferred // resolution logic. static Vec2 resolvePreferred(const Widget& child); static f32 applyMinMax(f32 value, f32 minV, f32 maxV); static f32 alignOffset(eAlign a, f32 available, f32 size); f32 m_spacing { 0.0f }; }; // ============================================================================ // FillLayout — every laid-out child is sized to fill the parent's content // area. Useful as a default (a "single slot" container) and as a building // block for things like SplitView panels. // ============================================================================ class FillLayout : public Layout { public: void arrange(Widget& parent) override; Vec2 measure(const Widget& parent) const override; }; // ============================================================================ // BoxLayout — arrange children in a single row (Horizontal) or column // (Vertical) with stretch factors and spacing. // // Algorithm (per axis = "main"): // 1. Sum preferred sizes of non-stretching children + spacing. // 2. The remainder is distributed among stretching children proportional // to their stretch factor. // 3. min/max are clamped per child; if a stretching child hits its max, // the leftover is redistributed among the rest in a second pass. // // Cross axis: each child either fills (Stretch, default) or uses its // preferred size aligned per alignCross. // ============================================================================ class BoxLayout : public Layout { public: enum class Orientation { Horizontal, Vertical }; explicit BoxLayout(Orientation orientation) : m_orientation(orientation) {} void arrange(Widget& parent) override; Vec2 measure(const Widget& parent) const override; inline Orientation getOrientation(void) const { return m_orientation; } inline void setOrientation(Orientation o) { m_orientation = o; } private: Orientation m_orientation; }; // ============================================================================ // GridLayout — fixed rows × cols. Cell sizes are uniform (rows = h/N, // cols = w/M). Children are placed in row-major insertion order. // LayoutHint::colSpan / rowSpan let a child cover multiple cells. // ============================================================================ class GridLayout : public Layout { public: GridLayout(u32 rows, u32 cols) : m_rows(rows), m_cols(cols) {} void arrange(Widget& parent) override; Vec2 measure(const Widget& parent) const override; inline u32 getRows(void) const { return m_rows; } inline u32 getCols(void) const { return m_cols; } inline void setDimensions(u32 rows, u32 cols) { m_rows = rows; m_cols = cols; } private: u32 m_rows; u32 m_cols; }; } }