Search code examples
emacselisp

Change text of tree widget node


I've written a piece of code that creates a tree widget:

(require 'tree-widget)

(defun my-create-tree-widget ()
  (interactive)
  (with-current-buffer 
    (get-buffer-create "*my-tree-widget*")
    (setq-local my-tree-widget
        (widget-create
         'tree-widget
         :open t
         :tag "one"
         :args
         (list (widget-convert
            'tree-widget
            :tag "two"
            :args (mapcar 
                    (apply-partially #'widget-convert 'item)
                    '("three" "four"))))))
    (switch-to-buffer (current-buffer))))

The resulting tree widget looks like this, with the :tag arguments becoming the labels of the nodes:

[-] one
 `-[+] two

Now I want to change the labels from my program, but setting the :tag value with widget-put doesn't change anything in the buffer. How can I accomplish that?


Solution

  • For some reason, the label of a tree widget node is stored as the first element in the :children property, so you need to retrieve that value, and change its :tag property, not the :tag property of the tree widget itself.

    After changing the :tag property, you need to force the widget to update. You can do that using widget-value-set. Since you don't actually want to change the value, just assign the same value again; it will still have the same effect of redrawing the widget.

    For example, to update the top-level node:

    (defun my-change-tree-widget-first-level ()
      (interactive)
      (let ((node (car (widget-get my-tree-widget :children))))
        (widget-put node :tag (format-time-string "%T"))
        ;; Redraw
        (widget-value-set node (widget-value node))))
    

    This results in:

    [-] 12:39:19
     `-[+] two
    

    To update a child node, just dig down using the :children property, keeping in mind that the first "child" element is not strictly speaking a child:

    (defun my-change-tree-widget-second-level ()
      (interactive)
      (let ((second-level (cadr (widget-get my-tree-widget :children))))
        (unless (tree-widget-p second-level)
          (error "This is not the tree widget"))
        (let ((node (car (widget-get second-level :children))))
          (widget-put node :tag (format-time-string "%T"))
          ;; Redraw
          (widget-value-set node (widget-value node)))))
    

    With this final result:

    [-] 12:39:19
     `-[+] 12:40:41