* bug#4787: 23.1; in Customize, after showing Lisp expression, how to see normal? @ 2009-10-22 22:00 Drew Adams 2012-02-05 18:42 ` Drew Adams 2020-10-02 15:24 ` Mauro Aranda 0 siblings, 2 replies; 8+ messages in thread From: Drew Adams @ 2009-10-22 22:00 UTC (permalink / raw) To: bug-gnu-emacs emacs -Q M-x customize-face default In Customize, click State > Show Lisp Expression. It now seems to be impossible to get back the normal display, i.e., to not see the Lisp expression anymore. In fact, the 3 menu items For Current Display, For All Displays, and Show Lisp Expression are 3 _alternatives_. They should be radio-button menu items, not just normal menu items. And the item names should be parallel. There is no understandable relation between "For Current Display", for instance, and "Show Lisp Expression". Those don't seem to have anything to do with each other. There is no way to guess that these are alternatives. I even started to write this bug saying that there was no way to get back the normal display. Fiddling and trying things, I eventually discovered that For Current Display did the job. This is NOT AT ALL obvious - horrible UI. This is a regression wrt Emacs 21 (and 20), where the menu items were clear. In GNU Emacs 23.1.1 (i386-mingw-nt5.1.2600) of 2009-07-29 on SOFT-MJASON Windowing system distributor `Microsoft Corp.', version 5.1.2600 configured using `configure --with-gcc (4.4)' ^ permalink raw reply [flat|nested] 8+ messages in thread
* bug#4787: 23.1; in Customize, after showing Lisp expression, how to see normal? 2009-10-22 22:00 bug#4787: 23.1; in Customize, after showing Lisp expression, how to see normal? Drew Adams @ 2012-02-05 18:42 ` Drew Adams 2020-10-02 15:24 ` Mauro Aranda 1 sibling, 0 replies; 8+ messages in thread From: Drew Adams @ 2012-02-05 18:42 UTC (permalink / raw) To: 4787, 4787 > severity 4787 wishlist 1. There is no way that a user looking for a way to show the normal, non-Lisp-expression display of an option value, would think to try "For Current Display". 2. Mutually exclusive UI alternatives should be presented as such. Having multiple menu items, of which a few are in fact alternatives, is misleading to users. There are two problems wrt this UI: (1) inappropriate menu-item name for what `For Current Display" does and (2) no way to see that 3 of the menu items are mutually exclusive alternatives. (I'm not sure now where I saw "For Current Display", "For All Displays", and "Show Lisp Expression". Perhaps that was from an earlier Emacs 24 build - dunno. Now I see "Show Current Value" and "Show Saved Lisp Expression". But the essential problems are the same as I've described.) ^ permalink raw reply [flat|nested] 8+ messages in thread
* bug#4787: 23.1; in Customize, after showing Lisp expression, how to see normal? 2009-10-22 22:00 bug#4787: 23.1; in Customize, after showing Lisp expression, how to see normal? Drew Adams 2012-02-05 18:42 ` Drew Adams @ 2020-10-02 15:24 ` Mauro Aranda 2020-10-03 17:32 ` Lars Ingebrigtsen 1 sibling, 1 reply; 8+ messages in thread From: Mauro Aranda @ 2020-10-02 15:24 UTC (permalink / raw) To: 4787 [-- Attachment #1.1: Type: text/plain, Size: 1405 bytes --] "Drew Adams" <drew.adams@oracle.com> writes: > emacs -Q > M-x customize-face default > In Customize, click State > Show Lisp Expression. > > It now seems to be impossible to get back the normal display, i.e., to > not see the Lisp expression anymore. > > In fact, the 3 menu items For Current Display, For All Displays, and > Show Lisp Expression are 3 _alternatives_. They should be radio-button > menu items, not just normal menu items. I think it would be nice to display them as radio buttons. But, this menus in Custom are displayed with `widget-choose', and `widget-choose' doesn't support menus in the so-called extended menu format. So I took a look to see if that would be easy to do. I attach a patch for review. If the feature is accepted, then it shouldn't be too hard to change Custom to pass a menu with radio-buttons for these options. > And the item names should be parallel. There is no understandable > relation between "For Current Display", for instance, and "Show Lisp > Expression". Those don't seem to have anything to do with each > other. There is no way to guess that these are alternatives. Perhaps "Customize Face For Current Display" and "Customize Face For All Kinds of Display" is better? Kinda long, though. Anyway, I attach a patch to add support for extended menus in widget-choose, as a first step to add radio buttons for related options in these Custom menus. [-- Attachment #1.2: Type: text/html, Size: 1692 bytes --] [-- Attachment #2: 0001-Support-extended-menus-in-widget-choose.patch --] [-- Type: text/x-patch, Size: 8607 bytes --] From e7422bf114c0cb1896f6c587420d49e9d0feedd3 Mon Sep 17 00:00:00 2001 From: Mauro Aranda <maurooaranda@gmail.com> Date: Fri, 2 Oct 2020 11:56:57 -0300 Subject: [PATCH] Support extended menus in widget-choose * lisp/wid-edit.el (widget--simplify-menu): New function, to convert extended menus into simplified menus when using the menu to prompt through the minibuffer. (widget-choose): Accept a keymap menu. When not using x-popup-menu, simplify the menu with widget--simplify-menu. Document the changes in the docstring. * doc/misc/widget.texi (Utilities): Document widget-choose. * etc/NEWS: Document the feature. --- doc/misc/widget.texi | 22 +++++++++++ etc/NEWS | 5 +++ lisp/wid-edit.el | 89 ++++++++++++++++++++++++++++++++++++++++---- 3 files changed, 109 insertions(+), 7 deletions(-) diff --git a/doc/misc/widget.texi b/doc/misc/widget.texi index bccfa0ab6c..3ce27a12a0 100644 --- a/doc/misc/widget.texi +++ b/doc/misc/widget.texi @@ -1740,6 +1740,28 @@ Utilities This is only meaningful for radio buttons or checkboxes in a list. @end defun +@defun widget-choose title items &optional event +Prompt the user to choose an item from a list of options. + +@var{title} is the name of the list of options. @var{items} should be +a menu, with its items in the simple format or in the extended format. +@xref{Defining Menus,, Defining Menus, elisp, the Emacs Lisp Reference +Manual}. Independently of the format, you don't have to provide a +title for the menu, just pass the desired title in @var{title}. The +optional @var{event} is an input event. If @var{event} is a mouse +event and the number of elements in @var{items} is less than the user +option @code{widget-menu-max-size}, then @code{widget-choose} uses a +popup menu to prompt the user. Otherwise, @code{widget-choose} uses +the minibuffer. + +When @var{items} is a keymap menu, the returned value is the symbol in +the key vector, as in the argument of @code{define-key} +(@pxref{Changing Key Bindings,,,elisp, the Emacs Lisp Reference +Manual}). When @var{items} is a list whose selectable items are of +the form (@var{name} . @var{value}) (i.e., the simplified format), +then the return value is the @var{value} of the chosen element. +@end defun + @node Widget Wishlist @chapter Wishlist @cindex todo diff --git a/etc/NEWS b/etc/NEWS index d5b1496bba..418fe25b21 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -1086,6 +1086,11 @@ based on the current window size. In previous versions of Emacs, this was always done (and that could lead to odd displays when resizing the window after starting). This variable defaults to nil. +** Widget + ++++ +*** widget-choose now supports menus in extended format. + ** Miscellaneous --- diff --git a/lisp/wid-edit.el b/lisp/wid-edit.el index 0a2ddb0ea1..6568cd2c8f 100644 --- a/lisp/wid-edit.el +++ b/lisp/wid-edit.el @@ -203,27 +203,100 @@ widget-menu-minibuffer-flag :group 'widgets :type 'boolean) +(defun widget--simplify-menu (extended) + "Convert the EXTENDED menu into a menu composed of simple menu items. + +Each item in the simplified menu is of the form (ITEM-STRING . REAL-BINDING), +where both elements are taken from the EXTENDED MENU. ITEM-STRING is the +correspondent ITEM-NAME in the menu-item entry: + (menu-item ITEM-NAME REAL-BINDING . ITEM-PROPERTY-LIST), and REAL-BINDING is +the symbol in the key vector, as in `define-key'. + (See `(elisp)Defining Menus' for more information.) + +Only visible, enabled and meaningful menu items make their way into +the returned simplified menu. That is: +For the menu item to be visible, it has to either lack a :visible form in its +item-property-list, or the :visible form has to evaluate to a non-nil value. +For the menu item to be enabled, it has to either lack a :enabled form in its +item-property-list, or the :enable form has to evaluate to a non-nil value. +Additionally, if the menu item is a radio button, then its selected form has +to evaluate to nil for the menu item to be meaningful." + (let (simplified) + (map-keymap (lambda (ev def) + (when (and (eq (nth 0 def) 'menu-item) + (nth 2 def)) ; Only menu-items with a real binding. + ;; Loop through the item-property-list, looking for + ;; :visible, :enable (or :active) and :button properties. + (let ((plist (nthcdr 3 def)) + (enable t) ; Enabled by default. + (visible t) ; Visible by default. + selected keyword value) + (while (and plist (cdr plist) + (keywordp (setq keyword (car plist)))) + (setq value (cadr plist)) + (cond ((memq keyword '(:visible :included)) + (setq visible value)) + ((memq keyword '(:enable :active)) + (setq enable value)) + ((and (eq keyword :button) + (eq (car value) :radio)) + (setq selected (cdr value)))) + (setq plist (cddr plist))) + (when (and (eval visible) + (eval enable) + (or (not selected) + (not (eval selected)))) + (push (cons (nth 1 def) ev) simplified))))) + extended) + (reverse simplified))) + (defun widget-choose (title items &optional event) "Choose an item from a list. First argument TITLE is the name of the list. -Second argument ITEMS is a list whose members are either +Second argument ITEMS should be a menu, either with simple item definitions, +or with extended item definitions. +When ITEMS has simple item definitions, it is a list whose members are either (NAME . VALUE), to indicate selectable items, or just strings to indicate unselectable items. + +When ITEMS is a menu that uses an extended format, then ITEMS should be a +keymap, and each binding should look like this: + (menu-item ITEM-NAME REAL-BINDING . ITEM-PROPERTY-LIST) +or like this: (menu-item ITEM-NAME) to indicate a non-selectable item. +REAL-BINDING should be a symbol, and should not be a keymap, because submenus +are not supported. + Optional third argument EVENT is an input event. -The user is asked to choose between each NAME from the items alist, -and the VALUE of the chosen element will be returned. If EVENT is a -mouse event, and the number of elements in items is less than +If EVENT is a mouse event, and the number of elements in items is less than `widget-menu-max-size', a popup menu will be used, otherwise the -minibuffer." +minibuffer. + +The user is asked to choose between each NAME from ITEMS. +If ITEMS has simple item definitions, then this function returns the VALUE of +the chosen element. If ITEMS is a keymap, then the return value is the symbol +in the key vector, as in the argument of `define-key'." (cond ((and (< (length items) widget-menu-max-size) event (display-popup-menus-p)) ;; Mouse click. - (x-popup-menu event - (list title (cons "" items)))) + (if (keymapp items) + ;; Modify the keymap prompt, and then restore the old one, if any. + (let ((prompt (keymap-prompt items))) + (unwind-protect + (progn + (setq items (delete prompt items)) + (push title (cdr items)) + ;; Return just the first element of the list of events. + (car (x-popup-menu event items))) + (setq items (delete title items)) + (when prompt + (push prompt (cdr items))))) + (x-popup-menu event (list title (cons "" items))))) ((or widget-menu-minibuffer-flag (> (length items) widget-menu-max-shortcuts)) + (when (keymapp items) + (setq items (widget--simplify-menu items))) ;; Read the choice of name from the minibuffer. (setq items (cl-remove-if 'stringp items)) (let ((val (completing-read (concat title ": ") items nil t))) @@ -233,6 +306,8 @@ widget-choose (setq val try)) (cdr (assoc val items)))))) (t + (when (keymapp items) + (setq items (widget--simplify-menu items))) ;; Construct a menu of the choices ;; and then use it for prompting for a single character. (let* ((next-digit ?0) -- 2.28.0 ^ permalink raw reply related [flat|nested] 8+ messages in thread
* bug#4787: 23.1; in Customize, after showing Lisp expression, how to see normal? 2020-10-02 15:24 ` Mauro Aranda @ 2020-10-03 17:32 ` Lars Ingebrigtsen 2020-10-05 13:28 ` Mauro Aranda 0 siblings, 1 reply; 8+ messages in thread From: Lars Ingebrigtsen @ 2020-10-03 17:32 UTC (permalink / raw) To: Mauro Aranda; +Cc: 4787 Mauro Aranda <maurooaranda@gmail.com> writes: > Anyway, I attach a patch to add support for extended menus in > widget-choose, as a first step to add radio buttons for related options > in these Custom menus. Looks good to me, so I've pushed this to the trunk. -- (domestic pets only, the antidote for overdose, milk.) bloggy blog: http://lars.ingebrigtsen.no ^ permalink raw reply [flat|nested] 8+ messages in thread
* bug#4787: 23.1; in Customize, after showing Lisp expression, how to see normal? 2020-10-03 17:32 ` Lars Ingebrigtsen @ 2020-10-05 13:28 ` Mauro Aranda 2020-10-06 1:34 ` Lars Ingebrigtsen 0 siblings, 1 reply; 8+ messages in thread From: Mauro Aranda @ 2020-10-05 13:28 UTC (permalink / raw) To: Lars Ingebrigtsen; +Cc: 4787 [-- Attachment #1.1: Type: text/plain, Size: 747 bytes --] Lars Ingebrigtsen <larsi@gnus.org> writes: > Mauro Aranda <maurooaranda@gmail.com> writes: > >> Anyway, I attach a patch to add support for extended menus in >> widget-choose, as a first step to add radio buttons for related options >> in these Custom menus. > > Looks good to me, so I've pushed this to the trunk. Great, thanks. The attached patch makes Custom use extended menus when invoking the State button. With this patch, custom-menu-filter wouldn't be used anymore in Custom, but I've found that the jdee package uses it. Another concern is that there might be 3rd party code that assumes these custom menus are in the simplified menu format, and add stuff to the list, or some other stuff. I'm not really sure what to do about it. [-- Attachment #1.2: Type: text/html, Size: 986 bytes --] [-- Attachment #2: 0001-Make-the-State-button-in-Custom-use-extended-menus.patch --] [-- Type: text/x-patch, Size: 16627 bytes --] From 00bad0d4ebde8c0fb7537c26a4ba44dc520d5593 Mon Sep 17 00:00:00 2001 From: Mauro Aranda <maurooaranda@gmail.com> Date: Mon, 5 Oct 2020 09:32:14 -0300 Subject: [PATCH] Make the State button in Custom use extended menus * lisp/cus-edit.el (custom-actioned-widget): New variable. Dynamically hold the widget for which to show the menu. (custom-variable-menu, custom-face-menu, custom-group-menu): Change to keymap menus. Use custom-actioned-widget for the :enable and :selected forms. Make related items radio buttons. (Bug#4787) (custom-variable-action, custom-face-action, custom-group-action): Stop using custom-menu-filter when passing the menu to widget-choose; just pass the keymap menu. (custom-menu-filter): Add a comment explaining why Custom doesn't need this function anymore. --- lisp/cus-edit.el | 261 +++++++++++++++++++++++++++-------------------- 1 file changed, 153 insertions(+), 108 deletions(-) diff --git a/lisp/cus-edit.el b/lisp/cus-edit.el index a62b623c44..5f8a2fe40f 100644 --- a/lisp/cus-edit.el +++ b/lisp/cus-edit.el @@ -487,6 +487,16 @@ custom-variable-prompt (if (symbolp v) v nil) (intern val))))) +(defvar custom-actioned-widget nil + "Widget for which to show the menu of available actions. + +When showing a menu for a custom-variable, custom-face or custom-group widget, +the respective custom-*-action functions bind this variable to that widget, and +the respective custom-*-menu menus use the binding in their :enable and +:selected forms.") + +;; Unused: Because `widget-choose' now supports keymap menus, we don't need to +;; do the filtering ourselves. (defun custom-menu-filter (menu widget) "Convert MENU to the form used by `widget-choose'. MENU should be in the same format as `custom-variable-menu'. @@ -2852,51 +2862,67 @@ custom-variable-standard-value (get (widget-value widget) 'standard-value)) (defvar custom-variable-menu - `(("Set for Current Session" custom-variable-set - (lambda (widget) - (eq (widget-get widget :custom-state) 'modified))) - ;; Note that in all the backquoted code in this file, we test - ;; init-file-user rather than user-init-file. This is in case - ;; cus-edit is loaded by something in site-start.el, because + ;; No need to give the keymap a prompt, `widget-choose' takes care of it. + (let ((map (make-sparse-keymap))) + (define-key-after map [custom-variable-set] + '(menu-item "Set for Current Session" custom-variable-set + :enable (eq (widget-get custom-actioned-widget :custom-state) + 'modified))) + ;; Conditionally add items that depend on having loaded the custom-file, + ;; rather than giving it a :visible form, because we used to conditionally + ;; add this item when using simplified menus. + ;; Note that we test init-file-user rather than user-init-file. This is + ;; in case cus-edit is loaded by something in site-start.el, because ;; user-init-file is not set at that stage. ;; https://lists.gnu.org/r/emacs-devel/2007-10/msg00310.html - ,@(when (or custom-file init-file-user) - '(("Save for Future Sessions" custom-variable-save - (lambda (widget) - (memq (widget-get widget :custom-state) - '(modified set changed rogue)))))) - ("Undo Edits" custom-redraw - (lambda (widget) - (and (default-boundp (widget-value widget)) - (memq (widget-get widget :custom-state) '(modified changed))))) - ("Revert This Session's Customization" custom-variable-reset-saved - (lambda (widget) - (memq (widget-get widget :custom-state) - '(modified set changed rogue)))) - ,@(when (or custom-file init-file-user) - '(("Erase Customization" custom-variable-reset-standard - (lambda (widget) - (and (get (widget-value widget) 'standard-value) - (memq (widget-get widget :custom-state) - '(modified set changed saved rogue))))))) - ("Set to Backup Value" custom-variable-reset-backup - (lambda (widget) - (get (widget-value widget) 'backup-value))) - ("---" ignore ignore) - ("Add Comment" custom-comment-show custom-comment-invisible-p) - ("---" ignore ignore) - ("Show Current Value" custom-variable-edit - (lambda (widget) - (eq (widget-get widget :custom-form) 'lisp))) - ("Show Saved Lisp Expression" custom-variable-edit-lisp - (lambda (widget) - (eq (widget-get widget :custom-form) 'edit)))) - "Alist of actions for the `custom-variable' widget. -Each entry has the form (NAME ACTION FILTER) where NAME is the name of -the menu entry, ACTION is the function to call on the widget when the -menu is selected, and FILTER is a predicate which takes a `custom-variable' -widget as an argument, and returns non-nil if ACTION is valid on that -widget. If FILTER is nil, ACTION is always valid.") + (when (or custom-file init-file-user) + (define-key-after map [custom-variable-save] + '(menu-item "Save for Future Sessions" custom-variable-save + :enable (memq + (widget-get custom-actioned-widget :custom-state) + '(modified set changed rogue))))) + (define-key-after map [custom-redraw] + '(menu-item "Undo Edits" custom-redraw + :enable (memq + (widget-get custom-actioned-widget :custom-state) + '(modified changed)))) + (define-key-after map [custom-variable-reset-saved] + '(menu-item "Revert This Session's Customization" + custom-variable-reset-saved + :enable (memq + (widget-get custom-actioned-widget :custom-state) + '(modified set changed rogue)))) + (when (or custom-file init-file-user) + (define-key-after map [custom-variable-reset-standard] + '(menu-item "Erase Customization" custom-variable-reset-standard + :enable (memq + (widget-get custom-actioned-widget :custom-state) + '(modified set changed saved rogue))))) + (define-key-after map [custom-variable-reset-backup] + '(menu-item "Set to Backup Value" custom-variable-reset-backup + :enable (get + (widget-value custom-actioned-widget) + 'backup-value))) + (define-key-after map [sep0] + '(menu-item "---")) + (define-key-after map [custom-comment-show] + '(menu-item "Add Comment" custom-comment-show + :enable (custom-comment-invisible-p custom-actioned-widget))) + (define-key-after map [sep1] + '(menu-item "---")) + (define-key-after map [custom-variable-edit] + '(menu-item "Show Current Value" custom-variable-edit + :button (:radio . (eq (widget-get custom-actioned-widget + :custom-form) + 'edit)))) + (define-key-after map [custom-variable-edit-lisp] + '(menu-item "Show Saved Lisp Expression" custom-variable-edit-lisp + :button (:radio . (eq (widget-get custom-actioned-widget + :custom-form) + 'lisp)))) + map) + "A menu for `custom-variable' widgets. +Used in `custom-variable-action' to show a menu to the user.") (defun custom-variable-action (widget &optional event) "Show the menu for `custom-variable' WIDGET. @@ -2907,12 +2933,11 @@ custom-variable-action (custom-variable-state-set widget)) (custom-redraw-magic widget) (let* ((completion-ignore-case t) + (custom-actioned-widget widget) (answer (widget-choose (concat "Operation on " - (custom-unlispify-tag-name - (widget-get widget :value))) - (custom-menu-filter custom-variable-menu - widget) - event))) + (custom-unlispify-tag-name + (widget-get widget :value))) + custom-variable-menu event))) (if answer (funcall answer widget))))) @@ -3677,37 +3702,52 @@ custom-face-value-create (custom-face-state-set widget)))))) (defvar custom-face-menu - `(("Set for Current Session" custom-face-set) - ,@(when (or custom-file init-file-user) - '(("Save for Future Sessions" custom-face-save))) - ("Undo Edits" custom-redraw - (lambda (widget) - (memq (widget-get widget :custom-state) '(modified changed)))) - ("Revert This Session's Customization" custom-face-reset-saved - (lambda (widget) - (memq (widget-get widget :custom-state) '(modified set changed)))) - ,@(when (or custom-file init-file-user) - '(("Erase Customization" custom-face-reset-standard - (lambda (widget) - (get (widget-value widget) 'face-defface-spec))))) - ("---" ignore ignore) - ("Add Comment" custom-comment-show custom-comment-invisible-p) - ("---" ignore ignore) - ("For Current Display" custom-face-edit-selected - (lambda (widget) - (not (eq (widget-get widget :custom-form) 'selected)))) - ("For All Kinds of Displays" custom-face-edit-all - (lambda (widget) - (not (eq (widget-get widget :custom-form) 'all)))) - ("Show Lisp Expression" custom-face-edit-lisp - (lambda (widget) - (not (eq (widget-get widget :custom-form) 'lisp))))) - "Alist of actions for the `custom-face' widget. -Each entry has the form (NAME ACTION FILTER) where NAME is the name of -the menu entry, ACTION is the function to call on the widget when the -menu is selected, and FILTER is a predicate which takes a `custom-face' -widget as an argument, and returns non-nil if ACTION is valid on that -widget. If FILTER is nil, ACTION is always valid.") + (let ((map (make-sparse-keymap))) + (define-key-after map [custom-face-set] + '(menu-item "Set for Current Session" custom-face-set)) + (when (or custom-file init-file-user) + (define-key-after map [custom-face-save] + '(menu-item "Save for Future Sessions" custom-face-save))) + (define-key-after map [custom-redraw] + '(menu-item "Undo Edits" custom-redraw + :enable (memq + (widget-get custom-actioned-widget :custom-state) + '(modified changed)))) + (define-key-after map [custom-face-reset-saved] + '(menu-item "Revert This Session's Customization" custom-face-reset-saved + :enable (memq + (widget-get custom-actioned-widget :custom-state) + '(modified set changed)))) + (when (or custom-file init-file-user) + (define-key-after map [custom-face-reset-standard] + '(menu-item "Erase Customization" custom-face-reset-standard + :enable (get (widget-value custom-actioned-widget) + 'face-defface-spec)))) + (define-key-after map [sep0] + '(menu-item "---")) + (define-key-after map [custom-comment-show] + '(menu-item "Add Comment" custom-comment-show + :enable (custom-comment-invisible-p custom-actioned-widget))) + (define-key-after map [sep1] + '(menu-item "---")) + (define-key-after map [custom-face-edit-selected] + '(menu-item "For Current Display" custom-face-edit-selected + :button (:radio . (eq (widget-get custom-actioned-widget + :custom-form) + 'selected)))) + (define-key-after map [custom-face-edit-all] + '(menu-item "For All Kinds of Displays" custom-face-edit-all + :button (:radio . (eq (widget-get custom-actioned-widget + :custom-form) + 'all)))) + (define-key-after map [custom-face-edit-lisp] + '(menu-item "Show Lisp Expression" custom-face-edit-lisp + :button (:radio . (eq (widget-get custom-actioned-widget + :custom-form) + 'lisp)))) + map) + "A menu for `custom-face' widgets. +Used in `custom-face-action' to show a menu to the user.") (defun custom-face-edit-selected (widget) "Edit selected attributes of the value of WIDGET." @@ -3775,12 +3815,11 @@ custom-face-action (if (eq (widget-get widget :custom-state) 'hidden) (custom-toggle-hide widget) (let* ((completion-ignore-case t) + (custom-actioned-widget widget) (symbol (widget-get widget :value)) (answer (widget-choose (concat "Operation on " (custom-unlispify-tag-name symbol)) - (custom-menu-filter custom-face-menu - widget) - event))) + custom-face-menu event))) (if answer (funcall answer widget))))) @@ -4311,29 +4350,36 @@ custom-group-value-create (custom-group--draw-horizontal-line))))) (defvar custom-group-menu - `(("Set for Current Session" custom-group-set - (lambda (widget) - (eq (widget-get widget :custom-state) 'modified))) - ,@(when (or custom-file init-file-user) - '(("Save for Future Sessions" custom-group-save - (lambda (widget) - (memq (widget-get widget :custom-state) '(modified set)))))) - ("Undo Edits" custom-group-reset-current - (lambda (widget) - (memq (widget-get widget :custom-state) '(modified)))) - ("Revert This Session's Customizations" custom-group-reset-saved - (lambda (widget) - (memq (widget-get widget :custom-state) '(modified set)))) - ,@(when (or custom-file init-file-user) - '(("Erase Customization" custom-group-reset-standard - (lambda (widget) - (memq (widget-get widget :custom-state) '(modified set saved))))))) - "Alist of actions for the `custom-group' widget. -Each entry has the form (NAME ACTION FILTER) where NAME is the name of -the menu entry, ACTION is the function to call on the widget when the -menu is selected, and FILTER is a predicate which takes a `custom-group' -widget as an argument, and returns non-nil if ACTION is valid on that -widget. If FILTER is nil, ACTION is always valid.") + (let ((map (make-sparse-keymap))) + (define-key-after map [custom-group-set] + '(menu-item "Set for Current Session" custom-group-set + :enable (eq (widget-get custom-actioned-widget :custom-state) + 'modified))) + (when (or custom-file init-file-user) + (define-key-after map [custom-group-save] + '(menu-item "Save for Future Sessions" custom-group-save + :enable (memq + (widget-get custom-actioned-widget :custom-state) + '(modified set))))) + (define-key-after map [custom-group-reset-current] + '(menu-item "Undo Edits" custom-group-reset-current + :enable (eq (widget-get custom-actioned-widget :custom-state) + 'modified))) + (define-key-after map [custom-group-reset-saved] + '(menu-item "Revert This Session's Customizations" + custom-group-reset-saved + :enable (memq + (widget-get custom-actioned-widget :custom-state) + '(modified set)))) + (when (or custom-file init-file-user) + (define-key-after map [custom-group-reset-standard] + '(menu-item "Erase Customization" custom-group-reset-standard + :enable (memq + (widget-get custom-actioned-widget :custom-state) + '(modified set saved))))) + map) + "A menu for `custom-group' widgets. +Used in `custom-group-action' to show a menu to the user.") (defun custom-group-action (widget &optional event) "Show the menu for `custom-group' WIDGET. @@ -4341,12 +4387,11 @@ custom-group-action (if (eq (widget-get widget :custom-state) 'hidden) (custom-toggle-hide widget) (let* ((completion-ignore-case t) + (custom-actioned-widget widget) (answer (widget-choose (concat "Operation on " (custom-unlispify-tag-name (widget-get widget :value))) - (custom-menu-filter custom-group-menu - widget) - event))) + custom-group-menu event))) (if answer (funcall answer widget))))) -- 2.28.0 ^ permalink raw reply related [flat|nested] 8+ messages in thread
* bug#4787: 23.1; in Customize, after showing Lisp expression, how to see normal? 2020-10-05 13:28 ` Mauro Aranda @ 2020-10-06 1:34 ` Lars Ingebrigtsen 2020-10-06 12:39 ` Mauro Aranda 0 siblings, 1 reply; 8+ messages in thread From: Lars Ingebrigtsen @ 2020-10-06 1:34 UTC (permalink / raw) To: Mauro Aranda; +Cc: 4787 Mauro Aranda <maurooaranda@gmail.com> writes: > With this patch, custom-menu-filter wouldn't be used anymore in Custom, > but I've found that the jdee package uses it. Another concern is that > there might be 3rd party code that assumes these custom menus are > in the simplified menu format, and add stuff to the list, or some other stuff. > > I'm not really sure what to do about it. Is there a way to make this backwards-compatible? For instance, by introducing a new name for the new menu stuff, and having a compatibility shim for the old custom menus? I say this without having looked at the code closely, so I'm kinda uninformed here. -- (domestic pets only, the antidote for overdose, milk.) bloggy blog: http://lars.ingebrigtsen.no ^ permalink raw reply [flat|nested] 8+ messages in thread
* bug#4787: 23.1; in Customize, after showing Lisp expression, how to see normal? 2020-10-06 1:34 ` Lars Ingebrigtsen @ 2020-10-06 12:39 ` Mauro Aranda 2020-10-07 2:36 ` Lars Ingebrigtsen 0 siblings, 1 reply; 8+ messages in thread From: Mauro Aranda @ 2020-10-06 12:39 UTC (permalink / raw) To: Lars Ingebrigtsen; +Cc: 4787 [-- Attachment #1.1: Type: text/plain, Size: 846 bytes --] Lars Ingebrigtsen <larsi@gnus.org> writes: > Mauro Aranda <maurooaranda@gmail.com> writes: > >> With this patch, custom-menu-filter wouldn't be used anymore in Custom, >> but I've found that the jdee package uses it. Another concern is that >> there might be 3rd party code that assumes these custom menus are >> in the simplified menu format, and add stuff to the list, or some other stuff. >> >> I'm not really sure what to do about it. > > Is there a way to make this backwards-compatible? For instance, by > introducing a new name for the new menu stuff, and having a > compatibility shim for the old custom menus? > > I say this without having looked at the code closely, so I'm kinda > uninformed here. I attach a more conservative patch. It could get more conservative (and more hacky at the same time), but maybe this one is enough. [-- Attachment #1.2: Type: text/html, Size: 1106 bytes --] [-- Attachment #2: 0001-Make-the-State-button-in-Custom-use-extended-menus.patch --] [-- Type: text/x-patch, Size: 18567 bytes --] From 8b032b067303211f0c933ab683cf9f24d22d1484 Mon Sep 17 00:00:00 2001 From: Mauro Aranda <maurooaranda@gmail.com> Date: Mon, 5 Oct 2020 09:32:14 -0300 Subject: [PATCH] Make the State button in Custom use extended menus * lisp/cus-edit.el (custom-actioned-widget): New variable. Dynamically hold the widget for which to show the menu. (custom-variable-extended-menu, custom-face-extended-menu) (custom-group-extended-menu): Keymap menus for the State menu. Use custom-actioned-widget for the :enable and :selected forms. Make related items radio buttons. (Bug#4787) (custom-variable-menu, custom-face-menu, custom-group-menu): Keep for backward compatibility, but default to nil, so we prefer the keymap menus instead. (custom-variable-action, custom-face-action, custom-group-action): Pass the keymap menu to widget-choose when the simplified menus are nil. --- lisp/cus-edit.el | 289 +++++++++++++++++++++++++++++++---------------- 1 file changed, 191 insertions(+), 98 deletions(-) diff --git a/lisp/cus-edit.el b/lisp/cus-edit.el index a62b623c44..9c5d89f89f 100644 --- a/lisp/cus-edit.el +++ b/lisp/cus-edit.el @@ -487,6 +487,14 @@ custom-variable-prompt (if (symbolp v) v nil) (intern val))))) +(defvar custom-actioned-widget nil + "Widget for which to show the menu of available actions. + +When showing a menu for a custom-variable, custom-face or custom-group widget, +the respective custom-*-action functions bind this variable to that widget, and +the respective custom-*-menu menus use the binding in their :enable and +:selected forms.") + (defun custom-menu-filter (menu widget) "Convert MENU to the form used by `widget-choose'. MENU should be in the same format as `custom-variable-menu'. @@ -2851,53 +2859,81 @@ custom-variable-state-set (defun custom-variable-standard-value (widget) (get (widget-value widget) 'standard-value)) -(defvar custom-variable-menu - `(("Set for Current Session" custom-variable-set - (lambda (widget) - (eq (widget-get widget :custom-state) 'modified))) - ;; Note that in all the backquoted code in this file, we test - ;; init-file-user rather than user-init-file. This is in case - ;; cus-edit is loaded by something in site-start.el, because - ;; user-init-file is not set at that stage. - ;; https://lists.gnu.org/r/emacs-devel/2007-10/msg00310.html - ,@(when (or custom-file init-file-user) - '(("Save for Future Sessions" custom-variable-save - (lambda (widget) - (memq (widget-get widget :custom-state) - '(modified set changed rogue)))))) - ("Undo Edits" custom-redraw - (lambda (widget) - (and (default-boundp (widget-value widget)) - (memq (widget-get widget :custom-state) '(modified changed))))) - ("Revert This Session's Customization" custom-variable-reset-saved - (lambda (widget) - (memq (widget-get widget :custom-state) - '(modified set changed rogue)))) - ,@(when (or custom-file init-file-user) - '(("Erase Customization" custom-variable-reset-standard - (lambda (widget) - (and (get (widget-value widget) 'standard-value) - (memq (widget-get widget :custom-state) - '(modified set changed saved rogue))))))) - ("Set to Backup Value" custom-variable-reset-backup - (lambda (widget) - (get (widget-value widget) 'backup-value))) - ("---" ignore ignore) - ("Add Comment" custom-comment-show custom-comment-invisible-p) - ("---" ignore ignore) - ("Show Current Value" custom-variable-edit - (lambda (widget) - (eq (widget-get widget :custom-form) 'lisp))) - ("Show Saved Lisp Expression" custom-variable-edit-lisp - (lambda (widget) - (eq (widget-get widget :custom-form) 'edit)))) - "Alist of actions for the `custom-variable' widget. +(defvar custom-variable-menu nil + "If non-nil, an alist of actions for the `custom-variable' widget. + +This variable is kept for backward compatibility reasons, please use +`custom-variable-extended-menu' instead. + Each entry has the form (NAME ACTION FILTER) where NAME is the name of the menu entry, ACTION is the function to call on the widget when the menu is selected, and FILTER is a predicate which takes a `custom-variable' widget as an argument, and returns non-nil if ACTION is valid on that widget. If FILTER is nil, ACTION is always valid.") +(defvar custom-variable-extended-menu + ;; No need to give the keymap a prompt, `widget-choose' takes care of it. + (let ((map (make-sparse-keymap))) + (define-key-after map [custom-variable-set] + '(menu-item "Set for Current Session" custom-variable-set + :enable (eq (widget-get custom-actioned-widget :custom-state) + 'modified))) + ;; Conditionally add items that depend on having loaded the custom-file, + ;; rather than giving it a :visible form, because we used to conditionally + ;; add this item when using simplified menus. + ;; Note that we test init-file-user rather than user-init-file. This is + ;; in case cus-edit is loaded by something in site-start.el, because + ;; user-init-file is not set at that stage. + ;; https://lists.gnu.org/r/emacs-devel/2007-10/msg00310.html + (when (or custom-file init-file-user) + (define-key-after map [custom-variable-save] + '(menu-item "Save for Future Sessions" custom-variable-save + :enable (memq + (widget-get custom-actioned-widget :custom-state) + '(modified set changed rogue))))) + (define-key-after map [custom-redraw] + '(menu-item "Undo Edits" custom-redraw + :enable (memq + (widget-get custom-actioned-widget :custom-state) + '(modified changed)))) + (define-key-after map [custom-variable-reset-saved] + '(menu-item "Revert This Session's Customization" + custom-variable-reset-saved + :enable (memq + (widget-get custom-actioned-widget :custom-state) + '(modified set changed rogue)))) + (when (or custom-file init-file-user) + (define-key-after map [custom-variable-reset-standard] + '(menu-item "Erase Customization" custom-variable-reset-standard + :enable (memq + (widget-get custom-actioned-widget :custom-state) + '(modified set changed saved rogue))))) + (define-key-after map [custom-variable-reset-backup] + '(menu-item "Set to Backup Value" custom-variable-reset-backup + :enable (get + (widget-value custom-actioned-widget) + 'backup-value))) + (define-key-after map [sep0] + '(menu-item "---")) + (define-key-after map [custom-comment-show] + '(menu-item "Add Comment" custom-comment-show + :enable (custom-comment-invisible-p custom-actioned-widget))) + (define-key-after map [sep1] + '(menu-item "---")) + (define-key-after map [custom-variable-edit] + '(menu-item "Show Current Value" custom-variable-edit + :button (:radio . (eq (widget-get custom-actioned-widget + :custom-form) + 'edit)))) + (define-key-after map [custom-variable-edit-lisp] + '(menu-item "Show Saved Lisp Expression" custom-variable-edit-lisp + :button (:radio . (eq (widget-get custom-actioned-widget + :custom-form) + 'lisp)))) + map) + "A menu for `custom-variable' widgets. +Used in `custom-variable-action' to show a menu to the user.") + (defun custom-variable-action (widget &optional event) "Show the menu for `custom-variable' WIDGET. Optional EVENT is the location for the menu." @@ -2907,12 +2943,17 @@ custom-variable-action (custom-variable-state-set widget)) (custom-redraw-magic widget) (let* ((completion-ignore-case t) + (custom-actioned-widget widget) (answer (widget-choose (concat "Operation on " - (custom-unlispify-tag-name - (widget-get widget :value))) - (custom-menu-filter custom-variable-menu - widget) - event))) + (custom-unlispify-tag-name + (widget-get widget :value))) + ;; Get rid of checks like this one if we ever + ;; remove the simplified menus. + (if custom-variable-menu + (custom-menu-filter custom-variable-menu + widget) + custom-variable-extended-menu) + event))) (if answer (funcall answer widget))))) @@ -3676,39 +3717,66 @@ custom-face-value-create (widget-put widget :children children) (custom-face-state-set widget)))))) -(defvar custom-face-menu - `(("Set for Current Session" custom-face-set) - ,@(when (or custom-file init-file-user) - '(("Save for Future Sessions" custom-face-save))) - ("Undo Edits" custom-redraw - (lambda (widget) - (memq (widget-get widget :custom-state) '(modified changed)))) - ("Revert This Session's Customization" custom-face-reset-saved - (lambda (widget) - (memq (widget-get widget :custom-state) '(modified set changed)))) - ,@(when (or custom-file init-file-user) - '(("Erase Customization" custom-face-reset-standard - (lambda (widget) - (get (widget-value widget) 'face-defface-spec))))) - ("---" ignore ignore) - ("Add Comment" custom-comment-show custom-comment-invisible-p) - ("---" ignore ignore) - ("For Current Display" custom-face-edit-selected - (lambda (widget) - (not (eq (widget-get widget :custom-form) 'selected)))) - ("For All Kinds of Displays" custom-face-edit-all - (lambda (widget) - (not (eq (widget-get widget :custom-form) 'all)))) - ("Show Lisp Expression" custom-face-edit-lisp - (lambda (widget) - (not (eq (widget-get widget :custom-form) 'lisp))))) - "Alist of actions for the `custom-face' widget. +(defvar custom-face-menu nil + "If non-nil, an alist of actions for the `custom-face' widget. + +This variable is kept for backward compatibility reasons, please use +`custom-face-extended-menu' instead. + Each entry has the form (NAME ACTION FILTER) where NAME is the name of the menu entry, ACTION is the function to call on the widget when the menu is selected, and FILTER is a predicate which takes a `custom-face' widget as an argument, and returns non-nil if ACTION is valid on that widget. If FILTER is nil, ACTION is always valid.") +(defvar custom-face-extended-menu + (let ((map (make-sparse-keymap))) + (define-key-after map [custom-face-set] + '(menu-item "Set for Current Session" custom-face-set)) + (when (or custom-file init-file-user) + (define-key-after map [custom-face-save] + '(menu-item "Save for Future Sessions" custom-face-save))) + (define-key-after map [custom-redraw] + '(menu-item "Undo Edits" custom-redraw + :enable (memq + (widget-get custom-actioned-widget :custom-state) + '(modified changed)))) + (define-key-after map [custom-face-reset-saved] + '(menu-item "Revert This Session's Customization" custom-face-reset-saved + :enable (memq + (widget-get custom-actioned-widget :custom-state) + '(modified set changed)))) + (when (or custom-file init-file-user) + (define-key-after map [custom-face-reset-standard] + '(menu-item "Erase Customization" custom-face-reset-standard + :enable (get (widget-value custom-actioned-widget) + 'face-defface-spec)))) + (define-key-after map [sep0] + '(menu-item "---")) + (define-key-after map [custom-comment-show] + '(menu-item "Add Comment" custom-comment-show + :enable (custom-comment-invisible-p custom-actioned-widget))) + (define-key-after map [sep1] + '(menu-item "---")) + (define-key-after map [custom-face-edit-selected] + '(menu-item "For Current Display" custom-face-edit-selected + :button (:radio . (eq (widget-get custom-actioned-widget + :custom-form) + 'selected)))) + (define-key-after map [custom-face-edit-all] + '(menu-item "For All Kinds of Displays" custom-face-edit-all + :button (:radio . (eq (widget-get custom-actioned-widget + :custom-form) + 'all)))) + (define-key-after map [custom-face-edit-lisp] + '(menu-item "Show Lisp Expression" custom-face-edit-lisp + :button (:radio . (eq (widget-get custom-actioned-widget + :custom-form) + 'lisp)))) + map) + "A menu for `custom-face' widgets. +Used in `custom-face-action' to show a menu to the user.") + (defun custom-face-edit-selected (widget) "Edit selected attributes of the value of WIDGET." (widget-put widget :custom-state 'unknown) @@ -3775,12 +3843,15 @@ custom-face-action (if (eq (widget-get widget :custom-state) 'hidden) (custom-toggle-hide widget) (let* ((completion-ignore-case t) + (custom-actioned-widget widget) (symbol (widget-get widget :value)) (answer (widget-choose (concat "Operation on " (custom-unlispify-tag-name symbol)) - (custom-menu-filter custom-face-menu - widget) - event))) + (if custom-face-menu + (custom-menu-filter custom-face-menu + widget) + custom-face-extended-menu) + event))) (if answer (funcall answer widget))))) @@ -4310,43 +4381,65 @@ custom-group-value-create (insert "\n") (custom-group--draw-horizontal-line))))) -(defvar custom-group-menu - `(("Set for Current Session" custom-group-set - (lambda (widget) - (eq (widget-get widget :custom-state) 'modified))) - ,@(when (or custom-file init-file-user) - '(("Save for Future Sessions" custom-group-save - (lambda (widget) - (memq (widget-get widget :custom-state) '(modified set)))))) - ("Undo Edits" custom-group-reset-current - (lambda (widget) - (memq (widget-get widget :custom-state) '(modified)))) - ("Revert This Session's Customizations" custom-group-reset-saved - (lambda (widget) - (memq (widget-get widget :custom-state) '(modified set)))) - ,@(when (or custom-file init-file-user) - '(("Erase Customization" custom-group-reset-standard - (lambda (widget) - (memq (widget-get widget :custom-state) '(modified set saved))))))) - "Alist of actions for the `custom-group' widget. +(defvar custom-group-menu nil + "If non-nil, an alist of actions for the `custom-group' widget. + +This variable is kept for backward compatibility reasons, please use +`custom-group-extended-menu' instead. + Each entry has the form (NAME ACTION FILTER) where NAME is the name of the menu entry, ACTION is the function to call on the widget when the menu is selected, and FILTER is a predicate which takes a `custom-group' widget as an argument, and returns non-nil if ACTION is valid on that widget. If FILTER is nil, ACTION is always valid.") +(defvar custom-group-extended-menu + (let ((map (make-sparse-keymap))) + (define-key-after map [custom-group-set] + '(menu-item "Set for Current Session" custom-group-set + :enable (eq (widget-get custom-actioned-widget :custom-state) + 'modified))) + (when (or custom-file init-file-user) + (define-key-after map [custom-group-save] + '(menu-item "Save for Future Sessions" custom-group-save + :enable (memq + (widget-get custom-actioned-widget :custom-state) + '(modified set))))) + (define-key-after map [custom-group-reset-current] + '(menu-item "Undo Edits" custom-group-reset-current + :enable (eq (widget-get custom-actioned-widget :custom-state) + 'modified))) + (define-key-after map [custom-group-reset-saved] + '(menu-item "Revert This Session's Customizations" + custom-group-reset-saved + :enable (memq + (widget-get custom-actioned-widget :custom-state) + '(modified set)))) + (when (or custom-file init-file-user) + (define-key-after map [custom-group-reset-standard] + '(menu-item "Erase Customization" custom-group-reset-standard + :enable (memq + (widget-get custom-actioned-widget :custom-state) + '(modified set saved))))) + map) + "A menu for `custom-group' widgets. +Used in `custom-group-action' to show a menu to the user.") + (defun custom-group-action (widget &optional event) "Show the menu for `custom-group' WIDGET. Optional EVENT is the location for the menu." (if (eq (widget-get widget :custom-state) 'hidden) (custom-toggle-hide widget) (let* ((completion-ignore-case t) + (custom-actioned-widget widget) (answer (widget-choose (concat "Operation on " (custom-unlispify-tag-name (widget-get widget :value))) - (custom-menu-filter custom-group-menu - widget) - event))) + (if custom-group-menu + (custom-menu-filter custom-group-menu + widget) + custom-group-extended-menu) + event))) (if answer (funcall answer widget))))) -- 2.28.0 ^ permalink raw reply related [flat|nested] 8+ messages in thread
* bug#4787: 23.1; in Customize, after showing Lisp expression, how to see normal? 2020-10-06 12:39 ` Mauro Aranda @ 2020-10-07 2:36 ` Lars Ingebrigtsen 0 siblings, 0 replies; 8+ messages in thread From: Lars Ingebrigtsen @ 2020-10-07 2:36 UTC (permalink / raw) To: Mauro Aranda; +Cc: 4787 Mauro Aranda <maurooaranda@gmail.com> writes: > I attach a more conservative patch. It could get more conservative (and > more hacky at the same time), but maybe this one is enough. Thanks; looks good, and I've applied it to Emacs 28. -- (domestic pets only, the antidote for overdose, milk.) bloggy blog: http://lars.ingebrigtsen.no ^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2020-10-07 2:36 UTC | newest] Thread overview: 8+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2009-10-22 22:00 bug#4787: 23.1; in Customize, after showing Lisp expression, how to see normal? Drew Adams 2012-02-05 18:42 ` Drew Adams 2020-10-02 15:24 ` Mauro Aranda 2020-10-03 17:32 ` Lars Ingebrigtsen 2020-10-05 13:28 ` Mauro Aranda 2020-10-06 1:34 ` Lars Ingebrigtsen 2020-10-06 12:39 ` Mauro Aranda 2020-10-07 2:36 ` Lars Ingebrigtsen
Code repositories for project(s) associated with this external index https://git.savannah.gnu.org/cgit/emacs.git https://git.savannah.gnu.org/cgit/emacs/org-mode.git This is an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.