Skip to content

Commit 14b7a78

Browse files
committed
[wip] Add alternative accessibility formats
1 parent 8bd31e9 commit 14b7a78

2 files changed

Lines changed: 152 additions & 14 deletions

File tree

docs/transient.org

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -685,6 +685,8 @@ don't hesitate to contact me.
685685
(setopt transient-select-menu-window t)
686686
(setopt transient-force-single-column t)
687687
(setopt transient-navigate-to-group-descriptions t)
688+
(setopt transient-always-read-value t)
689+
(setopt transient-use-accessible-format 'braille)
688690
(setopt transient-describe-menu t)
689691
(add-hook 'transient-setup-buffer-hook 'ding)
690692
#+end_src
@@ -726,6 +728,36 @@ text at point, to be run when a transient menu is active, for example:
726728
This is useful because it allows braille and audio devices to output
727729
the group title at point.
728730

731+
- User Option: transient-always-read-value ::
732+
733+
Whether to always read the new value.
734+
735+
If this is nil, then certain arguments are unconditionally disabled when
736+
they are invoked while they have a non-nil value. They then have to be
737+
invoked a second time to set another non-nil value. For other arguments
738+
which have a fixed set of possible values, all values are displayed at
739+
all times and the current non-nil value (if any) is highlighted using a
740+
different face.
741+
742+
If this is t, then more arguments will always read the value using the
743+
minibuffer. This is only intended for blind users, for whom the default
744+
behavior is problematic.
745+
746+
- User Option: transient-use-accessible-format ::
747+
748+
Whether to use present arguments and commands in a more accessible format.
749+
750+
If this is nil, then the default format, from the `format' slot is used.
751+
Blind users should set this to either `braille' or `audio' to sue the
752+
alternative format used in the respective slot.
753+
754+
Please note that I need your help to improve the default values used for
755+
these slots. If necessary I will also make this more customizable and
756+
add more documentation.
757+
758+
If this is enabled, then `transient-always-read-value' should be enabled
759+
as well.
760+
729761
- User Option: transient-describe-menu ::
730762

731763
This option controls whether short description is inserted at the
@@ -2622,6 +2654,17 @@ and ~advice*~ slots (see [[*Slots of ~transient-suffix~]]) are defined.
26222654
- ~%d~ For the description.
26232655
- ~%v~ For the infix value. Non-infix suffixes don't have a value.
26242656

2657+
The slots ~braille~ and ~audio~ work the same as ~format~. These
2658+
alternative formats are intended for blind users. Which format is
2659+
used depends on option ~transient-use-accessible-format~. The
2660+
following additional %-placeholders are intended for use in these
2661+
alternative format strings:
2662+
2663+
- ~%V~ For the raw infix value (without the argument).
2664+
- ~%a~ For the argument (or "(no argument)").
2665+
- ~%i~ One of the strings " inapt " or "", depending on whether the
2666+
argument is currently inapt.
2667+
26252668
- ~description~ The description, either a string or a function, which is
26262669
called with zero or one argument (the suffix object), and returns a
26272670
string.

lisp/transient.el

Lines changed: 109 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -536,6 +536,49 @@ in a more natural order."
536536
:group 'transient
537537
:type 'boolean)
538538

539+
(defcustom transient-always-read-value nil
540+
"Whether to always read the new value.
541+
542+
If this is nil, then certain arguments are unconditionally disabled when
543+
they are invoked while they have a non-nil value. They then have to be
544+
invoked a second time to set another non-nil value. For other arguments
545+
which have a fixed set of possible values, all values are displayed at
546+
all times and the current non-nil value (if any) is highlighted using a
547+
different face.
548+
549+
If this is t, then more arguments will always read the value using the
550+
minibuffer. This is only intended for blind users, for whom the default
551+
behavior is problematic."
552+
:package-version '(transient . "0.13.0")
553+
:group 'transient
554+
:type 'boolean)
555+
556+
(defcustom transient-use-accessible-format nil
557+
"Whether to present arguments and commands in a more accessible format.
558+
559+
If this is nil, then the default format, specified by the `format' slot
560+
is used. Blind users should set this to either `braille' or `audio' to
561+
use the alternative format specified by the respective slot.
562+
563+
Please note that these defaults are still subject to change and that I
564+
need your help to come up with appropriate values. If necessary I will
565+
also make this more customizable and add more documentation.
566+
567+
If you enabled this, then `transient-always-read-value' likely should be
568+
enabled as well."
569+
:package-version '(transient . "0.13.0")
570+
:group 'transient
571+
:type '(choice (const :tag "Use format specified by `format' slot" nil)
572+
(const :tag "Use format specified by `braille' slot" braille)
573+
(const :tag "Use format specified by `audio' slot" audio)))
574+
575+
(defcustom transient-overriding-format-alist nil
576+
"TODO"
577+
:package-version '(transient . "0.13.0")
578+
:group 'transient
579+
:type '(alist :key-type (symbol :tag "Class")
580+
:value-type (string :tag "Format")))
581+
539582
(defcustom transient-describe-menu nil
540583
"Whether to begin the menu buffer with a very short description.
541584
@@ -986,6 +1029,8 @@ predicate slots or more than one `inapt-if*' slots are non-nil."
9861029
(command :initarg :command)
9871030
(transient :initarg :transient)
9881031
(format :initarg :format :initform " %k %d")
1032+
(braille :initarg :braille :initform "%i%k %d")
1033+
(audio :initarg :audio :initform "%i%k %d")
9891034
(description :initarg :description :initform nil)
9901035
(face :initarg :face :initform nil)
9911036
(show-help :initarg :show-help :initform nil))
@@ -1016,7 +1061,9 @@ Technically a suffix object with no associated command.")
10161061
(reader :initarg :reader :initform nil)
10171062
(prompt :initarg :prompt :initform nil)
10181063
(choices :initarg :choices :initform nil)
1019-
(format :initform " %k %d (%v)"))
1064+
(format :initform " %k %d (%v)")
1065+
(braille :initform "%i%k %a is %V (%d)")
1066+
(audio :initform "%i%k %a is %V (%d)"))
10201067
"Transient infix command."
10211068
:abstract t)
10221069

@@ -1032,7 +1079,9 @@ Technically a suffix object with no associated command.")
10321079

10331080
(defclass transient-variable (transient-infix)
10341081
((variable :initarg :variable)
1035-
(format :initform " %k %d %v"))
1082+
(format :initform " %k %d %v")
1083+
(braille :initform "%k %a is %V (%d)")
1084+
(audio :initform "%k %a is %V (%d)"))
10361085
"Abstract superclass for infix commands that set a variable."
10371086
:abstract t)
10381087

@@ -3878,6 +3927,7 @@ it\", in which case it is pointless to preserve history.)"
38783927
(with-slots (value multi-value always-read allow-empty choices) obj
38793928
(if (and value
38803929
(not multi-value)
3930+
(not transient-always-read-value)
38813931
(not always-read)
38823932
transient--prefix)
38833933
(oset obj value nil)
@@ -3922,16 +3972,25 @@ it\", in which case it is pointless to preserve history.)"
39223972

39233973
(cl-defmethod transient-infix-read ((obj transient-switch))
39243974
"Toggle the switch on or off."
3925-
(if (oref obj value) nil (oref obj argument)))
3975+
(prog1 (if (oref obj value) nil (oref obj argument))
3976+
(when transient-always-read-value
3977+
(message "%s is now %s"
3978+
(oref obj argument)
3979+
(if (oref obj value) "enabled" "disabled")))))
39263980

39273981
(cl-defmethod transient-infix-read ((obj transient-switches))
39283982
"Cycle through the mutually exclusive switches.
39293983
The last value is \"don't use any of these switches\"."
39303984
(let ((choices (mapcar (apply-partially #'format (oref obj argument-format))
39313985
(oref obj choices))))
3932-
(if-let ((value (oref obj value)))
3933-
(cadr (member value choices))
3934-
(car choices))))
3986+
(cond-let
3987+
(transient-always-read-value
3988+
(completing-read (transient-prompt obj)
3989+
(cons "*disable*" choices)
3990+
nil t))
3991+
([value (oref obj value)]
3992+
(cadr (member value choices)))
3993+
((car choices)))))
39353994

39363995
(cl-defmethod transient-infix-read ((command symbol))
39373996
"Elsewhere use the reader of the infix command COMMAND.
@@ -4768,26 +4827,34 @@ as a button."
47684827
%d is formatted using `transient-format-description'.
47694828
%v is formatted using `transient-format-value'."
47704829
(static-if (>= emacs-major-version 29)
4771-
(format-spec (oref obj format)
4830+
(format-spec (transient--get-format obj)
47724831
`((?k . ,(lambda () (transient-format-key obj)))
47734832
(?d . ,(lambda () (transient-format-description obj)))
4774-
(?v . ,(lambda () (transient-format-value obj)))))
4775-
(format-spec (oref obj format)
4833+
(?v . ,(lambda () (transient-format-value obj)))
4834+
(?V . ,(lambda () (transient-format-value-only obj)))
4835+
(?a . ,(lambda () (transient-format-infix obj)))
4836+
(?i . ,(lambda () (transient-format-inapt obj)))))
4837+
(format-spec (transient--get-format obj)
47764838
`((?k . ,(transient-format-key obj))
47774839
(?d . ,(transient-format-description obj))
4778-
(?v . ,(transient-format-value obj))))))
4840+
(?v . ,(transient-format-value obj))
4841+
(?V . ,(transient-format-value-only obj))
4842+
(?a . ,(transient-format-infix obj))
4843+
(?i . ,(transient-format-inapt obj))))))
47794844

47804845
(cl-defmethod transient-format ((obj transient-suffix))
47814846
"Return a string generated using OBJ's `format'.
47824847
%k is formatted using `transient-format-key'.
47834848
%d is formatted using `transient-format-description'."
47844849
(static-if (>= emacs-major-version 29)
4785-
(format-spec (oref obj format)
4850+
(format-spec (transient--get-format obj)
47864851
`((?k . ,(lambda () (transient-format-key obj)))
4787-
(?d . ,(lambda () (transient-format-description obj)))))
4788-
(format-spec (oref obj format)
4852+
(?d . ,(lambda () (transient-format-description obj)))
4853+
(?i . ,(lambda () (transient-format-inapt obj)))))
4854+
(format-spec (transient--get-format obj)
47894855
`((?k . ,(transient-format-key obj))
4790-
(?d . ,(transient-format-description obj))))))
4856+
(?d . ,(transient-format-description obj))
4857+
(?i . ,(transient-format-inapt obj))))))
47914858

