| #include "rive/layout/grid_track.hpp" | |
| #include "rive/layout_component.hpp" | |
| #ifdef WITH_RIVE_LAYOUT | |
| #include "rive/layout/layout_data.hpp" | |
| #endif | |
| #include <cmath> | |
| using namespace rive; | |
| void GridTrack::markLayoutDirty() | |
| { | |
| #ifdef WITH_RIVE_LAYOUT | |
| if (parent() != nullptr && parent()->is<LayoutComponent>()) | |
| { | |
| parent()->as<LayoutComponent>()->markLayoutNodeDirty(); | |
| } | |
| #endif | |
| } | |
| void GridTrack::collectionChanged() { markLayoutDirty(); } | |
| void GridTrack::trackTypeChanged() { markLayoutDirty(); } | |
| void GridTrack::trackValueChanged() { markLayoutDirty(); } | |
| void GridTrack::trackMaxTypeChanged() { markLayoutDirty(); } | |
| void GridTrack::trackMaxValueChanged() { markLayoutDirty(); } | |
| #ifdef WITH_RIVE_LAYOUT | |
| namespace | |
| { | |
| YGStyleSizeLength gridSizeLength(GridTrackSizeType type, float value) | |
| { | |
| switch (type) | |
| { | |
| case GridTrackSizeType::points: | |
| return YGStyleSizeLength::points(value); | |
| case GridTrackSizeType::percent: | |
| return YGStyleSizeLength::percent(value); | |
| case GridTrackSizeType::fr: | |
| return YGStyleSizeLength::stretch(value); | |
| default: | |
| return YGStyleSizeLength::ofAuto(); | |
| } | |
| } | |
| YGGridTrackSize gridTrackSize(const GridTrack* track) | |
| { | |
| auto type = (GridTrackSizeType)track->trackType(); | |
| if (track->trackMaxType() != 0) | |
| { | |
| return YGGridTrackSize::minmax( | |
| gridSizeLength(type, track->trackValue()), | |
| gridSizeLength((GridTrackSizeType)(track->trackMaxType() - 1), | |
| track->trackMaxValue())); | |
| } | |
| switch (type) | |
| { | |
| case GridTrackSizeType::points: | |
| return YGGridTrackSize::length(track->trackValue()); | |
| case GridTrackSizeType::percent: | |
| return YGGridTrackSize::percent(track->trackValue()); | |
| case GridTrackSizeType::fr: | |
| return YGGridTrackSize::fr(track->trackValue()); | |
| default: | |
| return YGGridTrackSize::auto_(); | |
| } | |
| } | |
| } // namespace | |
| // The start line for a placement value expressed as a *cell* index. | |
| // | |
| // 0 is auto. Positive N already names the line that starts cell N. Negative | |
| // values count back from the last cell, which needs a shift: the engine | |
| // resolves line -1 to the grid's final line, and an item occupies the track | |
| // *starting* at its line, so -1 alone would land in an implicit track past the | |
| // end. Sending -2 for -1 puts the item in the last cell instead. | |
| // | |
| // Out-of-range negatives are left alone — the engine grows implicit tracks | |
| // leftwards, the same as CSS. | |
| static YGGridLine gridLine(int cell) | |
| { | |
| if (cell == 0) | |
| { | |
| return YGGridLine::auto_(); | |
| } | |
| return YGGridLine::fromInteger(cell < 0 ? cell - 1 : cell); | |
| } | |
| static YGGridLine gridSpan(uint32_t span) | |
| { | |
| return span > 1 ? YGGridLine::span((int)span) : YGGridLine::auto_(); | |
| } | |
| void GridTrack::syncContainerStyle(YGStyle& ygStyle, | |
| ContainerComponent* owner, | |
| uint32_t justifyItemsValue) | |
| { | |
| facebook::yoga::GridTrackList lists[4]; | |
| for (auto child : owner->children()) | |
| { | |
| if (!child->is<GridTrack>()) | |
| { | |
| continue; | |
| } | |
| auto track = child->as<GridTrack>(); | |
| if (track->collection() > 3) | |
| { | |
| continue; | |
| } | |
| lists[track->collection()].push_back(gridTrackSize(track)); | |
| } | |
| ygStyle.setGridTemplateColumns(std::move(lists[0])); | |
| ygStyle.setGridTemplateRows(std::move(lists[1])); | |
| ygStyle.setGridAutoColumns(std::move(lists[2])); | |
| ygStyle.setGridAutoRows(std::move(lists[3])); | |
| ygStyle.setJustifyItems((YGJustify)justifyItemsValue); | |
| } | |
| void GridTrack::syncStackContainerStyle(YGStyle& ygStyle, | |
| uint32_t justifyItemsValue) | |
| { | |
| facebook::yoga::GridTrackList single; | |
| single.push_back(YGGridTrackSize::fr(1)); | |
| facebook::yoga::GridTrackList singleRow; | |
| singleRow.push_back(YGGridTrackSize::fr(1)); | |
| ygStyle.setGridTemplateColumns(std::move(single)); | |
| ygStyle.setGridTemplateRows(std::move(singleRow)); | |
| ygStyle.setGridAutoColumns(facebook::yoga::GridTrackList{}); | |
| ygStyle.setGridAutoRows(facebook::yoga::GridTrackList{}); | |
| ygStyle.setJustifyItems((YGJustify)justifyItemsValue); | |
| } | |
| // The inline-axis (width) self-alignment for a grid item, downgrading a | |
| // resolved `stretch` to `flex-start` when the item hugs — a hug item sizes to | |
| // content and cannot stretch. justify-self defaults to auto (inherit the | |
| // container's justify-items, which itself defaults to stretch), so without this | |
| // a hug item would fill its cell's inline axis. | |
| static YGJustify resolveItemJustifySelf(uint32_t justifySelfValue, | |
| bool inlineHugs, | |
| uint32_t containerJustifyItems) | |
| { | |
| if (!inlineHugs) | |
| { | |
| return (YGJustify)justifySelfValue; | |
| } | |
| uint32_t effective = justifySelfValue == (uint32_t)YGJustifyAuto | |
| ? containerJustifyItems | |
| : justifySelfValue; | |
| return effective == (uint32_t)YGJustifyStretch | |
| ? YGJustifyFlexStart | |
| : (YGJustify)justifySelfValue; | |
| } | |
| // A stack collapses every child into its single cell. There is no placement | |
| // object on a stack child to write this, so the container's sync does it. | |
| // | |
| // No matching reset for the grid case: nothing can reparent an item or change a | |
| // parent's layoutType at runtime (neither property animates or binds), so a | |
| // style that was never written stays at yoga's auto-placement default. The | |
| // editor, where both *can* happen, resets in its own sync. | |
| void GridTrack::syncItemLines(YGStyle& ygStyle, | |
| int32_t column, | |
| int32_t row, | |
| uint32_t columnSpan, | |
| uint32_t rowSpan) | |
| { | |
| ygStyle.setGridColumnStart(gridLine(column)); | |
| ygStyle.setGridColumnEnd(gridSpan(columnSpan)); | |
| ygStyle.setGridRowStart(gridLine(row)); | |
| ygStyle.setGridRowEnd(gridSpan(rowSpan)); | |
| } | |
| void GridTrack::syncStackItemCell(YGStyle& ygStyle) | |
| { | |
| ygStyle.setGridColumnStart(YGGridLine::fromInteger(1)); | |
| ygStyle.setGridColumnEnd(YGGridLine::auto_()); | |
| ygStyle.setGridRowStart(YGGridLine::fromInteger(1)); | |
| ygStyle.setGridRowEnd(YGGridLine::auto_()); | |
| } | |
| // Inline-axis self alignment. Stays with the style rather than moving to the | |
| // placement child: it resolves against the container's justify-items and the | |
| // item's hug state, which syncStyle already has and a child would have to walk | |
| // for. | |
| void GridTrack::syncItemJustifySelf(YGStyle& ygStyle, | |
| uint32_t justifySelfValue, | |
| bool stack, | |
| bool inlineHugs, | |
| uint32_t containerJustifyItems) | |
| { | |
| if (stack) | |
| { | |
| // A stack's container justify comes from its alignment (start/center/ | |
| // end, never stretch), so the hug->flexStart downgrade that guards grid | |
| // stretch doesn't apply. Keep the item's own justify-self (auto | |
| // inherits the container's alignment) so horizontal alignment takes | |
| // effect. | |
| ygStyle.setJustifySelf((YGJustify)justifySelfValue); | |
| return; | |
| } | |
| ygStyle.setJustifySelf(resolveItemJustifySelf(justifySelfValue, | |
| inlineHugs, | |
| containerJustifyItems)); | |
| } | |
| #endif |