Subject: bug#69941: 30.0.50; Faulty fontification of radio button widgets

From: Stephen Berman <[email-masked]>
Date: Sun, 09 Feb 2025 11:31:10 +0100

On Sat, 8 Feb 2025 20:50:20 -0300 Mauro Aranda <[email-masked]> wrote:

> Stephen Berman <[email-masked]> writes:
>
>> With the attached file I can reproduce the problem.  Save the file and
>> then invoke Emacs from master (i.e., with your patch) as follows:
>>
>> emacs -Q -l srb-widget-test.el -f srb-widget-test
>>
>> In the displayed buffer "*Widget Test*" the three radio buttons "One",
>> "Two" and "Confirm" are active, which the default face visually
>> confirms.  (Their labels are fontified with face `widget-unselected'.)
>> Now do the following steps:
>>
>> 1. Hit TAB to move point to radio button "One".
>> 2. Type RET to activate the "Reset" button and move point to it.  Also,
>>    the radio buttons are now deactivated, which is indicated by their
>>    now being fontified with face `widget-inactive' (as are their
>>    labels).
>> 3. Type RET on the "Reset" button to reactivate the radio buttons.
>> 4. Hit TAB twice to move point to radio button "Two". Hitting RET
>>    results in the same state as in step 2.
>> 5. Repeat step 3 and then hit TAB three times (or S-TAB once) to move
>>    point to radio button "Confirm".  This pops up two indented radio
>>    buttons "Right" and "Wrong" under "Confirm", and point is now on
>>    "Right".  Note that the radio buttons "One", "Two" and "Confirm" are
>>    deactivated, while "Right" and "Wrong" are active.
>> 6. Type RET on radio button "Right".  This deletes radio buttons "Right"
>>    and "Wrong" and moves point to "Reset".  Radio buttons "One", "Two"
>>    and "Confirm" remain deactivated (confirmed by tabbing to each and
>>    typing RET, which dings and displays the message "Attempt to perform
>>    action on inactive widget"); however:
>> => Now radio button "One" is misfontified with the default face instead
>>    of face `widget-inactive'.
>>
>> If you repeat step 5 and then tab to radio button "Wrong" and hit RET,
>> the resulting state is the same as in step 6, i.e., radio button "One"
>> (and only it) is again misfontified.
>
> OK, it's a similar problem, but this time with the :inactive overlay,
> which has FRONT-ADVANCE t.
>
>> ;;; -*- lexical-binding: t; -*-
>>
>> (defvar srb--radio-widget)
>> (defvar srb--confirm-widget)
>> (defvar srb--reset-button)
>>
>> (defun srb--result (widget)
>>   (pcase (widget-value widget)
>>       ("One" (ignore))
>>       ("Two" (ignore))
>>       ("Confirm"
>>        (goto-char (widget-get srb--radio-widget :to))
>>        (setq srb--confirm-widget
>>          (widget-create
>>           'radio-button-choice
>>           :indent 4
>>           :notify (lambda (child &rest _)
>>             (srb--confirm child))
>>           '(item "Right")
>>           '(item "Wrong")))))
>>   (when srb--reset-button
>>     (widget-apply srb--reset-button :activate))
>>   (if (equal (widget-value srb--radio-widget) "Confirm")
>>       (widget-backward 2)
>>     (goto-char (widget-get srb--reset-button :from)))
>>   (widget-value-set srb--radio-widget "")
>>   (widget-apply srb--radio-widget :deactivate))
>
> Here, before the :deactivate call the widget is already inactive, and
> because of that the :deactivate call (which calls
> widget-specify-inactive) does nothing at all, since it detects there's
> already an :inactive overlay.  That :inactive overlay doesn't include
> the 1st button anymore, since that one gets inserted right at the
> overlay start.
>
> So, modifying an inactive widget right at the start or at the end of the
> overlay can produce this effect.
>
> I don't think we can solve it easily like my patch did with the :from
> and :to markers, though.  But here's an idea:
>
> When a widget is already inactive (i.e., it has an :inactive property),
> instead of doing nothing we make sure to move the overlay so that it
> includes again all the widget, from the :from marker to the :to marker.
>
> If we do that, we should document that if some code modifies an
> :inactive widget, it has to call again :deactivate.
>
> I attach a patch with that idea, together with documentation changes and
> a new test.

Thanks, I confirm this fixes the remaining misfontification.  However,
if I switch the order of the last two lines above -- i.e, first
deactivate the radio widget and then set its value to the empty
string -- then I see the same misfontification of radio button "One"
that still occurs in emacs-30 (i.e. without both this patch and your
previous one).  Is this expected, and if so, can you explain why?

Steve Berman