Terminal.Gui version
2.4.18-develop.46 (b5c08b3f91e0c066d5d78df71944f3d58009b166)
Observed on Windows. Other drivers have not been tested.
Description
A read-only DropDownList can leave the dropped list's text behind after an item is accepted with Enter when the application handles Accepting and moves focus. The selection and focus change occur correctly, and the popover is no longer active, but the cells it previously covered are not repainted.
Selecting the same item with the mouse does not leave stale content.
Minimal pattern
DropDownList dropDown = new ()
{
ReadOnly = true,
Source = new ListWrapper<string> (
new ObservableCollection<string> (["Labor", "Fuel/Energy", "Equipment"]))
};
ListView nextView = new ();
dropDown.Accepting += (_, e) =>
{
// Prevent Command.Accept from bubbling to and accepting the containing Dialog.
e.Handled = true;
nextView.SetFocus ();
};
Steps to reproduce
- Place the controls above in a
Dialog and run it.
- Open the dropdown using the keyboard.
- Move to another item and press Enter.
- Observe that focus moves and the dropdown closes, but some option text remains drawn over the controls below it.
- Repeat using the mouse to select an item; the covered area is repainted correctly.
Expected behavior
Whenever the popover is removed after focus changes, its former screen region should be repainted regardless of whether selection was made with the keyboard or mouse.
Suspected cause
DropDownList.OnHasFocusChanging calls App?.Popovers?.DeRegister (_listPopover) when losing focus:
|
protected override bool OnHasFocusChanging (bool currentHasFocus, bool newHasFocus, View? currentFocused, View? newFocused) |
|
{ |
|
if (base.OnHasFocusChanging (currentHasFocus, newHasFocus, currentFocused, newFocused)) |
|
{ |
|
return true; |
|
} |
|
|
|
if (newHasFocus) |
|
{ |
|
App?.Popovers?.Register (_listPopover); |
|
|
|
return false; |
|
} |
|
|
|
App?.Popovers?.DeRegister (_listPopover); |
|
|
|
return false; |
ApplicationPopover.DeRegister removes the active popover without hiding it or invalidating the underlying runnable view:
|
public bool DeRegister (IPopoverView? popover) |
|
{ |
|
if (popover is null || !IsRegistered (popover)) |
|
{ |
|
return false; |
|
} |
|
|
|
if (GetActivePopover () == popover) |
|
{ |
|
_activePopover = null; |
|
} |
|
|
|
_popovers.Remove (popover); |
|
|
|
PopoverDeRegistered?.Invoke (this, new EventArgs<IPopoverView> (popover)); |
|
|
|
return true; |
In contrast, ApplicationPopover.Hide calls SetNeedsDraw() on the top runnable view:
|
public void Hide (IPopoverView? popover = null) |
|
{ |
|
popover ??= GetActivePopover (); |
|
|
|
if (_activePopover != popover) |
|
{ |
|
return; |
|
} |
|
|
|
// If there's an existing popover, hide it. |
|
_activePopover = null; |
|
popover?.Visible = false; |
|
|
|
// Need View cast for TopRunnableView access |
|
if (popover is View popoverView) |
|
{ |
|
popoverView.App?.TopRunnableView?.SetNeedsDraw (); |
|
} |
The mouse selection path hides the popover through OnActivated, while this handled keyboard path loses focus and reaches the deregistration path.
Workaround
Explicitly invalidating the containing dialog after moving focus clears the stale cells:
dropDown.Accepting += (_, e) =>
{
e.Handled = true;
nextView.SetFocus ();
dialog.SetNeedsDraw ();
};
A possible library-level fix may be to hide the active popover before deregistering it on focus loss, or otherwise invalidate the region it covered.
Terminal.Gui version
2.4.18-develop.46(b5c08b3f91e0c066d5d78df71944f3d58009b166)Observed on Windows. Other drivers have not been tested.
Description
A read-only
DropDownListcan leave the dropped list's text behind after an item is accepted with Enter when the application handlesAcceptingand moves focus. The selection and focus change occur correctly, and the popover is no longer active, but the cells it previously covered are not repainted.Selecting the same item with the mouse does not leave stale content.
Minimal pattern
Steps to reproduce
Dialogand run it.Expected behavior
Whenever the popover is removed after focus changes, its former screen region should be repainted regardless of whether selection was made with the keyboard or mouse.
Suspected cause
DropDownList.OnHasFocusChangingcallsApp?.Popovers?.DeRegister (_listPopover)when losing focus:Terminal.Gui/Terminal.Gui/Views/DropDownList.cs
Lines 223 to 239 in b5c08b3
ApplicationPopover.DeRegisterremoves the active popover without hiding it or invalidating the underlying runnable view:Terminal.Gui/Terminal.Gui/App/ApplicationPopover.cs
Lines 63 to 79 in b5c08b3
In contrast,
ApplicationPopover.HidecallsSetNeedsDraw()on the top runnable view:Terminal.Gui/Terminal.Gui/App/ApplicationPopover.cs
Lines 97 to 114 in b5c08b3
The mouse selection path hides the popover through
OnActivated, while this handled keyboard path loses focus and reaches the deregistration path.Workaround
Explicitly invalidating the containing dialog after moving focus clears the stale cells:
A possible library-level fix may be to hide the active popover before deregistering it on focus loss, or otherwise invalidate the region it covered.