class

Tryst::App

Inherits Tryst::UI::AppContract < Reference < Object

Ruby interface to Tcl/Tk (mirrors ruby-tryst's lib/tryst.rb). App wraps a Tryst::Interp - the low-level bridge - with the ergonomic API real applications use: creating widgets, evaluating Tcl code, running the event loop.

Constants

BIND_SUBS = {:x => "%x", :y => "%y", :root_x => "%X", :root_y => "%Y", :widget => "%W", :keysym => "%K", :keycode => "%k", :char => "%A", :width => "%w", :height => "%h", :button => "%b", :mouse_wheel => "%D", :type => "%T", :data => "%d"} of Symbol => String

Symbol shorthands for #bind's substitution codes - Tk's own %-codes can always be passed directly instead (e.g. "%K") for anything not listed here. Mirrors ruby-tryst's App::BIND_SUBS (lib/tryst.rb).

RAW_SUB_PATTERN = /\A%[a-zA-Z%#]\z/

Every raw %-code #bind accepts is exactly this shape - Tk's own bind(n) documents %% and %<one letter> as the complete set (%a, %b, ..., %A, %B, ..., plus the literal %% and %#). A raw sub that doesn't match this is rejected outright rather than spliced into the bound script: #bind builds that script by interpolation (the id/sub codes are meant to be the only moving parts, everything else is a fixed template), and Tk re-parses the WHOLE script as Tcl - after its own %-substitution - every time the event fires. A caller-controlled string here would therefore run as arbitrary Tcl on every firing, not just at bind time.

Constructors

new(title : String | Nil = nil, track_widgets : Bool = true, &)

Bootstraps a new App, running block with self rebound to the new instance (mirrors ruby-tryst's App.new { ... } via instance_eval, see epic notes: 'with instance yield' only works spliced in here, never inside #initialize itself - confirmed by direct test).

Source
new(title : String | Nil = nil, track_widgets : Bool = true)

track_widgets: whether to populate #widgets as widgets are created (via a Tcl execution trace on every WIDGET_COMMANDS entry) - see #setup_widget_tracking. Callback cleanup on widget destruction (see #setup_destroy_cleanup) happens either way; ruby-tryst's debug-mode forcing of track_widgets on, and its Debugger integration, are out of scope for this port (see project notes on Debugger).

Source

Instance methods

_pending_exception=(_pending_exception : Exception | Nil)

@api private - set by RepeatingTimer when a tick's on_error: :raise strategy fires, so the error surfaces from the next #update call instead of going through tryst_crystal_callback_dispatch's own rescue (which would just report it as a generic Tcl error).

Source
add_debug_console(keybinding = "<F12>") : Bool

Enable the Tk debug console, toggled with the given keyboard shortcut (default: F12). The console is a built-in interactive Tcl shell, useful for inspecting variables and running Tcl commands at runtime - available on macOS and Windows only; on Linux this is a no-op (Linux has a real terminal instead). @return true if the console was created, false if unavailable on this platform.

Source
add_package_path(path : String) : Nil

Add a directory to Tcl's package search path (::auto_path). Goes through #tcl_invoke, not the string-interpolated lappend ::auto_path {#{path}} ruby-tryst builds - path is exactly the kind of externally-supplied value #tcl_invoke exists to pass safely.

Source
after(ms : Int32, on_error : ErrorPolicy = :raise, &block : -> Nil) : AfterHandle

Schedule a one-shot timer. Calls the block after ms milliseconds. on_error: :raise (default) propagates to Tcl's background error handler, :ignore swallows it. Returns an AfterHandle - pass to #after_cancel to cancel.

Source
after(ms : Int32, on_error : ErrorHandler, &block : -> Nil) : AfterHandle

As above, but hands the exception to on_error instead of applying a policy - see ErrorHandler.

Source
after_cancel(after_id : AfterHandle) : AfterHandle

Cancel a pending #after or #after_idle timer.

Source
after_idle

Schedule a block to run once when the event loop is idle. Returns an AfterHandle - pass to #after_cancel to cancel.

Source
appearance(window = :root) : String | Nil

A window's macOS appearance: "aqua", "darkaqua", or "auto". Returns nil on every other platform.

On Tk 8.6 - all this port targets - the only way in is Tk's private tk::unsupported namespace; the supported wm attributes -appearance spelling arrived in Tk 9. Tk answers with the appearance a window had before the call, and with an empty string if the window has no NSWindow yet, so show and settle a window before trusting this.

Source
aqua?

Whether Tk is running on macOS's Aqua windowing system. Memoized - the windowing system can't change for the life of the process, and the block form caches false as readily as true (a plain ||= would re-run the Tcl call every time on non-macOS).

Source
bind(widget, event : EventArg, *, subs : SubsArg = nil, owner : String | Nil = nil, &block : Array(String), CallbackSignal -> Nil) : String

Bind a Tk event on a widget, with optional substitutions forwarded as the block's Array(String) argument, in order requested. widget accepts a Widget, a path String, or a class tag (e.g. "Entry").

event is never Tk's own event-sequence syntax spelled by hand - see EventSpec.resolve for the full rules. In short: a bare Symbol resolves to whichever of Tk's own native events/keysyms it names (:return, :configure, :click), or a virtual event otherwise (:drop_file -> "<<DropFile>>"); an Array is always a native modifier combo ([:control, :s]). A raw Tk sequence String ("<<DropFile>>", "<Control-s>") remains a fully-supported escape hatch for anything the Symbol vocabulary doesn't cover yet.

subs: can be a single Symbol/String or an Array of them - Symbols map through BIND_SUBS, a String must be a raw Tk %-code.

@example Mouse click with window coordinates app.bind(".c", :click, subs: [:x, :y]) { |values, _signal| puts values.join(",") } @example No substitutions app.bind(".btn", :enter) { |_values, _signal| highlight } @example A custom virtual event with its own data app.bind(".", :drop_file, subs: :data) { |values, _signal| puts values[0] } @example A modifier combo app.bind(".", [:control, :s]) { save } @example Raw Tcl expression (for codes not in BIND_SUBS) app.bind(".c", :click, subs: "%T") { |values, _signal| ... }

owner: names the widget whose destruction releases this binding's callback, and defaults to widget itself - always right when widget IS a widget path. It matters only when binding to a BINDTAG, which is not a window and so never fires the <Destroy> that cleanup hangs off (see CallbackRegistry#forget_all_for_path): left alone, such a callback is tracked under a key nothing ever sweeps and lives as long as the process. Name the widget whose lifetime the tag actually follows - for a scroll region's shared wheel tag, the canvas being scrolled. This mirrors how every other non-window callback holder is already tracked (a menu entry under its menu, a text tag under its text widget, a canvas item under its canvas).

Leave it nil for a genuine CLASS tag ("Entry", "Button"): those outlive every individual widget on purpose, so there is no owner to name and nothing to release.

Source
bool_to_tcl(val) : String

Converts a Crystal truthy/falsy value to a Tcl boolean string. See Tryst.bool_to_tcl.

Source
bring_to_front(window = :root) : Nil

Show a window AND put it in front with the keyboard focus - what launching an app should do, where #show alone only deiconifies. See Interp#bring_to_front for what the sequence is and why the -topmost pin has to be released again; the release needs one turn of the event loop, so call this before entering #mainloop (Session#run already does).

Only the Widget-or-path coercion lives here - window accepts a Widget, a path String, or :root, the same as #show/#hide.

Source
busy(window = :root, &)

Show the busy cursor on a window while the block runs, and return whatever the block returned. Defaults to the root window (:root). window accepts a Widget, a path String, or :root.

Tk's busy cursor also swallows mouse events for the window and its children while held, which is the point - it's how you stop a user clicking into a half-finished operation. The cursor is forgotten again even if the block raises, so a failure can't leave the window wedged looking busy forever.

Source
callback

Wraps a block as a Proc(Array(String), CallbackSignal, Nil), for passing as a Proc-valued option to #command/#create_widget (e.g. command:, validatecommand:) - app.command(:button, ".b", command: app.callback { ... }). A block passed as a **kwargs value (unlike one passed directly to a method expecting &block) gets no help inferring its parameter types from context, so it can't just declare fewer params and have Crystal fill in the rest the way #register_callback/#bind's blocks can - wrapping it here, where the block IS passed directly to a &block parameter, is what gives it that flexibility (0, 1, or 2 params, extras ignored) before handing the correctly-typed result back as a plain value.

Source
callback_registry

Built on first use. Constructing it takes self, so building it lazily keeps self from escaping mid-construction - which would mark every ivar not yet assigned at that point as nilable for good.

Source
choose_color(initial : String | Nil = nil, title : String | Nil = nil, parent = nil) : String | Nil

Show the native color picker dialog. initial: e.g. "#ff0000". Returns the chosen color as "#rrggbb", or nil if cancelled.

Source
choose_dir(initialdir : String | Nil = nil, mustexist : Bool = false, title : String | Nil = nil, parent = nil) : String | Nil

Show the native "choose directory" dialog. mustexist: restrict the choice to an already-existing directory (Tk's own default is false, allowing a not-yet-created one). Returns the chosen directory path, or nil if cancelled.

Source
choose_open_file(filetypes : FileTypes | Nil = nil, initialdir : String | Nil = nil, initialfile : String | Nil = nil, title : String | Nil = nil, multiple : Bool = false, parent = nil) : String | Array(String) | Nil

Show the native "choose file to open" dialog. filetypes: e.g. [{"PNG Images", ".png"}, {"All Files", "*"}] - the second element of each pair can also be an array of extensions ({"Images", [".png", ".jpg"]}). multiple: allow selecting more than one file. Returns the chosen path (an array of paths if multiple:), or nil if the dialog was cancelled.

Source
choose_save_file(filetypes : FileTypes | Nil = nil, initialdir : String | Nil = nil, initialfile : String | Nil = nil, title : String | Nil = nil, defaultextension : String | Nil = nil, confirmoverwrite : Bool = true, parent = nil) : String | Nil

Show the native "choose file to save" dialog. filetypes: see #choose_open_file. defaultextension: appended if the typed filename doesn't already have one. confirmoverwrite: ask before overwriting an existing file (Tk's own default is true; pass false to skip the confirmation). Returns the chosen path, or nil if cancelled.

Source
clipboard

Typed wrapper around Tk's clipboard command family - see Clipboard. Built on first use, same reasoning as #winfo.

Source
command(cmd, args : Array(TclArgValue), kwargs : Hash(String, TclArgValue)) : String

Same as the splat overload above, for callers that already have a built Array(TclArgValue)/Hash(String, TclArgValue) in hand (e.g. Realizer forwarding a Node's own opts, only known at runtime) - a runtime Array/Hash can't be re-splatted into another method's own *args/**kwargs (same reason #raw_command needed this exact overload

  • see its own comment).
Source
command(cmd, *args : TclArgValue, **kwargs) : String

Build and evaluate a Tcl command from Crystal values. Positional args are converted: Symbols pass bare, Procs become callbacks (bind-shaped

  • see #callback), everything else is brace-quoted via #tcl_arg_value. Keyword args become "-key value" option pairs.

Any Proc-valued arg or kwarg is tracked and released on overwrite, explicit removal, or the owning widget's destruction. Widget type is inferred automatically from calls shaped like widget creation (a WIDGET_COMMANDS name as cmd, the new path as the first positional arg).

Consults CommandInterceptors.for_type(type) for the widget type recorded at cmd's path (see #record_widget_type) before falling through to #raw_command's generic handling. Raises AmbiguousCommandError if more than one registered interceptor claims the same call. @example app.command(:pack, ".btn", side: :left, padx: 10)

evaluates: pack .btn -side left -padx {10}

Source
create_widget(type, path : String | Nil = nil, parent = nil, idempotent : Bool = false, **kwargs) : Widget

Create a Tk widget and return a Widget wrapper.

Auto-generates a unique path if none is given, derived from the widget type and a monotonic counter. parent accepts a Widget, a path String, or nil. idempotent: skip the creation command if a widget already exists at path (see #menu) - for widgets meant to be fetched by a stable, caller-chosen path and reused across many calls, rather than freshly created each time.

@example Auto-named btn = app.create_widget("ttk::button", text: "Click")

btn.path => ".ttkbtn1"

@example Nested under a parent frm = app.create_widget("ttk::frame") btn = app.create_widget("ttk::button", parent: frm, text: "Click")

btn.path => ".ttkfrm1.ttkbtn1"

Source
dark?(window = :root) : Bool

Whether a window is currently being displayed in dark mode. Always false off macOS. Reflects the window's own forced appearance when #set_appearance pinned one, and the system preference otherwise.

Source
debug_info

A live snapshot of App's own per-path bookkeeping that #widgets doesn't cover - "is something still tracking a destroyed widget's path." Currently just @widget_types_by_path (#record_widget_type, read by #command on every call): unlike #widgets, it's written unconditionally regardless of track_widgets:, so it needs its own way to confirm #setup_destroy_cleanup actually keeps it bounded rather than growing across a create/destroy loop. A key absent from the result means empty, not present-as-zero.

Source
destroy(widget = :root) : Nil

Destroy a widget and all its children. widget accepts a Widget, a path String, :root, or the default (the root window).

Destroying the root also deletes the interpreter (see #setup_destroy_cleanup) - once this returns, or once the callback it was called from has returned, every further Tcl call on this App raises TclError as already-deleted. There is nothing useful left to call by then anyway: Tk itself refuses every widget command after the root is gone.

Source
ensure_tcl_helper(name : Symbol, & : -> String) : Nil

Evaluate script once per App instance under name, skipping it on later calls. Meant for widget-behavior code that needs to define a Tcl-side helper proc without re-sending and re-parsing that definition on every call.

Source
event_loop_running?

True while any event loop is actively servicing events - see Interp#event_loop_running?. @api private (RepeatingTimer).

Source
every(ms : Int32, on_error : ErrorPolicy = :raise, &block : -> Nil) : RepeatingTimer

Schedule a repeating timer. Calls the block every ms milliseconds until cancelled. The block runs on the main thread in the event loop, so it must be fast (don't block the UI). Returns a RepeatingTimer - call #cancel on it later.

@example Basic polling loop timer = app.every(50) { update_display } timer.cancel # stop later

Source
every(ms : Int32, on_error : ErrorHandler, &block : -> Nil) : RepeatingTimer

As above, but hands each tick's exception to on_error instead of applying a policy. Only this form keeps the timer running after an error - see ErrorHandler.

Source
font_metrics(font : String) : NamedTuple(ascent: Int32, descent: Int32, linespace: Int32)

A font's :ascent, :descent and :linespace in pixels. See Interp#font_metrics.

Source
get_variable(name) : String

Get a Tcl variable's value. name accepts array-element and namespaced forms.

Source
grab_release(window = :root) : Nil

Release a grab previously set with #grab_set. Defaults to the root window ("."). window accepts a Widget or a path String. Prefer app.window(window).grab_release for new code - this flat method is kept for parity with ruby-tryst and just delegates there.

Source
grab_set(window = :root, global : Bool = false) : Nil

Set the input grab on a window. Defaults to the root window ("."). window accepts a Widget or a path String. Prefer app.window(window).grab_set for new code - this flat method is kept for parity with ruby-tryst and just delegates there.

Source
hide(window = :root) : Nil

Hide a window without destroying it. Defaults to the root window (:root). window accepts a Widget, a path String, or :root.

Source
interp
Source
mainloop

Enter the Tk event loop. Blocks until the application exits.

ruby-tryst warns here if running under IRB/Pry (mainloop would make the REPL unresponsive) - Crystal has no equivalent REPL culture to detect the same way, so that warning is skipped rather than forced into a shape that doesn't really fit. Raises a RepeatingTimer's on_error: :raise exception the same way #update does (see there), checked once per loop iteration so a timer error surfaces here too rather than sitting unread for the rest of the run - #update is never called while #mainloop owns the event loop.

Source
make_list(args : Enumerable(String)) : String
Source
make_list
Source
make_list(*args : String) : String

Builds a properly-escaped Tcl list from Crystal strings. See Tryst.make_list.

Source
measure_chars(font : String, text : String, max_pixels : Int32, partial_ok : Bool = false, whole_words : Bool = false, at_least_one : Bool = false) : NamedTuple(bytes: Int32, width: Int32)

How much of text fits within max_pixels (-1 for unlimited), as :bytes and their :width in pixels - for truncation, ellipsis and line wrapping. See Interp#measure_chars for the flags.

Source
message_box(message : String, title : String | Nil = nil, detail : String | Nil = nil, icon : Symbol = :info, type : Symbol = :ok, default : Symbol | Nil = nil, parent = nil) : Symbol

Show a message box with one or more buttons. icon: :error/:info/:question/:warning. type: :ok/:okcancel/:abortretryignore/:yesno/:yesnocancel/:retrycancel - which button(s) to show. default: which button is focused by default (e.g. :cancel); Tk's own choice if omitted. Returns the pressed button as a Symbol - :ok, :cancel, :yes, :no, :abort, :retry, or :ignore.

Source
native_window_handle(window = :root) : NativeWindow

The platform window identifier behind a widget, for handing to something that draws into a window Tk owns. window accepts a Widget, a path String, or :root. Raises unless the widget is mapped - see Interp#native_window_handle.

Source
off_thread(new_thread : Bool = false, &block : -> T) : T forall T

Runs block off Tk's own thread and returns its result, raising whatever exception block raised instead - same contract as calling block synchronously would have, just relocated.

Route slow File I/O, DNS, and TLS calls through this rather than calling them directly: on Tk's thread those calls run bare, with the thread pinned (see syscall_guard.cr - Crystal's SYSMON would otherwise migrate the fiber onto a different OS thread mid-call, and Tcl requires every call into a given interpreter to come from the one OS thread that created it, forever), so Tk's event loop and every other fiber on it stall for as long as the call takes. A sub-millisecond settings read is harmless there; a network fetch is a frozen UI. Interp#check_thread_affinity! is the hard-error backstop should anything still reach Tk from the wrong thread.

new_thread: false (default) dispatches to Tryst::OffThreadWorker's single persistent, lazily-started thread - shared across every default-mode call in the process, so concurrent calls queue behind each other rather than running in parallel. Pass new_thread: true for a one-shot dedicated Isolated thread instead (same shape Tryst::BackgroundWork uses per task) when a call is slow/heavy enough that it shouldn't make other off_thread calls wait behind it.

@example content = app.off_thread { File.read(path) }

Source
on_close(window = :root, &block : Array(String), CallbackSignal -> Nil) : Nil

Register a handler for the window manager's close button. Defaults to the root window ("."). window accepts a Widget or a path String. Prefer app.window(window).on_close { } for new code - this flat method is kept for parity with ruby-tryst and just delegates there.

Source
on_widget_destroyed(owner : T, &block : T, String -> Nil) : Nil forall T

Same hook, scoped to owner's own lifetime via a WeakRef: the subscription is swept once owner is unreachable, rather than staying registered (and permanently unregisterable) for the life of the App the way the plain block form above is - what makes this one safe for a per-widget subscription instead of just a once-per-App one like Session's own node_destroyed hook.

block receives owner as an explicit argument for exactly this reason - write against that, not against self/an outer local, or the block itself pins owner reachable and defeats the WeakRef.

Source
on_widget_destroyed

Register a callback fired with a widget's own Tk path immediately after Tk destroys it (see #setup_destroy_cleanup) - regardless of whether the destroy was explicit (an app-level #destroy call, or a tryst-ui Handle#destroy!) or implicit (the window manager's own close button, or Tk recursively destroying a descendant along with its parent). Core App has no opinion on what a "widget path" ultimately represents to a caller - tryst-ui's Session hangs its own Document-cleanup off this (see Document#node_destroyed), so an implicit destroy's bookkeeping converges on the exact same path an explicit one already used.

Source
package_names

Every package this interpreter currently knows about - scans ::auto_path for package indexes first (see #scan_packages), so a package nothing has required yet still shows up.

Source
package_present?(name : String) : Bool

Whether a package is already loaded (package required, not just discoverable on ::auto_path) in this interpreter.

Source
package_versions(name : String) : Array(String)

Every version of a package available on ::auto_path - scans first, same as #package_names.

Source
popup_menu(menu, x : Int32, y : Int32, entry = nil) : Nil

Pop up a menu at the given screen coordinates. menu: a Widget or path String. entry: index or label of the entry to show as active.

Source
raw_command(cmd, args : Array(TclArgValue), kwargs : Hash(String, TclArgValue)) : String

Same as the splat overload above, for callers that already have a built Array(TclArgValue)/Hash(String, TclArgValue) in hand - a CommandInterceptors block, for instance, which receives exactly this shape and can't re-splat a runtime Array into another method's own *args (verified directly, same reason Tryst.make_list needed its own Enumerable overload: "argument to splat must be a tuple"). Mirrors ruby-tryst's raw_command being directly callable from within an interceptor.

Source
raw_command(cmd, *args : TclArgValue, **kwargs) : String

The dumb Tcl builder underneath #command - no interceptor lookup, no per-widget-type awareness. Used by #command's own generic fallback; prefer #command, call this directly only from within a future interceptor. Any Proc here still gets registered as a real, working callback - it just isn't tracked for release the way #command's own kwargs are (see #track_widget_option_callbacks).

Built as a plain argv array passed to Interp#tcl_invoke (Tcl_EvalObjv) rather than a joined string handed to #tcl_eval, so no value needs escaping - unbalanced braces, $, [, newlines, whatever, all pass through verbatim.

Source
register_callback(relay_break : Bool = true, &block : Array(String), CallbackSignal -> Nil) : String

Register a Crystal callable as a Tcl callback. The block's second argument is a Tryst::CallbackSignal - ignore it unless the callback needs to signal Tcl control flow (see Interp#register_callback).

Source
register_drop_target(widget) : Nil

Register widget as a native OS file-drop target. Once a real drag actually lands on it, a <<DropFile>> virtual event fires with the dropped path(s) as a Tcl list in its -data field:

app.register_drop_target(:root)
app.bind(:root, :drop_file, subs: :data) do |values, _signal|
  paths = app.split_list(values[0])
  puts "Dropped #{paths.size} file(s): #{paths.inspect}"
end

Currently a documented no-op: the native platform layer that detects a real OS-level drag and fires the event (X11/XDND on Linux, NSDraggingDestination on macOS, WM_DROPFILES on Windows) - a genuine per-platform native-code undertaking, not something FFI against an already-built system library covers - hasn't shipped yet. Calling this today does nothing harmful and nothing useful; <<DropFile>> can still be exercised directly for testing via event generate widget <<DropFile>> -data {...} (see this method's own spec cases), and any caller written against this API today starts working for real the moment the native layer lands, with no code change needed here.

Source
require_package(name : String, version : String | Nil = nil) : String

Load a Tcl package into this interpreter. Raises TclError (with a clearer message naming the package) if it isn't found on ::auto_path - see #add_package_path.

Source
reset_widget_counters!

Resets #create_widget's auto-naming counters back to zero, without touching any already-created widget. Test-only in practice (mirrors ruby-tryst's Tryst::TestWorker reset_widget_counters!, test/tryst_test_worker.rb) - the persistent Tk test worker calls this between tests so auto-named paths (".ttkbtn1", ...) don't keep incrementing across tests that never destroyed their own widgets.

Source
screenshot_rect(window = :root) : String

A widget's on-screen content rectangle, formatted for macOS screencapture -R: "x,y,w,h". Chrome-free by construction - a window manager's title bar/shadow is a separate frame that winfo's rootx/rooty/width/height never include. See scripts/screenshot.sh.

Source
set_appearance(mode : Appearance, window = :root) : Nil

Force a window's macOS appearance, opting it out of the system light/dark preference; :auto hands it back. A no-op on every other platform. Not appearance=, since it takes the window to act on as well as the mode - the same reason #set_window_title isn't a setter.

Source
set_appearance(mode : String, window = :root) : Nil

Raw-value overload, for an appearance name Appearance doesn't cover.

Source
set_variable(name, value) : String

Set a Tcl variable. Useful for widget textvariable and variable options. Goes through Tcl_SetVar directly (no re-parsing), so the value never needs escaping - braces, backslashes, $, [, whatever, all safe. name accepts array-element and namespaced forms.

Source
set_window_geometry(geometry : String, window = :root) : Nil

Set a window's geometry (e.g. "400x300", "400x300+100+50"). Defaults to the root window ("."). window accepts a Widget or a path String.

Source
set_window_max_size(width : Int32, height : Int32, window = :root) : Nil

Set a window's maximum size, as {width, height} in pixels. Defaults to the root window ("."). window accepts a Widget or a path String.

Source
set_window_min_size(width : Int32, height : Int32, window = :root) : Nil

Set a window's minimum size, as {width, height} in pixels. Defaults to the root window ("."). window accepts a Widget or a path String.

Source
set_window_resizable(width : Bool, height : Bool, window = :root) : Nil

Set whether a window is resizable. Defaults to the root window ("."). window accepts a Widget or a path String.

Source
set_window_title(title : String, window = :root) : Nil

Set a window's title. Defaults to the root window ("."). window accepts a Widget or a path String.

Source
show(window = :root) : Nil

Show a window. Defaults to the root window (:root). window accepts a Widget, a path String, or :root.

Source
split_list(str : String | Nil) : Array(String)

Splits a Tcl list string into a Ruby array of strings. See Tryst.split_list.

Source
style

Typed wrapper around Tk's ttk::style command family - see Style. Built on first use, same reasoning as #winfo.

Source
tcl_arg_value(value : TclArgValue) : String

Resolves a Crystal value to the plain string #raw_command passes as one tcl_invoke argv element - no Tcl quoting of any kind, since tcl_invoke (Tcl_EvalObjv) never re-parses its arguments.

Source
tcl_eval(script : String) : String

Evaluate a raw Tcl script string and return the result. Prefer #tcl_invoke for building commands from Crystal values.

Source
tcl_invoke(args : Enumerable(String)) : String
Source
tcl_invoke(*args : String) : String

Invoke a Tcl command with pre-split arguments (no Tcl parsing).

Source
tcl_major_version

:ditto:, just the major version number.

Source
tcl_patch_level

The Tcl/Tk version actually loaded at runtime (e.g. "9.0.3") - see Interp#tcl_patch_level's own doc comment for why this, not the compile-time TCL_MAJOR_VERSION constant, is what a runtime feature-availability check should read.

Source
tcl_to_bool(str : String) : Bool

Converts a Tcl boolean string to a Crystal Bool. See Tryst.tcl_to_bool.

Source
text_width(font : String, text : String) : Int32

Pixel width of text in a given font. See Interp#text_width - this goes through Tk's C font API, not the slower Tcl font measure.

Source
unbind(widget, event : EventArg, *, owner : String | Nil = nil) : Nil

Remove an event binding previously set with #bind. Pass the same owner: #bind was given, or this reconciles a different container and the callback is never released.

Source
unregister_callback(id : String) : Nil

Remove a previously registered callback by its id.

Source
unsafe_interp_ptr

See Interp#unsafe_ptr's own doc comment - the one deliberate escape hatch for a satellite shard's FFI that needs the real Tcl_Interp* (erased to Void*).

Source
update

Process all pending events and idle callbacks, then return. Raises an exception a RepeatingTimer's on_error: :raise tick handling stashed via #_pending_exception= (see there for why it can't just raise directly from the tick).

Source
update_idletasks

Process only pending idle callbacks (e.g. geometry redraws), then return.

Source
widgets
Source
window(path = :root) : Window

A single toplevel window, addressed by path - groups wm subcommands and composite window-lifecycle behaviors (#on_close, #grab_set/#grab_release, #modal) into one object. Defaults to the root window ("."). path accepts a Widget or a path String.

Source
window_geometry(window = :root) : String

Get a window's current geometry. Defaults to the root window ("."). window accepts a Widget or a path String.

Source
window_max_size(window = :root) : Tuple(Int32, Int32)

Get a window's maximum size. Defaults to the root window ("."). window accepts a Widget or a path String.

Source
window_min_size(window = :root) : Tuple(Int32, Int32)

Get a window's minimum size. Defaults to the root window ("."). window accepts a Widget or a path String.

Source
window_resizable(window = :root) : Tuple(Bool, Bool)

Get whether a window is resizable ({width_resizable, height_resizable}). Defaults to the root window ("."). window accepts a Widget or a path String.

Source
window_title(window = :root) : String

Get a window's current title. Defaults to the root window ("."). window accepts a Widget or a path String.

Source
winfo

Typed wrapper around Tk's winfo command family (width, exists?, ...) - see Winfo. Built on first use: constructing it takes self, and doing that inside #initialize would mark every ivar not yet assigned at that point as nilable for good.

Source
with_font(font : String, & : FontHandle -> T) : T forall T

Resolves font once and yields a FontHandle to measure many strings against it - for a per-glyph layout pass that would otherwise pay #text_width/#font_metrics/#measure_chars's own Tk_GetFont/Tk_FreeFont cost per glyph. See Interp#with_font.

Source

Nested types