Module: Teek::CanvasBindInterceptor Private
- Defined in:
- lib/teek/canvas_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.
Registered for the "canvas" CommandInterceptors entry below.
Canvas items aren't windows (only the canvas itself is one), so a bound
item's callback never fires canvas delete
is silent - the same leak shape menu entries have. Unlike menu or
text/treeview tags, canvas has no "list every live binding" enumeration
command (no analogue to menu's index end or text's tag names), so
this can't do a full-scan reconcile. Instead it re-queries only the
(tagOrId, sequence) keys it already knows about - via canvas bind tagOrId sequence, the 2-arg read form - after every bind/delete call,
and lets whatever no longer resolves drop out.
A binding on a numeric item id is released this way once that item is deleted (Tk's Tk_DeleteAllBindings clears its binding-table entries along with it). A binding on a tag is NOT released by deleting a tagged item - the tag itself isn't an item, so its binding-table entry persists independent of which (if any) items currently carry that tag.
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
Class Method Summary collapse
- .call(app, path, args, kwargs) ⇒ Object private
- .requery(app, path, before, sub, args) ⇒ Object private
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.
29 30 31 32 33 34 35 36 |
# File 'lib/teek/canvas_bind_interceptor.rb', line 29 def self.call(app, path, args, kwargs) sub = args[0]&.to_s return nil unless MUTATING_SUBCOMMANDS.include?(sub) result = app.raw_command(path, *args, **kwargs) app.callback_registry.reconcile([:canvas_bind, path]) { |before| requery(app, path, before, sub, args) } result end |
.requery(app, path, before, sub, args) ⇒ 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.
38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
# File 'lib/teek/canvas_bind_interceptor.rb', line 38 def self.requery(app, path, before, sub, args) keys = before.keys keys += [[args[1].to_s, args[2].to_s]] if sub == 'bind' && args.length >= 4 keys.uniq.each_with_object({}) do |(tag_or_id, seq), after| # Unlike a tag (a plain Tcl string, always a valid query target even # if nothing currently carries it), a numeric item id is a hash key # into the canvas's item table - querying one after its item is # deleted raises "item \"N\" doesn't exist" rather than returning # empty, so a deleted item's binding has to be dropped via rescue, # not by checking the result. current = begin app.tcl_eval("#{path} bind #{tag_or_id} #{seq}") rescue Teek::TclError '' end # \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 # canvas-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. after[[tag_or_id, seq]] = Regexp.last_match(1) if current =~ /\Aruby_callback (\S+)\z/ end end |