From c009a0a6f73bb61d2bd37f4acb8435b335ed2fa0 Mon Sep 17 00:00:00 2001
From: Mauro Aranda <maurooaranda@gmail.com>
Date: Thu, 22 Oct 2020 13:52:42 +0200
Subject: Allow moving members of editable-list widget, via delete+insert

* etc/NEWS (Widget): Announce the feature (bug#6419).
* lisp/wid-edit.el (widget-editable-list-delete-at): Save into a new
widget property, :last-deleted, the WIDGET to be deleted.  Add
docstring.
(widget-editable-list-insert-before): If there is a recently deleted
child for the editable list, insert that one, instead of a new default
widget.  Add docstring.
(insert-button widget): Make :help-echo a function to avoid the
help-echo string become too long.
(delete-button widget): Tweak the :help-echo string, to document this
behavior.

* test/lisp/wid-edit-tests.el (widget-test-moving-editable-list-item):
Test the feature.
---
 lisp/wid-edit.el | 33 +++++++++++++++++++++++++++++----
 1 file changed, 29 insertions(+), 4 deletions(-)

(limited to 'lisp/wid-edit.el')

diff --git a/lisp/wid-edit.el b/lisp/wid-edit.el
index 6568cd2c8f1..c3366b62cdb 100644
--- a/lisp/wid-edit.el
+++ b/lisp/wid-edit.el
@@ -2721,7 +2721,10 @@ Return an alist of (TYPE MATCH)."
 (define-widget 'insert-button 'push-button
   "An insert button for the `editable-list' widget."
   :tag "INS"
-  :help-echo "Insert a new item into the list at this position."
+  :help-echo (lambda (widget)
+               (if (widget-get (widget-get widget :parent) :last-deleted)
+                   "Insert back the last deleted item from this list, at this position."
+                 "Insert a new item into the list at this position."))
   :action 'widget-insert-button-action)
 
 (defun widget-insert-button-action (widget &optional _event)
@@ -2734,7 +2737,7 @@ Return an alist of (TYPE MATCH)."
 (define-widget 'delete-button 'push-button
   "A delete button for the `editable-list' widget."
   :tag "DEL"
-  :help-echo "Delete this item from the list."
+  :help-echo "Delete this item from the list, saving it for later reinsertion."
   :action 'widget-delete-button-action)
 
 (defun widget-delete-button-action (widget &optional _event)
@@ -2824,9 +2827,18 @@ Return an alist of (TYPE MATCH)."
     (cons found value)))
 
 (defun widget-editable-list-insert-before (widget before)
-  ;; Insert a new child in the list of children.
+  "Insert a new widget as a child of WIDGET.
+
+If there is a recently deleted child, the new widget is that deleted child.
+Otherwise, the new widget is the default child of WIDGET.
+
+The new widget gets inserted at the position of the BEFORE child."
   (save-excursion
     (let ((children (widget-get widget :children))
+          (last-deleted (when-let ((lst (widget-get widget :last-deleted)))
+                          (prog1
+                              (pop lst)
+                            (widget-put widget :last-deleted lst))))
 	  (inhibit-read-only t)
 	  (inhibit-modification-hooks t))
       (cond (before
@@ -2834,7 +2846,11 @@ Return an alist of (TYPE MATCH)."
 	    (t
 	     (goto-char (widget-get widget :value-pos))))
       (let ((child (widget-editable-list-entry-create
-		    widget nil nil)))
+                    widget (and last-deleted
+                                (widget-apply last-deleted
+                                              :value-to-external
+                                              (widget-get last-deleted :value)))
+                    last-deleted)))
 	(when (< (widget-get child :entry-from) (widget-get widget :from))
 	  (set-marker (widget-get widget :from)
 		      (widget-get child :entry-from)))
@@ -2847,6 +2863,15 @@ Return an alist of (TYPE MATCH)."
   (widget-apply widget :notify widget))
 
 (defun widget-editable-list-delete-at (widget child)
+  "Delete the widget CHILD from the known children of widget WIDGET.
+
+Save CHILD into the :last-deleted list, so it can be inserted later."
+  ;; Save the current value of CHILD, to use if the user later inserts the
+  ;; widget.
+  (widget-put child :value (widget-apply child :value-get))
+  (let ((lst (widget-get widget :last-deleted)))
+    (push child lst)
+    (widget-put widget :last-deleted lst))
   ;; Delete child from list of children.
   (save-excursion
     (let ((buttons (copy-sequence (widget-get widget :buttons)))
-- 
cgit v1.2.3