Subject: bug#70413: 30.0.50; FR: skip inactive widgets when tabbing

From: Stephen Berman <[email-masked]>
Date: Tue, 16 Apr 2024 11:20:10 +0200

In buffers containing widgets, such as the Customize buffers, the
widgets can be either active or inactive: when you click (with the mouse
or RET) on an active widget, its associated action is executed, and when
you click on an inactive widget, this raises the error "Attempt to
perform action on inactive widget".  You can navigate among the widgets
by pressing TAB or S-TAB to move point to the next or previous widget.

I think it would be useful and convenient to skip over inactive widgets
when tabbing; e.g. you then avoid accidentally tabbing to an inactive
widget, typing RET and getting the error, and in a buffer with many
active and inactive widgets, you can tab more quickly to the desired
active widget by skipping over the inactive ones.

The attached patch implements this behavior.  Since tabbing to inactive
widgets has always been the behavior in the widget library, skipping is
conditioned on the value of a boolean defcustom, with the default being
the current non-skipping.

In GNU Emacs 30.0.50 (build 2, x86_64-pc-linux-gnu, GTK+ Version
 3.24.41, cairo version 1.18.0) of 2024-04-16 built on strobelfs
Repository revision: b436f430e3897e1aa6dcb5a39564a3553bbf631f
Repository branch: master
Windowing system distributor 'The X.Org Foundation', version 11.0.12101013
System Description: Linux From Scratch r12.1-41

Configured using:
 'configure 'CFLAGS=-Og -g3' PKG_CONFIG_PATH=/opt/qt5/lib/pkgconfig'

Configured features:
ACL CAIRO DBUS FREETYPE GIF GLIB GMP GNUTLS GPM GSETTINGS HARFBUZZ JPEG
LCMS2 LIBSYSTEMD LIBXML2 MODULES NATIVE_COMP NOTIFY INOTIFY PDUMPER PNG
RSVG SECCOMP SOUND SQLITE3 THREADS TIFF TOOLKIT_SCROLL_BARS TREE_SITTER
WEBP X11 XDBE XIM XINPUT2 XPM GTK3 ZLIB

diff --git a/lisp/wid-edit.el b/lisp/wid-edit.el
index 172da3db1e0..b40d4bf8898 100644
--- a/lisp/wid-edit.el
+++ b/lisp/wid-edit.el
@@ -1219,11 +1219,20 @@ widget-button-press
 	(when (commandp command)
 	  (call-interactively command))))))

+(defcustom widget-skip-inactive nil
+  "If non-nil, skip inactive widgets when tabbing through buffer."
+  :version "30.1"
+  :group 'widgets
+  :type 'boolean)
+
 (defun widget-tabable-at (&optional pos)
   "Return the tabable widget at POS, or nil.
-POS defaults to the value of (point)."
+POS defaults to the value of (point).  If user option
+`widget-skip-inactive' is non-nil, inactive widgets are not tabable."
   (let ((widget (widget-at pos)))
-    (if widget
+    (if (and widget (if widget-skip-inactive
+                        (widget-apply widget :active)
+                      t))
 	(let ((order (widget-get widget :tab-order)))
 	  (if order
 	      (if (>= order 0)