/*
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;
};
}
}