class

Tryst::UI::Handle

Inherits Reference < Object

The single handle type for a node, valid across both phases - during build you compose/name/record-events on it; live methods (#path, #configure) raise NotRealizedError until the node's realized slot is filled in by the realizer, then the same Handle object drives the real widget through it.

One method is deliberately absent: #on_drag, which CanvasItem's own on_drag/#draggable wait on too - see canvas_item.cr's own doc comment for why both are held back together. The window lifecycle (show/hide/modal/grab_release/on_close) IS ported, alongside the :window widget type it acts on; only its Screens/ModalStack integration is still outstanding.

Resolves this node's own WidgetType#addressing strategy from the registry, falling back to WidgetAddressing for an unregistered type

  • the registry now has a real second strategy to resolve (MenuEntryAddressing, for a menu entry with no Tk path of its own), matching ruby's own (WidgetTypes.for_type(node.type)&.addressing || WidgetAddressing).new(node).

Constructors

new(node : Node)
Source

Instance methods

app

The underlying app this widget was realized into. Raises NotRealizedError before realize.

Source
arc(*coords, **opts) : CanvasItem

An arc/pie-slice/chord along the oval inscribed in the bounding box [x1, y1, x2, y2]. Only valid on a ui.canvas handle.

Source
bitmap(*coords, **opts) : CanvasItem

A stipple bitmap anchored at [x, y]. Only valid on a ui.canvas handle.

Source
configure

Mutate the live widget's options - delegated entirely to this node type's addressing strategy, so Handle itself carries no per-type knowledge of how to reach it. Raises NotRealizedError before realize; OptionError (not a bare TclError) for an unknown option - see option_error.cr.

Source
destroy!(defer : Bool | Nil = nil) : Nil

Tears down this node's live widget (and everything under it), releasing its callbacks via tryst's existing <Destroy> cleanup, and unlinks the node from the retained tree so it stops being reachable at all, not just Tk-dead.

A descendant node, or a DIFFERENT node whose Tk window Tk itself destroys (the window manager's own close button, most commonly), gets exactly the same bookkeeping done for it, off the same real Tk <Destroy> this triggers - see Document#node_destroyed, which is the one thing both an explicit #destroy! and an implicit destroy converge on. #perform_destroy! below still does its own node's bookkeeping directly too, redundantly but harmlessly with whatever #node_destroyed also does for that same node - so this stays reliable even for a Handle built against a FakeApp with no real Tk <Destroy> mechanism to fire at all (see handle_spec.cr).

Destroying a widget SYNCHRONOUSLY from inside the click handler of one of its own descendants (a dialog's own "Close" button tearing down the dialog it lives in) is a real Tk hazard: ttk::button (and others) queue their own internal bindings for that SAME click, which then run against a widget that's already gone. defer absorbs this automatically.

defer: nil (the default) auto-detects: defers to the next Tk idle point (Tryst.in_callback? true - the hazard above) so the current click finishes first, or destroys synchronously otherwise (a script/test with no event loop running has nothing to defer TO, and wants "gone when this call returns" semantics). Pass explicitly to override either way.

Safe to call on an already-gone node - whether that's because this same handle's own deferred destroy hasn't run yet, or because the node stopped being realized some other way in the meantime (an ancestor's destroy, or Tk's own doing - e.g. the window manager's close button). Use #configure or another realized-only method instead of #destroy! to actually detect that case; those raise NotRealizedError.

Source
disable

Shorthand for configure(state: :disabled) - greyed out, not interactive/invocable. Raises NotRealizedError before realize.

Source
ellipse(*coords, **opts) : CanvasItem

An ellipse inscribed in the bounding box [x1, y1, x2, y2]. Only valid on a ui.canvas handle.

Source
enable

Shorthand for configure(state: :normal) - Tk's own default state. Raises NotRealizedError before realize.

Source
events

Every event binding declared on this node so far, in declaration order - on_click/on_key/on_right_click and friends all funnel through here. Meaningful at any phase: before realize these are still queued (nothing wired to Tcl yet), after realize they're the bindings actually in effect.

Source
grab_release

Release a grab previously taken by #modal. Safe whether or not one was ever held. Only valid on a ui.window handle.

Source
hide

Hide the window again: releases any grab #show took (a no-op if it wasn't modal - grab_release is always safe) and withdraws it. Only valid on a ui.window handle.

Source
image(*coords, **opts) : CanvasItem

A Tk image anchored at [x, y] - which image via image:, named the way every other Tk image option names one:

canvas.image(0, 0, image: photo.name, anchor: :nw)

Takes the image's Tcl name rather than a Tryst::Photo itself, since TclArgValue (what an option value has to be) has no Photo member - Photo#to_s does return the name, so .name and interpolation agree. Only valid on a ui.canvas handle.

Has no counterpart in ruby-tryst's tryst-ui, whose Handle stops at #bitmap: without this there is no way to put a photo on a ui.canvas at all, which rules out anything drawing through Tryst::Photo (a paint layer is a photo canvas item).

Source
line(*coords, **opts) : CanvasItem

A straight line through the given points - [x1, y1, x2, y2, ...], flat or nested, two or more points. Only valid on a ui.canvas handle. Raises ArgumentError if this handle isn't a canvas. Raises NotRealizedError before realize.

Source
name

The node's explicit name.

Source
on(event : EventArg, *, subs : SubsArg = nil, &block : Array(String), CallbackSignal -> Nil) : Handle

The escape hatch for any event the on_* methods above don't curate - a custom virtual event, or a native combo none of them expose. Scoped to this handle's own widget, so no path/target argument is needed the way session.app.bind's would be. event follows the same rules as App#bind: a bare Symbol/Array resolved against Tk's real vocabulary (EventSpec.resolve), or a raw Tk sequence String as its own escape hatch.

Source
on_action

Fires when the widget is activated, via Tk's own -command option. Prefer this to #on_click for anything meant to be pressed: -command also fires on keyboard activation of a focused widget (Space on a button), and treats a press dragged off the widget before release as a cancel, where a raw <Button-1> binding has already fired on the way down.

Only for types whose Tk command actually takes -command (see WidgetType#takes_command?) - anything else raises, rather than quietly setting an option Tk will ignore.

Source
on_click

Fires on a left click. A literal <Button-1> binding: it fires on the press, wherever the release lands, and never on a keyboard activation - see #on_action for the option most pressable widgets actually want.

Source
on_click(*subs : Symbol | String, &block : Array(String), CallbackSignal -> Nil) : Handle

ditto, asking for event details alongside it - the same substitutions App#bind takes (:x/:y for widget coordinates, :root_x/:root_y for screen ones, :button, ..., or a raw Tk %-code), arriving in the block's args in the order named.

Without this a click handler knows THAT a click happened and nothing about where, which is unusable on a canvas: one binding on the whole widget plus the coordinates is how a grid of cells gets handled without a callback per cell.

Source
on_close

Fires when the window's close button is pressed (titlebar close box, Cmd-W, Alt-F4, ...). tryst's own default - destroy the window

  • only applies while nothing else has claimed it, so the block decides whether the window actually closes at all: call #destroy! if that's what you want, or #hide to keep a palette window around for later.

Wired straight away once realized, and queued onto the node otherwise (Realizer picks it up from there), so this reads the same either side of realize - the same before/after pair the other on_* methods here get. Declaring on_close: as a build option does the same thing; this is for setting or replacing one after the fact.

Only valid on a ui.window handle - the root window's own close handler isn't a Handle's to set, since the root has no Handle; reach it through the app instead (session.app.on_close). Raises ArgumentError on any other type.

Source
on_key(spec : Symbol | String, &block : Array(String), CallbackSignal -> Nil) : Handle

Fires on a key press. spec is either a friendly Symbol (:enter, :escape, :up, ...) or a "Modifier-Modifier-Key" String ("Ctrl-s", "Ctrl-Shift-s") - see Keysyms.

Source
on_release

Fires when the left button is RELEASED. The other half of a press-and-hold interaction: #on_click fires going down, this one coming back up, and only together can a widget offer the classic "drag off before releasing to cancel" behaviour.

Source
on_release(*subs : Symbol | String, &block : Array(String), CallbackSignal -> Nil) : Handle

ditto, with event substitutions - see #on_click's own overload.

Source
on_right_click

Fires on a right click, however the platform spells it (Button-3 on Linux/Windows, Button-2 or Control-Button-1 on macOS). Handle it yourself with a block, or use the overload below to pop up a menu instead.

Source
on_right_click(*subs : Symbol | String, &block : Array(String), CallbackSignal -> Nil) : Handle

ditto, with event substitutions - see #on_click's own overload. Each platform spelling gets the same subs, so a handler reads its coordinates the same way wherever the click came from.

Source
on_right_click(menu : Handle) : Handle

Pops menu up at the click's screen position. Raises ArgumentError unless menu is a :menu or :context_menu handle - a Handle's node type isn't part of its static type, so that one stays a runtime check.

Source
on_tab_changed

Fires when the selected tab changes (Tk's <<NotebookTabChanged>>). The block is handed the newly selected tab's own name where it has one, and its plain zero-based index where it doesn't - preferring a name over a raw Tk index, the way every other lookup in the DSL does.

Only valid on a ui.tabs handle. Raises ArgumentError otherwise.

Source
options

What Tk currently thinks this widget's options are right now, straight from a bare configure. Keyed by option name with no leading dash ("text", "state"), values as Tk reports them. Raises NotRealizedError before realize.

Source
oval(*coords, **opts) : CanvasItem
Source
path

This node's live address - the real Tk widget path. Raises NotRealizedError before realize.

Source
polygon(*coords, **opts) : CanvasItem

A closed shape through the given points - [x1, y1, x2, y2, ...], flat or nested, three or more points. Only valid on a ui.canvas handle.

Source
rectangle(*coords, **opts) : CanvasItem

A rectangle with corners [x1, y1, x2, y2]. Only valid on a ui.canvas handle.

Source
show

Reveal a declared window (they're created withdrawn - see widget_types/window.cr): positions it just clear of the parent it's nested under, deiconifies, raises it to the front, and - only if it was declared modal: true - grabs input and focuses it via #modal. Only valid on a ui.window handle. Raises ArgumentError otherwise, NotRealizedError before realize.

Source
tagged(tag : String | Symbol | Int32) : CanvasItem

A handle onto whatever items currently carry tag - zero, one, or many (see CanvasItem, which addresses a tag and an id identically). Doesn't create anything; a shape-creation method (e.g. #line) already returns a single-item handle for its own new item - this is for addressing a shared tags: group (or reaching an item by an id you already have) after the fact. Only valid on a ui.canvas handle.

Source
text(*coords, **opts) : CanvasItem

Text anchored at [x, y]. Only valid on a ui.canvas handle.

Source
text_content

This widget's content API - insert/get/delete, named formats, markers, search, embedded images. See TextContent for the whole surface. Only valid on a ui.text_area handle.

realized.path, so a text_area wrapped in its own scrollbar hands back the text widget rather than the wrapper frame around it.

Source
type

The node's type, e.g. :button.

Source