summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--etc/NEWS7
-rw-r--r--lisp/wid-edit.el33
-rw-r--r--test/lisp/wid-edit-tests.el19
3 files changed, 55 insertions, 4 deletions
diff --git a/etc/NEWS b/etc/NEWS
index 2aed5751595..9e8182a2dae 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -1166,6 +1166,13 @@ window after starting). This variable defaults to nil.
+++
*** 'widget-choose' now supports menus in extended format.
+---
+*** The 'editable-list' widget now supports moving items up and down.
+You can now move items up and down by deleting and then reinserting
+them, using the DEL and INS buttons respectively. This is useful in
+Custom buffers, for example, to change the order of the elements in a
+list.
+
** Miscellaneous
---
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)))
diff --git a/test/lisp/wid-edit-tests.el b/test/lisp/wid-edit-tests.el
index df49ffc8224..4508b680232 100644
--- a/test/lisp/wid-edit-tests.el
+++ b/test/lisp/wid-edit-tests.el
@@ -129,4 +129,23 @@
(widget-insert "And some non-widget text.")
(should (string= (widget-apply wid :value-get) "")))))
+(ert-deftest widget-test-moving-editable-list-item ()
+ "Check that we can move an editable list item up or down, via delete+insert."
+ (with-temp-buffer
+ (widget-insert "Testing editable-list.\n\n")
+ (let ((lst (widget-create 'editable-list
+ :value '("beg" "end" "middle")
+ '(editable-field :value "unknown"))))
+ (use-local-map widget-keymap)
+ (widget-setup)
+ ;; Go to the DEL button for the 2nd element and action it.
+ (goto-char (widget-get (nth 2 (widget-get lst :buttons)) :from))
+ (widget-apply-action (widget-at))
+ ;; Go to the INS button and action it.
+ (goto-char (widget-get lst :to))
+ (widget-backward 1)
+ (widget-apply-action (widget-at))
+ ;; Check that we effectively moved the item to the last position.
+ (should (equal (widget-value lst) '("beg" "middle" "end"))))))
+
;;; wid-edit-tests.el ends here