Module: Teek::TagBindInterceptor Private

Defined in:
lib/teek/tag_bind_interceptor.rb

Overview

This module is part of a private API. You should avoid using this module if possible, as it may be removed or be changed in the future.

Live-scan helper for the shared "text"/"ttk::treeview" CommandInterceptors entry below - both widgets have byte-identical tag bind/tag names shapes.

Tags aren't windows, so a tag's bound callback never fires on its own; the widget that owns it is typically long-lived and reused (log panes, editors, tree views), so tags churn while the widget persists. Unlike menu entries, a tag name is a stable hash key Tk never renumbers, so reconciling is a straightforward full scan: enumerate every live tag (tag names), read back what's bound to each (tag bind $tag / tag bind $tag $seq), and release whatever dropped out.

Constant Summary collapse

MUTATING_SUBCOMMANDS =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

%w[bind delete].freeze
LIVE_COMMANDS_TCL_PROC =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

<<~TCL.freeze
  proc ::teek_tag_live_commands {path} {
    set result {}
    foreach tag [$path tag names] {
      foreach seq [$path tag bind $tag] {
        lappend result [$path tag bind $tag $seq]
      }
    }
    return $result
  }
TCL

Class Method Summary collapse

Class Method Details

.call(app, path, args, kwargs) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

A tag bind/tag delete call goes through raw_command unchanged (a bound Proc is registered exactly like any other bind-shaped positional arg), then reconciles tracked tag callbacks against Tk's live tag-bind state.



53
54
55
56
57
58
59
# File 'lib/teek/tag_bind_interceptor.rb', line 53

def self.call(app, path, args, kwargs)
  return nil unless args[0]&.to_s == 'tag' && MUTATING_SUBCOMMANDS.include?(args[1]&.to_s)

  result = app.raw_command(path, *args, **kwargs)
  app.callback_registry.reconcile([:tag_bind, path]) { |_before| live_command_ids(app, path) }
  result
end

.live_command_ids(app, path) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



34
35
36
37
38
39
40
41
42
43
44
45
46
47
# File 'lib/teek/tag_bind_interceptor.rb', line 34

def self.live_command_ids(app, path)
  app.ensure_tcl_helper(:tag_live_commands) { LIVE_COMMANDS_TCL_PROC }
  raw = app.tcl_eval("::teek_tag_live_commands #{path}")
  # \z anchors this to a BARE `ruby_callback <id>` with nothing after.
  # raw_command's generic positional-Proc handling technically allows a
  # caller to pass %-substitutions to a tag-bind-shaped app.command
  # call (the same mechanism App#bind uses), but nothing in teek does
  # that today. If a caller ever does, this regex silently stops
  # matching and that id leaks on rebuild/delete - drop the \z anchor
  # (match just the leading `ruby_callback <id>`) if that changes.
  app.split_list(raw).each_with_object({}) do |cmd, ids|
    ids[Regexp.last_match(1)] = Regexp.last_match(1) if cmd =~ /\Aruby_callback (\S+)\z/
  end
end