47924859
(cl-defgeneric transient-format-key (obj)
47934860
"Format OBJ's `key' for display and return the result.")
@@ -4980,6 +5047,34 @@ apply the face `transient-unreachable' to the complete string."
49805047
choices
49815048
(propertize "|" 'face 'transient-delimiter))))))
49825049

5050+
(cl-defmethod transient-format-value-only ((obj transient-infix))
5051+
(if-let ((value (oref obj value)))
5052+
(prin1-to-string value t)
5053+
"disabled"))
5054+
5055+
(cl-defmethod transient-format-value-only ((obj transient-switch))
5056+
(if (oref obj value) "enabled" "disabled"))
5057+
5058+
(cl-defmethod transient-format-value-only ((_obj transient-switches))
5059+
"TODO" "TODO")
5060+
5061+
(cl-defmethod transient-format-infix ((obj transient-infix))
5062+
(or (ignore-error (invalid-slot-name unbound-slot) (oref obj argument))
5063+
(ignore-error (invalid-slot-name unbound-slot) (oref obj variable))
5064+
"BUG: Neither argument nor variable is bound"))
5065+
5066+
(cl-defmethod transient-format-inapt ((obj transient-suffix))
5067+
(if (oref obj inapt) "inapt " ""))
5068+
5069+
(cl-defmethod transient--get-format ((obj transient-suffix))
5070+
(alist-get obj transient-overriding-format-alist
5071+
(pcase transient-use-accessible-format
5072+
('braille (oref obj braille))
5073+
('audio (oref obj audio))
5074+
(_ (oref obj format)))
5075+
nil
5076+
(lambda (type obj) (ignore-errors (cl-typep obj type)))))
5077+
49835078
(cl-defmethod transient--get-description ((obj transient-child))
49845079
(cond-let*
49855080
[[desc (oref obj description)]]

0 commit comments

Comments
 (0)