4. Selectable elements

Selectable fields are those where the user does not have the ability to type, but instead must choose from a series of options. Although in Emacs, due to its nature as a text editor, some of them sit on a fine line between being selectable or not.

If you have built forms before, some of them will be familiar to you: select, radio, checkbox, toggle, color... But in Emacs we also have others that are not so common, such as menu-choice or editable-list.

menu-choice (select or dropdown)

The widget is similar to HTML's select, but with the difference that you cannot type in certain fields. That is, the user must choose an option from a series of options, but we can optionally add a free text field so the user can type whatever they want.

(widget-create 'menu-choice
           :tag "Which smartphone do you have?"
           :help-string "Select your smartphone"
           :value "Android"
           :notify (lambda (widget &rest ignore)
             (message "Selected: %s" (widget-value widget)))
           '(choice-item :tag "Android" :value "Android")
           '(item :tag "iPhone" :value "iPhone")
           '(item :tag "Blackberry" :value "Blackberry")
           '(editable-field :value "Other" :format "Other: %v"))

Login example

Here we can see how the options have been expanded.

Login example

And this time the editable field "Other" has been selected.

Login example

The attributes are similar to other widgets, such as editable-field, except that here we must add other widgets inside the menu-choice.

  • choice-item: Item selected by default. You must add the :value attribute to menu-choice with the same value as the choice-item for it to work. Check the previous example. tag is the text shown in the dropdown and value is the value assigned to the menu-choice when it is selected.
  • item: Item that can be selected. tag and value behave the same as choice-item.
  • editable-field: Editable text field. Since it behaves like an editable-field, you can add the :format attribute to it so a default text is shown.

item (text)

The widget was born and exists to create a selectable element within a group of buttons such as menu-choice or radio-button-choice. But by its nature, we can also use it independently to show plain editable text.

(widget-create 'item :value "Example text")

It is important to always store it in a variable so you can modify it later.

(setq my-item (widget-create 'item :value "Example text"))

Below you can see an example where a button increments a number that is shown dynamically in the item.

(setq number 0)
(widget-insert "\n")
(setq my-item (widget-create 'item :value "0"))
(widget-insert "\n")
(widget-create 'push-button
               :notify (lambda (&rest ignore)
             (setq number (1+ number))
                         (widget-value-set my-item number))
               :help-echo "Click me to increment the number"
               "Increment")

In the documentation you will not find any reference to using the widget as a read-only space or an area where we can render strings. However, we will use it for that purpose since there is no other widget that lets us do so.

radio-button-choice (radio button)

Radio buttons are those buttons that are grouped and only one of them can be selected. In Emacs, we use the radio-button-choice widget to create the group. Each button is created with the item widget.

Radio buttons example

(widget-insert "What do you want to drink?:\n\n")
(widget-create 'radio-button-choice
    :value "Coffee"
    :notify (lambda (widget &rest ignore)
                (message "You selected %s" (widget-value widget)))
    '(item "Coffee")
    '(item "Tea")
    '(item "Water"))

In case we do not want any button to be selected by default, we can omit the :value attribute.

checkbox

Checkboxes are similar to radio buttons, but in this case several can be selected at once. For that, we use the checkbox widget.

Checkbox example

(widget-insert "\nFavorite hobbies:\n\n")
(widget-create 'checkbox
           :help-echo "Check if you like reading"
           :notify (lambda (&rest ignore) (message "You selected Reading")))
(widget-insert " Reading\n")
(widget-create 'checkbox
           :help-echo "Check if you like watching movies"
           :notify (lambda (&rest ignore) (message "You selected Watching series")))
(widget-insert " Watching series\n")
(widget-create 'checkbox
           :help-echo "Check if you like playing video games"
           :notify (lambda (&rest ignore) (message "You selected Playing video games")))
(widget-insert " Playing video games\n")

To use :format, we must include %[%v%] in the text.

(widget-create 'checkbox
                    :format "%[%v%] Reading"
                    :notify (lambda (&rest ignore)
                             (message "You selected Reading")))

In case we want one of the checkboxes to be selected by default, we must include t as the last argument of checkbox.

(widget-create 'checkbox
           :help-echo "Check if you like reading"
           :notify (lambda (&rest ignore) (message "You selected Reading"))
           t)

toggle (switch)

It is similar to a checkbox in functionality, but visually it is a 2-position switch: on or off. That is, a button that can be in the enabled or disabled state.

Toggle example

(widget-insert "Do you accept the terms? ")
(widget-create 'toggle
    :help-echo "Do you accept the terms?"
    :on "👍"
    :on-glyph '
    :off "👎"
    :off-glyph '
    :value t
    :notify (lambda (widget &rest ignore)
             (message "You selected %s"
                  (if (widget-value widget)
                      "accept"
                    "reject"))))

You must define:

  • :on: Text shown when the toggle is enabled.
  • :off: Text shown when the toggle is disabled.
  • :value: Initial value of the toggle. t for enabled and nil for disabled.
  • :notify: Function that runs when the toggle state changes.

Optionally you can define:

  • :on-glyph: Glyph icon, if they are enabled, shown when the toggle is enabled.
  • :off-glyph: Glyph icon, if they are enabled, shown when the toggle is disabled.
  • :help-echo: Text shown when the mouse hovers over it.

editable-list (editable list)

Editable lists are a most peculiar widget. They let you create a list of elements that can be edited. That is, we can add, remove and modify elements of the list. Similar to a TODO.

Editable list example

(widget-create 'editable-list
               :entry-format "%i %d %v"
           :notify (lambda (widget &rest ignore) (message "%s" (widget-value widget)))
           :value '("elisp" "emacs")
           '(editable-field :value ""))

If we want to include arguments in the buttons, such as a help text, we must use the :insert-button-args, :delete-button-args and :append-button-args attributes.

(widget-create 'editable-list
               :entry-format "%i %d %v"
           :insert-button-args '(:help-echo "Insert a new element" :tag "Insert")
           :delete-button-args '(:help-echo "Delete the element" :tag "Delete")
           :append-button-args '(:help-echo "Insert an element at the end" :tag "Insert new line")
           :value '("elisp" "emacs")
               '(editable-field :value ""))
  • :insert-button-args: Arguments passed to the insert button.
  • :delete-button-args: Arguments passed to the delete button.
  • :append-button-args: Arguments passed to the insert button located at the end of the list.

You will not be able to use :notify to get the changes of the list. Instead, you must use :notify-insert and :notify-delete.

There are other attributes we can use such as :buttons, which represents the widgets of the insert and delete buttons. If you want to customize them, you can override them.

As a practical example of the widget, in the following example I have created a form to create hashtags.

(widget-insert "\nInclude the tags you want to create: \n\n")
(setq widget-preview (widget-create 'item :value ""))
(widget-insert "\n")
(widget-create 'editable-list
               :entry-format "%i %d %v"
               :notify (lambda (widget &rest ignore)
                         (widget-value-set widget-preview
                  ;; Convert the list to a string, adding a separating space. Similar to join in other languages
                  (mapconcat 'identity
                         ;; Prepend a "#" to each element.
                         (mapcar (lambda (item) (concat "#" item))
                             ;; Ignore empty fields
                             (seq-filter (lambda (item) (and (stringp item) (not (string= item ""))))
                                 (widget-value widget))) " ")))
               :value '()
           '(editable-field :value ""))

color

It is a widget that is not documented, but it is present in the source code of the package. It lets us select a color from a color palette.

Color example


(widget-create 'color
    :value "#ff0000"
    :tag "Color"
    :format "%: %v (%{Preview%})\n"
    :notify (lambda (widget &rest ignore)
             (message "You selected %s" (widget-value widget))))

The :format attribute is more complex than in the rest of the widgets. It lets us define the tag text (%t), the value (%v) and the preview text (%{Preview%}).

If you want to rename the selection button, you can override the widget-color-value-create function.

(defun widget-color-value-create (widget)
  (widget-field-value-create widget)
  (widget-insert " ")
  (widget-create-child-and-convert
   widget 'push-button
   :tag " Select " :action 'widget-color--choose-action)
  (widget-insert " "))

group

It is not a visual widget like all the previous ones; its role is functional. It gives us the ability to group widgets to organize their behaviors. You can think of it as a folder.

(setq form-contact (widget-create 'group :tag "form-contact"))
(setq input-email (widget-create
              'editable-field
              :size 20
              :format "Email: %v"
              :parent form-contact)) ; New
(setq input-password (widget-create
              'editable-field
              :size 20
              :format "Password: %v"
              :secret ?*
              :parent form-contact)) ; New

I cannot give you more information about this widget, since I have not found documentation about it.

Files

If we read the source code of the widget package (emacs/lisp/wid-edit.el), we can find other selectors that are not documented. Among them, ones to select files, images and directories.

I have not included them in the course because I have not managed to get them to work. If you manage to get them working on your side, do not hesitate to share it with me.

Activity 1

We are going to build a form that helps us calculate amounts with and without taxes.

The form must have the following fields:

  • Text field to enter the amount
  • Dropdown
  • Button to calculate
  • item to show the result

The dropdown, or menu-choice, will contain the following options:

  • Without taxes
  • With taxes

When the calculate button is pressed, the value of the text field will be obtained and increased by 21% if the "With taxes" option has been selected. Otherwise, "Without taxes", 21% will be subtracted. The result of the operation will be shown in the item.

Activity 2

Create a form with the following structure:

  • Hide completed tasks? toggle
  • item with the number of completed tasks

When the toggle is pressed, the number of completed tasks will be shown in the item. The information will be obtained from the following endpoint: https://dummyjson.com/todos

Activity 3

Use an editable list to add numbers. All the fields of the list must be numeric. After the list, the result must be shown; you can rely on an item for that.

This work is under a Attribution-NonCommercial-NoDerivatives 4.0 International license.

Desafíos de programación atemporales y multiparadigmáticos

Desafíos de programación atemporales y multiparadigmáticos

Te encuentras ante un librillo de actividades, divididas en 2 niveles de dificultad. Te enfrentarás a los casos más comunes que te puedes encontrar en pruebas técnicas o aprender conceptos elementales de programación.

Buy the book

Will you buy me a coffee?

This is how I keep writing without ads or paywalls.

Comments

There are no comments yet.