NAME
====

X::Selkie::WidgetCycle - thrown when a widget's parent chain loops back on itself

SYNOPSIS
========

```raku
use X::Selkie::WidgetCycle;

CATCH {
    when X::Selkie::WidgetCycle {
        note "widget tree is cyclic: {.chain}";
    }
}
```

DESCRIPTION
===========

A Selkie widget tree is a tree by convention, not by construction: `parent` is a writable attribute that layout containers set when they adopt a child. One mis-ordered reparent — a container adopting a widget that is already one of its own ancestors — makes the "tree" cyclic, and every walk up a parent chain then runs forever.

Selkie's parent-chain walks all hop through `Selkie::Tree`'s `next-ancestor`, which throws this exception once a single walk exceeds `PARENT-CHAIN-LIMIT` hops. Catching it is rarely useful: it is always an upstream bug in whatever set `parent`, and the whole point of throwing is that the alternative — a render thread spinning on an endless chain, with a frozen screen and no keyboard — is undiagnosable from the outside.

`chain` holds a bounded, human-readable rendering of the widgets involved (class name and `widget-id`, joined by arrows), which is normally enough to identify the offending container directly. `hops` is the budget that was exceeded.

SEE ALSO
========

  * [Selkie::Tree](Selkie--Tree.md) — `next-ancestor`, `PARENT-CHAIN-LIMIT`, and the walk contract

  * [Selkie::Widget](Selkie--Widget.md) — where `parent` lives

class X::Selkie::WidgetCycle
----------------------------

Thrown when a parent-chain walk exceeds Selkie::Tree's `PARENT-CHAIN-LIMIT`, i.e. when the widget tree contains a cycle.

### has Str $.chain

Bounded description of the widgets on the offending chain, as `Class#id -` Class#id -> …>.

### has Int $.hops

The hop budget that was exceeded.

