Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/docs/guides/styles/overflow-and-scrolling.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,4 +63,4 @@ const container = new LayoutContainer({
});
```

See the [Trackpad Customization](../core/components.md##trackpad-customization) section for more details on available options.
See the [Trackpad Customization](../core/components.md#trackpad-customization) section for more details on available options.
97 changes: 62 additions & 35 deletions src/core/utils/sort-children.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,64 +4,91 @@ import { type Layout } from '../Layout';

/**
* Sorts the children of the layout based on their order in the parent container
* This is necessary because not all children are part of the layout and we need to
* make sure that the Yoga children are in the correct order
* @param layout - The layout to sort the children for
* syncing the Yoga tree with the Pixi display list.
*/
export function onChildAdded(layout: Layout, pixiParent: Container) {
let parentLayout = pixiParent.layout;
let yogaIndex = -1;
let overflowContainer: Container | undefined;

// If inside an overflow container, actual yoga parent is the overflow's parent layout.
if (!parentLayout && (pixiParent as OverflowContainer).isOverflowContainer) {
parentLayout = pixiParent.parent?.layout;
// update yogaIndex
yogaIndex = pixiParent.children.indexOf(layout.target);
pixiParent = pixiParent.parent!;
overflowContainer = pixiParent;
parentLayout = overflowContainer.parent?.layout;
pixiParent = overflowContainer.parent!;
}

if (parentLayout) {
const yogaParent = layout.yoga.getParent();
if (!parentLayout) {
return;
}

if (yogaParent) {
yogaParent!.removeChild(layout.yoga);
}
// Detach from previous yoga parent if any
const yogaParent = layout.yoga.getParent();

// If the child is the last one, we can just append it
if (pixiParent.children.indexOf(layout.target) === pixiParent.children.length - 1 && yogaIndex === -1) {
parentLayout.yoga.insertChild(layout.yoga, parentLayout.yoga.getChildCount());
if (yogaParent) {
yogaParent.removeChild(layout.yoga);
}

return;
}
const yogaIndex = computeYogaInsertionIndex(layout, pixiParent, overflowContainer);

if (yogaIndex === -1) {
return;
}

// Find the corresponding Yoga index
for (let i = 0; i < pixiParent.children.length; i++) {
const child = pixiParent.children[i]!;
// Fast append path
if (yogaIndex === parentLayout.yoga.getChildCount()) {
parentLayout.yoga.insertChild(layout.yoga, yogaIndex);

if (child.layout && child.visible) {
yogaIndex++;
return;
}

parentLayout.yoga.insertChild(layout.yoga, yogaIndex);
}

/**
* Computes the Yoga insertion index with a single pass over the logical (flattened if needed) sibling list.
* @param layout - layout being inserted
* @param parent - the real parent container whose layout we insert into
* @param overflow - optional overflow container that actually contains the target
*/
function computeYogaInsertionIndex(layout: Layout, parent: Container, overflow?: Container): number {
const target = layout.target;
let index = 0;

if (overflow) {
// Iterate real parent children; when reaching overflow, iterate its children inline.
for (const child of parent.children) {
if (child === overflow) {
for (const inner of overflow.children) {
if (!inner.layout || !inner.visible) continue;
if (inner === target) return index;
index++;
}

return -1; // target not found inside overflow
}
if (child === layout.target) {
break;
if (child.layout && child.visible) {
index++;
}
}

// If the yogaIndex is -1, it means the child was not found in the parent container
// This can happen if the child is not part of the layout or is not visible
// In this case, we do not insert the child into the Yoga layout
if (yogaIndex === -1) {
return;
}

parentLayout.yoga.insertChild(layout.yoga, yogaIndex);
return -1;
}
for (const child of parent.children) {
if (!child.layout || !child.visible) continue;
if (child === target) return index;
index++;
}

return -1;
}

/**
* Removes the child from the layout
* @param layout - The layout to remove the child from
*/
export function onChildRemoved(layout: Layout) {
const yogaParent = layout.yoga.getParent();

yogaParent && yogaParent!.removeChild(layout.yoga);
if (yogaParent) {
yogaParent.removeChild(layout.yoga);
}
}
Loading