Layout Framework (08_04_00)¶
Sistema de layouts flexible y responsive para interfaces de usuario de plugins de audio.
📋 Características¶
- FlexLayout: Sistema flexbox para layouts dinámicos (row/column)
- GridLayout: Sistema de grid con filas/columnas configurables
- ConstraintLayout: Layout basado en constraints (similar a Auto Layout)
- ResponsiveEngine: Sistema responsive con breakpoints
- AspectRatioConstraint: Mantención de aspect ratio
- AdaptiveSpacing: Sistema de espaciado adaptativo
🚀 Uso Rápido¶
FlexLayout¶
#include "FlexLayout.hpp"
FlexLayout layout;
layout.setDirection(FlexLayout::Direction::Row);
layout.setJustify(FlexLayout::Justify::SpaceBetween);
layout.setAlign(FlexLayout::Align::Center);
layout.setSpacing(10.0f);
layout.addChild(&knob1, 1.0f); // flexGrow = 1
layout.addChild(&knob2, 2.0f); // flexGrow = 2 (ocupa 2x espacio)
layout.layout(Rect{0, 0, 800, 600});
GridLayout¶
#include "GridLayout.hpp"
GridLayout grid;
grid.setRows({100.0f, -1.0f, 100.0f}); // fixed, auto, fixed
grid.setColumns({-1.0f, -2.0f, -1.0f}); // 1fr, 2fr, 1fr
grid.setGap(10.0f, 10.0f);
grid.addChild(&widget1, 0, 0); // row 0, col 0
grid.addChild(&widget2, 0, 1, 1, 2); // row 0, col 1, span 2 cols
grid.layout(Rect{0, 0, 800, 600});
ConstraintLayout¶
#include "ConstraintLayout.hpp"
ConstraintLayout layout;
layout.addChild(&button);
layout.addChild(&label);
// Center button
layout.addConstraint(&button, ConstraintLayout::Attribute::CenterX,
nullptr, ConstraintLayout::Attribute::CenterX);
layout.addConstraint(&button, ConstraintLayout::Attribute::CenterY,
nullptr, ConstraintLayout::Attribute::CenterY);
// Label below button with 10px gap
layout.addConstraint(&label, ConstraintLayout::Attribute::Top,
&button, ConstraintLayout::Attribute::Bottom, 10.0f);
layout.layout(Rect{0, 0, 800, 600});
ResponsiveEngine¶
#include "ResponsiveEngine.hpp"
ResponsiveEngine engine;
engine.setBaseWidth(800.0f);
engine.setBaseHeight(600.0f);
engine.onBreakpointChange([](const std::string& name, SizeClass sizeClass) {
std::cout << "Breakpoint: " << name << "\n";
});
engine.update(windowWidth, windowHeight);
float scaleFactor = engine.getScaleFactor();
🔧 API Principal¶
FlexLayout¶
enum class Direction { Row, Column, RowReverse, ColumnReverse };
enum class Justify { Start, End, Center, SpaceBetween, SpaceAround, SpaceEvenly };
enum class Align { Start, End, Center, Stretch };
void setDirection(Direction dir);
void setJustify(Justify j);
void setAlign(Align a);
void addChild(Widget* widget, float flexGrow = 0.0f, float flexShrink = 1.0f);
void layout(const Rect& bounds) override;
GridLayout¶
void setRows(const std::vector<float>& rows); // Positive = fixed, 0 = auto, negative = fr
void setColumns(const std::vector<float>& columns);
void setGap(float rowGap, float colGap);
void addChild(Widget* widget, size_t row, size_t col, size_t rowSpan = 1, size_t colSpan = 1);
void layout(const Rect& bounds) override;
ConstraintLayout¶
enum class Attribute { Left, Right, Top, Bottom, CenterX, CenterY, Width, Height };
enum class Relation { Equal, LessThanOrEqual, GreaterThanOrEqual };
void addConstraint(Widget* first, Attribute firstAttr,
Widget* second, Attribute secondAttr,
float constant = 0.0f, float multiplier = 1.0f,
Relation relation = Relation::Equal);
void layout(const Rect& bounds) override;
ResponsiveEngine¶
enum class SizeClass { Compact, Medium, Expanded };
enum class Orientation { Portrait, Landscape };
void addBreakpoint(float width, const std::string& name, SizeClass sizeClass);
void update(float width, float height);
SizeClass getCurrentSizeClass() const;
Orientation getCurrentOrientation() const;
float getScaleFactor() const;
float scale(float value) const;
📦 Compilación¶
cd 08_04_00_layout_framework
mkdir build && cd build
cmake .. -DCMAKE_BUILD_TYPE=Release
cmake --build .
🧪 Tests¶
📚 Ejemplos¶
🎯 Casos de Uso¶
Plugin UI con múltiples knobs¶
FlexLayout mainLayout;
mainLayout.setDirection(FlexLayout::Direction::Row);
mainLayout.setJustify(FlexLayout::Justify::SpaceAround);
mainLayout.setPadding(Insets{20.0f});
mainLayout.addChild(&freqKnob);
mainLayout.addChild(&resKnob);
mainLayout.addChild(&driveKnob);
mainLayout.addChild(&mixKnob);
mainLayout.layout(pluginBounds);
Dashboard con header/footer¶
GridLayout dashboard;
dashboard.setRows({60.0f, -1.0f, 40.0f}); // header, content, footer
dashboard.setColumns({-1.0f}); // full width
dashboard.addChild(&header, 0, 0);
dashboard.addChild(&content, 1, 0);
dashboard.addChild(&footer, 2, 0);
dashboard.layout(windowBounds);
Responsive UI¶
ResponsiveEngine engine;
FlexLayout layout;
engine.onBreakpointChange([&](const std::string& bp, SizeClass sc) {
if (sc == SizeClass::Compact) {
layout.setDirection(FlexLayout::Direction::Column);
} else {
layout.setDirection(FlexLayout::Direction::Row);
}
});
engine.update(width, height);
layout.layout(bounds);
⚙️ Configuración¶
Padding y Spacing¶
// FlexLayout
layout.setSpacing(10.0f); // Gap entre items
layout.setPadding(Insets{20.0f}); // Padding uniforme
layout.setPadding(Insets{10.0f, 20.0f, 10.0f, 20.0f}); // TRBL
// GridLayout
grid.setGap(10.0f, 20.0f); // row gap, col gap
grid.setPadding(Insets{15.0f});
Breakpoints Personalizados¶
ResponsiveEngine engine;
engine.clearBreakpoints(); // Remover defaults
engine.addBreakpoint(480.0f, "phone", SizeClass::Compact);
engine.addBreakpoint(768.0f, "tablet", SizeClass::Medium);
engine.addBreakpoint(1024.0f, "laptop", SizeClass::Expanded);
engine.addBreakpoint(1920.0f, "desktop", SizeClass::Expanded);
🎨 Design Patterns¶
Nested Layouts¶
// Main grid
GridLayout mainGrid;
mainGrid.setRows({100.0f, -1.0f});
mainGrid.setColumns({-1.0f});
// Header con FlexLayout
FlexLayout headerLayout;
headerLayout.setDirection(FlexLayout::Direction::Row);
headerLayout.setJustify(FlexLayout::Justify::SpaceBetween);
// ... add header items
// Layout anidado
mainGrid.layout(bounds);
Rect headerBounds = mainGrid.getCellBounds(0, 0);
headerLayout.layout(headerBounds);
📊 Performance¶
- FlexLayout: O(n) para n widgets
- GridLayout: O(n) para n widgets
- ConstraintLayout: O(n*m) para n widgets y m constraints (iterativo, max 10 iteraciones)
- ResponsiveEngine: O(1) update, O(log n) para n breakpoints
🔍 Debugging¶
Todos los layouts exponen sus bounds calculados:
Rect bounds = widget->getBounds();
std::cout << "Widget at (" << bounds.x << ", " << bounds.y << ") "
<< "size (" << bounds.width << " x " << bounds.height << ")\n";
📝 Notas¶
- Todos los layouts respetan
Widget::participatesInLayout() - Los widgets invisibles no participan en layout
- Los tamaños se respetan dentro de
minSizeymaxSize - GridLayout: valores negativos = fractional units (fr)
- GridLayout: valor 0 = auto-sizing
- ConstraintLayout usa resolución iterativa (no Cassowary completo)
🔗 Dependencias¶
- C++20
- Catch2 (solo para tests)
📄 Licencia¶
Parte del framework AudioLab.