NAME
====

Selkie::Widget::Checkbox - Focusable boolean toggle

SYNOPSIS
========

```raku
use Selkie::Widget::Checkbox;
use Selkie::Sizing;

my $cb = Selkie::Widget::Checkbox.new(
    label  => 'Enable notifications',
    sizing => Sizing.fixed(1),
);
$cb.on-change.tap: -> Bool $checked {
    $app.store.dispatch('settings/notifications', value => $checked);
};
```

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

Renders as `[x] label` when checked, `[ ] label` when unchecked. Space or Enter toggles the state, as does a primary mouse click on any cell of the checkbox row. User activation is debounced by default so duplicate terminal press events from one physical click do not flip the checkbox twice.

`set-checked` is idempotent — passing the current value is a no-op and doesn't emit on `on-change`. Safe to call from a store subscription without causing feedback loops.

GLYPHS
======

The `[x]`/`[ ]` indicator is configurable via `checked-glyph` and `unchecked-glyph` (constructor args, both `is rw`) or the `set-glyphs` method. Defaults are byte-identical to the historical hardcoded pair, so existing code renders exactly as before.

**Keep both glyphs the same `.chars` length.** The indicator is rendered directly before the label with no fixed-width padding between them — if `checked-glyph` and `unchecked-glyph` differ in length, the label visibly shifts left or right every time the checkbox toggles. Mismatched widths aren't rejected (a narrower "off" glyph paired with a wrapped/bracketed "on" glyph is a legitimate design choice), but the jitter is the tradeoff.

**Avoid `☑`/`☐` (U+2611/U+2610).** These render as ambiguous-width in many terminal/font combinations — some environments draw them as a single column, others as two, and the mismatch between the two glyphs compounds the jitter problem above. That ambiguity is why the ASCII `[x]`/`[ ]` pair remains the default rather than a Unicode checkbox glyph.

Tested alternatives that render consistently:

  * `[x]` / `[ ]` — the default, plain ASCII.

  * `[✓]` / `[ ]` — checkmark inside brackets; U+2713 is single-width almost everywhere.

  * `(•)` / `( )` — bullet radio-style indicator.

  * `☒` / `☐` — ballot box with X (U+2612) paired with empty ballot box (U+2610); both single-width in practice, unlike the `☑` pairing above.

```raku
my $cb = Selkie::Widget::Checkbox.new(label => 'Enable notifications');
$cb.set-glyphs(checked => '[✓]', unchecked => '[ ]');
```

`Selkie::Widget::RadioGroup` has the same `[x]`/`[ ]` hardcoding and is a natural follow-up for the same treatment, but that widget is out of scope here.

EXAMPLES
========

Syncing with the store
----------------------

```raku
# Subscribe: reflect store changes into the widget
$app.store.subscribe-with-callback(
    'sync-notif',
    -> $s { $s.get-in('settings', 'notifications') // True },
    -> Bool $v { $cb.set-checked($v) },   # no-op if unchanged — safe
    $cb,
);

# Emit: user toggle dispatches to the store
$cb.on-change.tap: -> Bool $v {
    $app.store.dispatch('settings/set', field => 'notifications', value => $v);
};
```

SEE ALSO
========

  * [Selkie::Widget::RadioGroup](Selkie--Widget--RadioGroup.md) — one-of-many selection

  * [Selkie::Widget::Button](Selkie--Widget--Button.md) — plain action button

### has Str $.label

The label displayed after the `[x]` / `[ ]` indicator. Required.

### has UInt $.debounce-ms

Reject user toggles that arrive within this many milliseconds of the previous accepted user toggle. Defaults to 120ms to collapse duplicate mouse press events and repeat-key bursts. Set to 0 to allow every user activation. Direct `toggle` and `set-checked` calls remain immediate for programmatic state changes.

### has Str $.checked-glyph

Glyph shown when `checked` is True. Defaults to `[x]` — see GLYPHS for alternatives and the equal-width caveat.

### has Str $.unchecked-glyph

Glyph shown when `checked` is False. Defaults to `[ ]`.

### method checked

```raku
method checked() returns Bool
```

Current state.

### method set-checked

```raku
method set-checked(
    Bool:D $v
) returns Mu
```

Set the state, emitting on-change only if the value actually changed. No-op on same-value assignments — safe to call from a store subscription.

### method toggle

```raku
method toggle() returns Mu
```

Flip the state and emit on-change unconditionally.

### method on-change

```raku
method on-change() returns Supply
```

Supply emitting `Bool` each time the state changes.

### method set-focused

```raku
method set-focused(
    Bool $f
) returns Mu
```

Set focus state. Called by `Selkie::App`'s focus dispatcher; the focused-styling palette flips on/off accordingly.

### method is-focused

```raku
method is-focused() returns Bool
```

Whether the checkbox currently has keyboard focus.

### method set-glyphs

```raku
method set-glyphs(
    Str:D :$checked!,
    Str:D :$unchecked!
) returns Nil
```

Set both indicator glyphs at once. See GLYPHS for guidance on keeping `$checked` and `$unchecked` the same width.

