diff --git a/src/vaev-engine/layout/block.cpp b/src/vaev-engine/layout/block.cpp index 5d6b8da7..3b27d468 100644 --- a/src/vaev-engine/layout/block.cpp +++ b/src/vaev-engine/layout/block.cpp @@ -8,6 +8,7 @@ export module Vaev.Engine:layout.block; import :values; import :layout.base; import :layout.layout; +import :layout.floats; namespace Vaev::Layout { @@ -170,6 +171,7 @@ struct BlockFormatingContext : FormatingContext { bool blockWasCompletelyLaidOut = false; Au lastMarginBottom = 0_au; + FloatsContainer floats{input.position, inlineSize}; for (usize i = startAt; i < endChildren; ++i) { auto& c = box.children()[i]; @@ -182,9 +184,30 @@ struct BlockFormatingContext : FormatingContext { ) ); - // TODO: Implement floating - // if (c.style->float_ != Float::NONE) - // continue; + if (c.style->float_ != Float::NONE) { + Input floatInput = { + .intrinsic = IntrinsicSize::MAX_CONTENT, + .availableSpace = {input.availableSpace.x, 0_au}, + .containingBlock = {inlineSize, input.knownSize.y.unwrapOr(0_au)}, + }; + auto size = layout(tree, c, floatInput).size; + auto margins = computeMargins(tree, c, floatInput); + auto bound = floats.place(size + margins.all(), c.style->float_, lastMarginBottom).shrink(margins); + if (input.fragment) { + layout( + tree, + c, + { + .fragment = input.fragment, + .knownSize = bound.size().cast>(), + .position = bound.topStart(), + .availableSpace = {input.availableSpace.x, 0_au}, + .containingBlock = {inlineSize, input.knownSize.y.unwrapOr(0_au)}, + } + ); + } + continue; + } Input childInput = { .fragment = input.fragment, diff --git a/src/vaev-engine/layout/builder.cpp b/src/vaev-engine/layout/builder.cpp index b839a9c6..6350977d 100644 --- a/src/vaev-engine/layout/builder.cpp +++ b/src/vaev-engine/layout/builder.cpp @@ -801,7 +801,11 @@ static void _buildChildDefaultDisplay(BuilderContext bc, Gc::Ref c } // NOTE: Flow for From::BLOCK and From::INLINE - if (display == Display::Outside::BLOCK) { + // https://www.w3.org/TR/css-display-3/#valdef-display-flow + if (display == Display::Outside::BLOCK or + childStyle->float_ != Float::NONE or + childStyle->position != Position::STATIC) { + bc.flushRootInlineBoxIntoAnonymousBox(); _innerDisplayDispatchCreationOfBlockLevelBox(bc, child, childStyle, display); } else { diff --git a/src/vaev-engine/layout/floats.cpp b/src/vaev-engine/layout/floats.cpp new file mode 100644 index 00000000..a99631b1 --- /dev/null +++ b/src/vaev-engine/layout/floats.cpp @@ -0,0 +1,129 @@ +module; + +#include + +export module Vaev.Engine:layout.floats; + +import :values; + +namespace Vaev::Layout { + +export struct FloatsContainer { + struct Placed { + RectAu rect; + Float side; + }; + + Vec _placed; + Vec2Au _origin; + Au _inlineSize; + + FloatsContainer(Vec2Au origin, Au inlineSize) + : _origin(origin), _inlineSize(inlineSize) {} + + FloatsContainer() : _origin({0_au, 0_au}), _inlineSize(0_au) {} + + static bool _verticallyOverlaps(RectAu const& r, Au bandY, Au bandH) { + Au r0 = r.y; + Au r1 = r.y + r.height; + Au b0 = bandY; + Au b1 = bandY + bandH; + return not(b1 <= r0 or r1 <= b0); + } + + // Compute available inline range [start, end) in container coords at band y..y+h + Pair inlineAvailable(Au y, Au h) const { + // Fast path: no floats + if (_placed.len() == 0) + return {0_au, _inlineSize}; + + Au left = 0_au; // consumed from Start + Au right = _inlineSize; // open end (exclusive) + for (auto const& pf : _placed) { + if (not _verticallyOverlaps(pf.rect, _origin.y + y, h)) + continue; + if (pf.side == Float::INLINE_START or pf.side == Float::LEFT) { + Au used = (pf.rect.x + pf.rect.width) - _origin.x; + if (used > left) + left = used; + } else if (pf.side == Float::INLINE_END or pf.side == Float::RIGHT) { + Au used = pf.rect.x - _origin.x; + if (used < right) + right = used; + } + } + if (left > right) + left = right; // no space => collapsed interval + return {left, right}; + } + + Au clear(Clear clr, Au fromY) const { + bool start = clr == Clear::BOTH or clr == Clear::INLINE_START or clr == Clear::LEFT; + bool end = clr == Clear::BOTH or clr == Clear::INLINE_END or clr == Clear::RIGHT; + + Au y = fromY; + bool changed = true; + while (changed) { + changed = false; + for (auto const& pf : _placed) { + if (not _verticallyOverlaps(pf.rect, _origin.y + y, 1_au)) + continue; + bool isStart = (pf.side == Float::INLINE_START or pf.side == Float::LEFT); + bool isEnd = (pf.side == Float::INLINE_END or pf.side == Float::RIGHT); + if ((start and isStart) or (end and isEnd)) { + Au ny = (pf.rect.y + pf.rect.height) - _origin.y; + if (ny > y) { + y = ny; + changed = true; + } + } + } + } + return y; + } + + // Place a float box with size (inline=w, block=h) at or after y + RectAu place(Vec2Au size, Float side, Au y) { + Au w = size.x; + Au h = size.y; + if (w <= 0_au or h <= 0_au) + return {_origin.x, _origin.y + y, 0_au, 0_au}; + + Au curY = y; + while (true) { + auto [availStart, availEnd] = inlineAvailable(curY, h); + Au availW = availEnd - availStart; + if (availW >= w) { + Au x = (side == Float::INLINE_START or side == Float::LEFT) + ? (_origin.x + availStart) + : (_origin.x + (availEnd - w)); + RectAu rect{x, _origin.y + curY, w, h}; + _placed.pushBack({rect, side}); + return rect; + } + + // Not enough room on this slice; push below the lowest overlapping edge + Au nextY = curY; + for (auto const& pf : _placed) { + if (_verticallyOverlaps(pf.rect, _origin.y + curY, h)) { + Au ny = (pf.rect.y + pf.rect.height) - _origin.y; + if (ny > nextY) + nextY = ny; + } + } + if (nextY == curY) { + // Wider than container: clamp to container width and place at side + Au clampedW = (w > _inlineSize) ? _inlineSize : w; + Au x = (side == Float::INLINE_START or side == Float::LEFT) + ? _origin.x + : _origin.x + (_inlineSize - clampedW); + RectAu rect{x, _origin.y + curY, clampedW, h}; + _placed.pushBack({rect, side}); + return rect; + } + curY = nextY; + } + } +}; + +} // namespace Vaev::Layout \ No newline at end of file diff --git a/src/vaev-engine/layout/mod.cpp b/src/vaev-engine/layout/mod.cpp index c483efa8..5a256ba1 100644 --- a/src/vaev-engine/layout/mod.cpp +++ b/src/vaev-engine/layout/mod.cpp @@ -5,6 +5,7 @@ export import :layout.base; export import :layout.block; export import :layout.builder; export import :layout.flex; +export import :layout.floats; export import :layout.grid; export import :layout.inline_; export import :layout.layout; diff --git a/src/vaev-engine/layout/paint.cpp b/src/vaev-engine/layout/paint.cpp index ec4b3bcb..07a6207a 100644 --- a/src/vaev-engine/layout/paint.cpp +++ b/src/vaev-engine/layout/paint.cpp @@ -217,7 +217,8 @@ static void _paintChildren(Frag& frag, Scene::Stack& stack, auto predicate) { // NOTE: Positioned elements act as if they establish a stacking context auto position = s.position; - if (position != Position::STATIC) { + auto float_ = s.float_; + if (position != Position::STATIC or float_ != Float::NONE) { if (predicate(s)) _paintStackingContext(c, stack); continue; diff --git a/src/vaev-engine/values/insets.cpp b/src/vaev-engine/values/insets.cpp index 408b5f5f..8632ec22 100644 --- a/src/vaev-engine/values/insets.cpp +++ b/src/vaev-engine/values/insets.cpp @@ -28,7 +28,7 @@ export enum struct Position { }; export bool impliesRemovingFromFlow(Position position) { - return position == Position::ABSOLUTE || position == Position::FIXED; + return position == Position::ABSOLUTE or position == Position::FIXED; } export template <>