ComboCtrl with ListCtrl selection issue

I originally posted my three questions on Stackoverflow, but this discussion forum seems to be more appropriate.

I am building a wx.ComboCtrl with a wx.ListCtrl attached. The reason for doing this is because I want to set the foreground colour of the choices (the colour shows the user the status of the item). I want these colours to show when the box is dropped down and when a user has made a selection.

The problem I run into is that on Linux (Ubuntu 20.04), after a selection was made, the background colour of the wx.ComboCtrl remains blue (and the foreground colour remains white), even if I move focus to another widget. It doesn’t matter which colour I set for the text to be displayed on the ComboCtrl, it remains white text with a blue background. See screenshot.

I can only get it to show me the default (gray) background with my selected foreground colour if I move the focus to another window and then back to my own window.

In Windows this doesn’t happen: after selecting an item, the background colour of the ComboCtrl is default (gray), however it does show a little dotted line around the selection. See screenshot.

NOTE: As I am new to this forum it appears I cannot add a second screenshot. Please refer to the Stackoverflow issue for the second screenshot.

Here is the modified demo code that I am using to reproduce the issue. The comments in the code are left overs from some of the things I tried.

#!/usr/bin/env python

import wx
import os

#----------------------------------------------------------------------

#----------------------------------------------------------------------
# This class is used to provide an interface between a ComboCtrl and the
# ListCtrl that is used as the popoup for the combo widget.

class ListCtrlComboPopup(wx.ComboPopup):

    def __init__(self):
        wx.ComboPopup.__init__(self)
        self.lc = None

    def AddItem(self, txt, _colour):
        self.lc.InsertItem(self.lc.GetItemCount(), txt)
        _entry = self.lc.GetItem(self.lc.GetItemCount() - 1)
        _entry.SetTextColour(_colour)
        #_entry.SetItemTextColour(_colour)
        self.lc.SetItem(_entry)

    def OnMotion(self, evt):
        item, flags = self.lc.HitTest(evt.GetPosition())
        if item >= 0:
            self.lc.Select(item)
            self.curitem = item

    def OnLeftDown(self, evt):
        self.value = self.curitem
        self.Dismiss()


    # The following methods are those that are overridable from the
    # ComboPopup base class.  Most of them are not required, but all
    # are shown here for demonstration purposes.

    # This is called immediately after construction finishes.  You can
    # use self.GetCombo if needed to get to the ComboCtrl instance.
    def Init(self):
        self.value = -1
        self.curitem = -1

    # Create the popup child control.  Return true for success.
    def Create(self, parent):
        self.lc = wx.ListCtrl(parent, style=wx.LC_SINGLE_SEL | wx.SIMPLE_BORDER | wx.LC_REPORT | wx.LC_NO_HEADER)
        self.lc.InsertColumn(0, '')
        self.lc.Bind(wx.EVT_MOTION, self.OnMotion)
        self.lc.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown)
        return True

    # Return the widget that is to be used for the popup
    def GetControl(self):
        return self.lc

    # Called just prior to displaying the popup, you can use it to
    # 'select' the current item.
    def SetStringValue(self, val):
        idx = self.lc.FindItem(-1, val)
        if idx != wx.NOT_FOUND:
            self.lc.Select(idx)

    # Return a string representation of the current item.
    def GetStringValue(self):
        if self.value >= 0:
            return self.lc.GetItemText(self.value)
        return ""

    # Called immediately after the popup is shown
    def OnPopup(self):
        wx.ComboPopup.OnPopup(self)

    # Called when popup is dismissed
    def OnDismiss(self):
        print (self.GetStringValue())
        wx.ComboPopup.OnDismiss(self)

    # This is called to custom paint in the combo control itself
    # (ie. not the popup).  Default implementation draws value as
    # string.
    def PaintComboControl(self, dc, rect):
        wx.ComboPopup.PaintComboControl(self, dc, rect)

    # Receives key events from the parent ComboCtrl.  Events not
    # handled should be skipped, as usual.
    def OnComboKeyEvent(self, event):
        wx.ComboPopup.OnComboKeyEvent(self, event)

    # Implement if you need to support special action when user
    # double-clicks on the parent wxComboCtrl.
    def OnComboDoubleClick(self):
        wx.ComboPopup.OnComboDoubleClick(self)

    # Return final size of popup. Called on every popup, just prior to OnPopup.
    # minWidth = preferred minimum width for window
    # prefHeight = preferred height. Only applies if > 0,
    # maxHeight = max height for window, as limited by screen size
    #   and should only be rounded down, if necessary.
    def GetAdjustedSize(self, minWidth, prefHeight, maxHeight):
        return wx.ComboPopup.GetAdjustedSize(self, minWidth, prefHeight, maxHeight)

    # Return true if you want delay the call to Create until the popup
    # is shown for the first time. It is more efficient, but note that
    # it is often more convenient to have the control created
    # immediately.
    # Default returns false.
    def LazyCreate(self):
        return wx.ComboPopup.LazyCreate(self)

#----------------------------------------------------------------------


class MyTestPanel(wx.Panel):
    def __init__(self, parent, log):
        self.log = log
        wx.Panel.__init__(self, parent, -1)
        
        txt = wx.TextCtrl(self, wx.ID_ANY, pos=(100,100))

        comboCtrl = wx.ComboCtrl(self, wx.ID_ANY, "Third item", (10,10), size=(200,-1), style=wx.CB_READONLY)
        popupCtrl = ListCtrlComboPopup()

        # It is important to call SetPopupControl() as soon as possible
        comboCtrl.SetPopupControl(popupCtrl)

        # Populate using wx.ListView methods
        popupCtrl.AddItem("First Item", [255, 127, 0])
        popupCtrl.AddItem("Second Item", [192, 127, 45])
        popupCtrl.AddItem("Third Item", [25, 223, 172])
        #popupCtrl.GetAdjustedSize(100, 35, 100)
        #comboCtrl.SetTextColour(_colour)
        comboCtrl.SetForegroundColour(wx.Colour(235, 55, 55))


#----------------------------------------------------------------------

def runTest(frame, nb, log):
    win = MyTestPanel(nb, log)
    return win

#----------------------------------------------------------------------



overview = """<html><body>
<h2><center>wx.combo.ComboCtrl</center></h2>

A combo control is a generic combobox that allows a totally custom
popup. In addition it has other customization features. For instance,
position and size of the dropdown button can be changed.

</body></html>
"""


if __name__ == '__main__':
    import sys,os
    import run
    run.main(['', os.path.basename(sys.argv[0])] + sys.argv[1:])

Question 1:

How can I make it so that once an item has been selected the appropriate text colour (the one I programmatically set) and default (gray) background colour is shown.

Question 2:

When dropping down the ComboCtrl, it is showing the ListCtrl, which has a single column only. You can see that the “Second item” on the list is not displayed entirely because the column is too narrow. How can I make it so that the column is always the same width as the widget itself, even when the ComboCtrl resizes (as a result of resizing the parent window)?

Question 3:

Not overly important, but while we are on the subject: is there a way to get rid of the little dotted box that is shown around the selected item when running this in Windows?

In advance, thank you very much for your thoughts and ideas on this.

Marc.

I just ran my test code on an older machine running wx version 4.0.1 and there the problem doesn’t happen! Perhaps I should downgrade.