class

Phosphor::Widget(S, M)

Inherits Reference < Object

Abstract base for app-specific widget wrappers.

A Widget sits between a reusable MockWidget and the app's Screen. Its two jobs are:

  1. on_event — translate raw terminal Events into app Msg values. This is the only place in the framework that performs EventMsg translation.
  2. sync — translate the app's state (S) + app.theme into MockWidget::Props. This is the only place in the framework that calls app.theme.

Neither job bleeds into the other: on_event never touches the theme, and sync never returns a Msg. The wrapped MockWidget never sees app types.

Generic over S, the same application state type parameter Screen(S, M) uses — a Widget and the Screen that mounts it always share the same S. Also generic over M, the app's own Msg type — on_event returns M?, not the framework's built-in Phosphor::Msg specifically, so an app can define its own message vocabulary instead of using the shipped one. See Screen(S, M)'s own doc comment for why this is generic at all.

See DESIGN.md § "Widget" for the full rationale and a worked example.

Constructors

new(app : App(S, M))
Source

Instance methods

animation_interval

How often this widget needs a TickEvent to animate, or nil if it never animates. View#animation_interval collects the minimum non-nil value across every mounted widget so App#run knows how long it can block before waking to synthesize a tick.

Defaults to nil. Widgets that wrap an animating MockWidget should override this to delegate to @mock.animation_interval, the same way #hit_test/#handle_cmd delegate.

Source
app

The host application — used in sync to call app.theme.

Source
handle_cmd(cmd : Cmd) : CmdResult

Responds to cmd by mutating the wrapped MockWidget's own render state. Used by View#dispatch to route mouse-wheel scroll commands (Cmd::ScrollUp/Cmd::ScrollDown) by position, the same way hit_test routes clicks — scrolling changes a MockWidget's cursor or scroll offset directly, not the app's own state, so it bypasses Msg and Screen#handle entirely.

Defaults to CmdResult::None. Widgets that wrap a MockWidget should override this to delegate to @mock.handle_cmd(cmd); widgets with no MockWidget need not override it.

Source
hit_test(x : Int32, y : Int32) : Bool

Returns true if (x, y) falls within this widget's last rendered bounds. Used by View#dispatch to route MouseEvents by position instead of focus.

Defaults to false. Widgets that wrap a MockWidget should override this to delegate to @mock.hit_test(x, y); widgets with no MockWidget (e.g. a stub that renders nothing) are never hit.

Source
mount_id

This widget's own mount id (the Symbol passed to View#mount), or nil before that call. Set alongside #view by View#mount. Backs #register_zone, which passes it through as a zone's widget_id so View#dispatch_touch can fall back to this widget when a zone's own id doesn't match any mounted widget.

Source
mount_id=(mount_id : Symbol | Nil)

This widget's own mount id (the Symbol passed to View#mount), or nil before that call. Set alongside #view by View#mount. Backs #register_zone, which passes it through as a zone's widget_id so View#dispatch_touch can fall back to this widget when a zone's own id doesn't match any mounted widget.

Source
on_event(event : Event) : M | Nil

Translates event into an application Msg, or returns nil if this widget does not handle the event.

This is the only place that maps EventMsg. Return nil for every event the widget does not consume; View#dispatch collects non-nil results and forwards them to Screen#handle.

Source
preferred_height(width : Int32) : Int32 | Nil

This widget's preferred row count at the given width, or nil if it has none. View#preferred_height sums this across every mounted widget so Terminal#auto_resize can size an Inline session's block to fit its content.

Defaults to nil. Widgets that wrap a MockWidget with a natural height should override this to delegate to @mock.preferred_height(width), the same way #hit_test/#handle_cmd delegate.

Source
register_zone(id : Symbol, frame : Frame) : Void

Registers frame as a named touch target on the mounted View, under id — a convenience so a widget can call this from its own render after computing its frame, without reaching into View directly. A no-op before #view/#mount_id are set (i.e. before View#mount has run).

id need not match this widget's own mount id — it can name a sub-region (e.g. "action_apply" within an ActionBarWidget mounted as :action_bar). View#dispatch_touch tries id first and falls back to this widget's own #mount_id, passed through here as the zone's widget_id, when no widget is mounted under id itself. See View#register_zone/#zone_at.

Source
render(frame : Frame, buf : Buffer) : Void

Writes this widget's cells into buf within the bounds of frame.

Delegates to the wrapped MockWidget#render. View#render_widget always calls sync before render on each frame, so @mock.props is up-to-date when this runs.

Source
sync(state : S) : Void

Rebuilds the wrapped MockWidget's Props from state and app.theme.

This is the only place that calls app.theme. All theme → Style resolution happens here; the wrapped MockWidget only ever receives already-resolved Style values.

Source
view

The View this widget is mounted on, or nil before View#mount is called. Set by View#mount, not #initialize — a Widget is constructed before it's mounted, so there's no View to reference yet. Backs #register_zone.

Source
view=(view : View(S, M) | Nil)

The View this widget is mounted on, or nil before View#mount is called. Set by View#mount, not #initialize — a Widget is constructed before it's mounted, so there's no View to reference yet. Backs #register_zone.

Source