Teek::App Class
Inherits: Object
Instance Methods ↑
add_debug_console(keybinding '<F12>')
Enable the Tk debug console. The console starts hidden and can be toggled with the given keyboard shortcut (default: F12).
The Tk console is a built-in interactive Tcl shell — useful for inspecting variables, running Tcl commands, and debugging widget layouts at runtime. It is available on macOS and Windows only; on Linux this method is a no-op (Linux has the real terminal).
Parameters
keybindingString— Tk event to toggle the console (default: "")
Returns Boolean — true if the console was created, false if
unavailable on this platform
Example
app = Teek::App.new
app.add_debug_console # F12 toggles console
app.add_debug_console("<F11>") # custom key
add_package_path(path)
Add a directory to Tcl’s package search path.
Parameters
pathString— directory containing Tcl packages
Returns void
after(ms, on_error: :raise, &block)
Schedule a one-shot timer. Calls the block after ms milliseconds.
Parameters
msInteger— delay in millisecondson_error:raise, Proc, nil— error handling strategy: -:raise(default) — exception propagates to Tcl background error handler. -Proc— called with the exception; error is swallowed. -nil— error is silently swallowed.
Returns String — timer ID, pass to #after_cancel to cancel
@yield block to call when the timer fires
after_cancel(after_id)
Cancel a pending #after or #after_idle timer.
Parameters
after_idString— timer ID returned by#afteror#after_idle
Returns void
after_idle(&block)
Schedule a block to run once when the event loop is idle.
Returns String — timer ID, pass to #after_cancel to cancel
@yield block to call when the event loop is idle
appearance
Get the macOS window appearance. No-op (returns nil) on non-macOS.
Returns String, nil — "aqua", "darkaqua", "auto", or nil on non-macOS
Example
app.appearance # => "aqua", "darkaqua", or "auto"
app.appearance = :light # force light mode
app.appearance = :dark # force dark mode
app.appearance = :auto # follow system setting
See also
appearance=(mode)
Set the macOS window appearance. No-op on non-macOS.
Parameters
modeSymbol, String—:light,:dark,:auto, or a raw Tk value
Returns void
bool_to_tcl(val)
Convert a Ruby boolean to a Tcl boolean string (“1” or “0”).
Parameters
valBoolean
Returns String — "1" or "0"
busy(window: '.')
Show a busy cursor on a window while executing a block. The cursor is restored even if the block raises.
Parameters
windowString— Tk window path
Returns — the block's return value
@yield the work to perform while busy
choose_color(initial: nil, title: nil, parent: nil)
Show the native color picker dialog.
Parameters
initialString, nil— initial color (e.g."#ff0000")titleString, nil— dialog window titleparentString, nil— parent window (defaults to the root window)
Returns String, nil — the chosen color as "#rrggbb", or nil if cancelled
choose_dir(initialdir: nil, mustexist: false, title: nil, parent: nil)
Show the native “choose directory” dialog.
Parameters
initialdirString, nil— directory the dialog starts inmustexistBoolean— restrict the choice to an already-existing directory (Tk's own default is false, allowing a not-yet-created one)titleString, nil— dialog window titleparentString, nil— parent window (defaults to the root window)
Returns String, nil — the chosen directory path, or nil if cancelled
choose_open_file(filetypes: nil, initialdir: nil, initialfile: nil, title: nil, multiple: false, parent: nil)
Show the native “choose file to open” dialog.
Parameters
filetypesArray<Array>, nil— e.g.[["PNG Images", ".png"], ["All Files", "*"]]- the second element of each pair can also be an array of extensions (["Images", [".png", ".jpg"]])initialdirString, nil— directory the dialog starts ininitialfileString, nil— filename pre-filled in the dialogtitleString, nil— dialog window titlemultipleBoolean— allow selecting more than one fileparentString, nil— parent window (defaults to the root window)
Returns String, Array<String>, nil — the chosen path (an array of
paths if multiple:), or nil if the dialog was cancelled
choose_save_file(filetypes: nil, initialdir: nil, initialfile: nil, title: nil, defaultextension: nil, confirmoverwrite: true, parent: nil)
Show the native “choose file to save” dialog.
Parameters
filetypesArray<Array>, nil— see#choose_open_fileinitialdirString, nil— directory the dialog starts ininitialfileString, nil— filename pre-filled in the dialogtitleString, nil— dialog window titledefaultextensionString, nil— extension appended if the typed filename doesn't already have oneconfirmoverwriteBoolean— ask before overwriting an existing file (Tk's own default is true; pass false to skip the confirmation)parentString, nil— parent window (defaults to the root window)
Returns String, nil — the chosen path, or nil if cancelled
command(cmd, *args, **kwargs)
Build and evaluate a Tcl command from Ruby values. Positional args
are converted: Symbols pass bare, Procs become callbacks, everything
else is brace-quoted. 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 - there is no
unsafe way to attach a callback through this method, regardless of
whether the call happens to match a registered per-widget-type
interceptor (see CommandInterceptors) or falls through to the
generic default. 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) - not tied to track_widgets.
Parameters
cmdSymbol, String— the Tcl command nameargs— positional argumentskwargs— keyword arguments mapped to-key valuepairs
Returns String — the Tcl result
@raise AmbiguousCommandError if more than one registered
interceptor claims the same call - see CommandInterceptors
Example
app.command(:pack, '.btn', side: :left, padx: 10)
# evaluates: pack .btn -side left -padx {10}
create_widget(type, path nil, parent: nil, idempotent: false, **kwargs)
Create a Tk widget and return a Widget wrapper.
Auto-generates a unique path if none is given. The path is derived from the widget type and a monotonic counter.
Parameters
typeString, Symbol— Tk widget command (e.g. 'ttk::button', :canvas)pathString, nil— explicit Tk path, or nil for auto-namingparentWidget, String, nil— parent widget for path nestingidempotentBoolean— skip the creation command if a widget already exists atpath- for widgets meant to be fetched by a stable, caller-chosen path and reused across many calls (see#menu) rather than freshly created each timekwargs— keyword arguments passed to the Tk widget command
Returns Widget — the created widget
Examples
# Auto-named
btn = app.create_widget('ttk::button', text: 'Click')
# btn.path => ".ttkbtn1"
# Explicit path
frm = app.create_widget('ttk::frame', '.myframe')
# Nested under a parent
frm = app.create_widget('ttk::frame')
btn = app.create_widget('ttk::button', parent: frm, text: 'Click')
# btn.path => ".ttkfrm1.ttkbtn1"
dark?
Returns true if the window is currently displayed in dark mode. Always returns false on non-macOS.
Returns Boolean
destroy(widget '.')
Destroy a widget and all its children.
Parameters
widgetString— Tk widget path (e.g. ".frame1")
Returns void
@raise ArgumentError
ensure_tcl_helper(name)
Evaluate script once per App instance under name, skipping it on
later calls. Meant for widget-behavior modules that need to define a
Tcl-side helper proc (e.g. a scan routine) without re-sending and
re-parsing that definition on every call.
Parameters
nameSymbol— unique name for this helper
Returns void
@yieldreturn String the Tcl script to evaluate the first time
every(ms, on_error: :raise, &block)
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).
Parameters
msInteger— interval in millisecondson_error:raise, Proc, nil— error handling strategy: -:raise(default) — cancels the timer and raises the exception from the next call to#update. -Proc— called with the exception; timer keeps running. -nil— cancels the timer silently; error stored inRepeatingTimer#last_error.
Returns RepeatingTimer — cancel handle
@yield block to call on each tick
Examples
# Basic polling loop
timer = app.every(50) { update_display }
timer.cancel # stop later
# With error handler (timer keeps running)
timer = app.every(100, on_error: ->(e) { log(e) }) { risky_work }
# Silent cancel on error
timer = app.every(50, on_error: nil) { maybe_fails }
timer.last_error # => check later
font_metrics(font)
Get font metrics (ascent, descent, linespace) for a given font. Uses Tk’s C font API directly.
Parameters
fontString— font description (e.g. "Helvetica 12", "TkDefaultFont")
Returns Hash{Symbol => Integer} — :ascent, :descent, :linespace
@raise Teek::TclError if the font is not found
get_variable(name)
Get a Tcl variable’s value.
Parameters
nameString— variable name (array-element and namespaced forms work)
Returns String — the value
@raise Teek::TclError if the variable doesn't exist
grab_release(window: '.')
Release a grab previously set with #grab_set. See Window#grab_release.
Parameters
windowString, Widget— (default: the root window)
Returns void
@note Prefer app.window(window).grab_release for new code - this
flat method is kept for compatibility and just delegates there.
grab_set(window: '.', global: false)
Set the input grab on window. See Window#grab_set.
Parameters
windowString, Widget— (default: the root window)globalBoolean
Returns void
@note Prefer app.window(window).grab_set for new code - this flat
method is kept for compatibility and just delegates there.
See also
hide(window '.')
Hide a window without destroying it. Defaults to the root window (“.”).
Parameters
windowString— Tk window path
Returns void
See also
initialize(title: nil, track_widgets: true, debug: false, &block)
Returns App — a new instance of App
make_list(*args)
Build a properly-escaped Tcl list from Ruby strings.
Parameters
argsArray<String>— elements to join
Returns String — a Tcl-formatted list
measure_chars(font, text, max_pixels, **opts)
Measure how many bytes of text fit within a pixel width limit. Useful for text truncation, ellipsis, and line wrapping.
Parameters
fontString— font description (e.g. "Helvetica 12")textString— text to measuremax_pixelsInteger— maximum pixel width (-1 for unlimited)optsHash— options
Returns Hash{Symbol => Integer} — :bytes and :width
Options
:partial_okBoolean— allow partial character at boundary:whole_wordsBoolean— break only at word boundaries:at_least_oneBoolean— always return at least one character
@raise Teek::TclError if the font is not found
message_box(message:, title: nil, detail: nil, icon: :info, type: :ok, default: nil, parent: nil)
Show a message box with one or more buttons.
Parameters
messageString— the main message texttitleString, nil— dialog window titledetailString, nil— additional explanatory text, shown smaller belowmessageicon:error, :info, :question, :warning— icon to displaytype:ok, :okcancel, :abortretryignore, :yesno, :yesnocancel, :retrycancel— which button(s) to showdefaultSymbol, nil— which button is focused by default (e.g.:cancel); defaults to Tk's own choice if omittedparentString, nil— parent window (defaults to the root window)
Returns Symbol — the pressed button - :ok, :cancel, :yes,
:no, :abort, :retry, or :ignore
modal(window: '.', global: false, &block)
Make window modal. See Window#modal.
Parameters
windowString, Widget— (default: the root window)globalBoolean— see#grab_set
Returns void
@note Prefer app.window(window).modal <code></code> for new code - this flat
method is kept for compatibility and just delegates there.
@yield optional - runs with the grab and focus already set
See also
on_close(window: '.', &block)
Register a handler for the window manager’s close button (WM_DELETE_WINDOW - the titlebar close box, Cmd-W, Alt-F4, etc., depending on platform).
Tk’s own default behavior (destroy the window) only applies when
nothing else has claimed this protocol - setting a handler here
replaces it, so the block is entirely responsible for deciding
whether the window actually closes. Call #destroy yourself if you
want it to; do nothing (or show a confirmation first) if you don’t.
Parameters
windowString— Tk window path (default: the root window)
Returns void
@note Prefer app.window(window).on_close <code></code> for new code - this
flat method is kept for compatibility and just delegates there.
@yield called when the window's close button is pressed
Examples
# Confirm before quitting
app.on_close { app.destroy('.') if app.message_box(message: 'Quit?', type: :yesno) == :yes }
# A toplevel that just hides instead of closing
app.on_close(window: settings_window) { app.tcl_eval("wm withdraw #{settings_window}") }
See also
package_names
List all packages known to this interpreter.
Scans auto_path for package indexes before querying.
Returns Array<String>
package_present?(name)
Check if a package is already loaded in this interpreter.
Parameters
nameString— package name
Returns Boolean
package_versions(name)
List available versions of a package.
Scans auto_path for package indexes before querying.
Parameters
nameString— package name
Returns Array<String>
raw_command(cmd, *args, **kwargs)
The dumb Tcl builder underneath #command - no interceptor lookup,
no per-widget-type awareness. Used internally by interceptors (to
actually perform their Tcl work without re-entering dispatch) and by
#command’s own generic fallback. Any Proc here still gets
registered as a real, working callback (positional: bind-shaped,
relay_break_continue: true; kwarg, via #tcl_arg_value: option-shaped,
relay_break_continue: false) - it just isn’t tracked for release.
Prefer #command; call this directly only from within an
interceptor.
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. There is nothing here for a
value to “break out” of.
Returns String — the Tcl result
record_widget_type(cmd, args)
Records that args[0] is a widget of type cmd, if this call looks
like widget creation (cmd is a known WIDGET_COMMANDS entry). Not
tied to track_widgets - this is what lets #command look up a
registered interceptor for a bare path string on any later call,
regardless of how the widget was created.
Returns void
register_callback(callable, relay_break_continue: true)
Register a Ruby callable as a Tcl callback.
The callable can use throw for Tcl control flow:
throw :teek_break - stop event propagation (like Tcl “break”)
throw :teek_continue - Tcl TCL_CONTINUE
throw :teek_return - Tcl TCL_RETURN
:teek_break/:teek_continue only mean something when Tcl actually
dispatches the result through a context that knows how to handle
TCL_BREAK/TCL_CONTINUE - Tk’s bind mechanism does; a plain script
invocation (a menu entry’s or widget’s -command) does not, and
returning either code there is a Tcl error (“invoked break/continue
outside of a loop”), not a no-op. relay_break_continue: false is
for exactly those non-bind callers: throw is still caught (so it
can’t crash as an uncaught throw), but is treated as the callback
simply finishing, instead of being relayed to Tcl. :teek_return is
always relayed either way - TCL_RETURN is safe in any context.
Parameters
callable#call— a Proc or lambda to invoke from Tclrelay_break_continueBoolean— whether a caught :teek_break/ :teek_continue is relayed to Tcl as TCL_BREAK/TCL_CONTINUE (true, for bind-dispatched callbacks) or silently absorbed (false, for callbacks invoked as a plain script - menu/widget -command options)
Returns Integer — callback ID, usable as ruby_callback <id> in Tcl
See also
register_drop_target(widget)
Register a widget as a file drop target.
After registration, dropping files onto the widget generates a single
<<DropFile>> virtual event with all file paths as a Tcl list in the
event data. Use #split_list to convert to a Ruby array.
Parameters
widgetString— Tk widget path (e.g., ".", ".frame")
Returns void
Example
app.register_drop_target('.')
app.bind('.', '<<DropFile>>', :data) do |data|
paths = app.split_list(data)
puts "Dropped #{paths.length} file(s): #{paths.inspect}"
end
require_package(name, version nil)
Load a Tcl package into this interpreter.
Parameters
nameString— package name (e.g. "BWidget")versionString, nil— minimum version constraint
Returns String — the version that was loaded
@raise Teek::TclError if the package is not found
set_variable(name, value)
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.
Parameters
nameString— variable name (array-element and namespaced forms work)valueString— value to set
Returns String — the value
set_window_geometry(geometry, window: '.')
Set a window’s geometry (e.g. “400x300”, “400x30010050”).
Parameters
geometryString— geometry stringwindowString— Tk window path
Returns String — the geometry
See also
set_window_resizable(width, height, window: '.')
Set whether a window is resizable.
Parameters
widthBoolean— allow horizontal resizeheightBoolean— allow vertical resizewindowString— Tk window path
Returns void
See also
set_window_title(title, window: '.')
Set a window’s title.
Parameters
titleString— new titlewindowString— Tk window path
Returns String — the title
See also
show(window '.')
Show a window. Defaults to the root window (“.”).
Parameters
windowString— Tk window path
Returns void
See also
split_list(str)
Split a Tcl list string into a Ruby array of strings.
Parameters
strString— a Tcl-formatted list
Returns Array<String>
tcl_eval(script)
Evaluate a raw Tcl script string and return the result.
Prefer #command for building commands from Ruby values; use this
when you need Tcl-level features like variable substitution or
inline expressions that #command can’t express.
Parameters
scriptString— Tcl code to evaluate
Returns String — the Tcl result
@note Any callback embedded in script (e.g. a hand-built
ruby_callback <id>) is on you to register and release - none of
#command's tracking applies here. Creating a widget this way
(instead of via #command/#create_widget/#menu) also means its
type is never recorded, so a registered CommandInterceptors entry
won't engage for it even on later, ordinary #command calls -
create widgets through those methods and reach for tcl_eval for
everything else.
tcl_invoke(*args)
Invoke a Tcl command with pre-split arguments (no Tcl parsing).
Safer than #tcl_eval when arguments may contain special characters.
Parameters
argsArray<String>— command name followed by arguments
Returns String — the Tcl result
tcl_to_bool(str)
Convert a Tcl boolean string (“0”, “1”, “yes”, “no”, etc.) to Ruby boolean.
Parameters
strString— a Tcl boolean value
Returns Boolean
text_width(font, text)
Measure the pixel width of a text string in a given font.
Uses Tk’s C font API directly — faster than the Tcl font measure command.
Parameters
fontString— font description (e.g. "Helvetica 12", "TkDefaultFont")textString— text to measure
Returns Integer — pixel width
@raise Teek::TclError if the font is not found
track_widget_option_callbacks(cmd, args, kwargs)
#command’s fallback for any call that no registered interceptor
claimed: registers any Proc-valued kwarg (e.g. command:,
validatecommand:) as a callback tracked under cmd, releasing it if
reconfigured or when the widget is destroyed. A widget’s own options
are never silently renumbered or invalidated out from under us the
way menu entries are, so this uses a cheap in-memory
CallbackRegistry#reconcile rather than a live-scan one.
Tracked under the widget’s own path, by [*context, key], where
context is args normalized to strings - except a bare configure
(or widget creation - the container is the new widget’s path, not the
cmd used to create it) normalizes to an empty context, since all
three address the same underlying option namespace and must replace
each other. Any other subcommand (e.g. a treeview’s heading col)
keeps its own args as part of the key, so two different targets
sharing an option name (two columns both using command:) don’t
collide.
Returns Hash — kwargs with any Proc values swapped for the Tcl
script #raw_command embeds
unbind(widget, event)
Remove an event binding previously set with #bind.
Parameters
widgetString— Tk widget path or class tageventString— Tk event name, with or without angle brackets
Returns void
See also
unregister_callback(id)
Remove a previously registered callback by its ID.
Parameters
idInteger— callback ID returned by#register_callback
Returns void
update_idletasks
Process only pending idle callbacks (e.g. geometry redraws), then return.
Returns void
window(path '.')
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 instead of threading
window: through a pile of unrelated flat methods. This app’s own
window_title/set_window_title/etc., #wm, #on_close,
#grab_set, #grab_release, and #modal all delegate here
internally - use whichever reads better to you, they’re the same
underlying calls.
Parameters
pathString, Widget— (default: the root window)
Returns Window
window_geometry(window: '.')
Get a window’s current geometry.
Parameters
windowString— Tk window path
Returns String — geometry string (e.g. "400x30000")
See also
window_resizable(window: '.')
Get whether a window is resizable.
Parameters
windowString— Tk window path
Returns Array(Boolean, Boolean) — [width_resizable, height_resizable]
See also
window_title(window: '.')
Get a window’s current title.
Parameters
windowString— Tk window path
Returns String — current title
See also
Attributes ↑
_pending_exception= [W]
@api private
callback_registry [R]
Returns the value of attribute callback_registry.
clipboard [R]
Returns the value of attribute clipboard.
debugger [R]
Returns the value of attribute debugger.
interp [R]
Returns the value of attribute interp.
widgets [R]
Returns the value of attribute widgets.
winfo [R]
Returns the value of attribute winfo.
wm [R]
Returns the value of attribute wm.