Repository: psychopy/psychopy

Issue:
Textbox not editable if multiple textbox components used 
Issue raised on forum https://discourse.psychopy.org/t/editable-textbox-not-responding/17786/5 if multiple textboxes are presented the second is not editable. See minimally working example here.
https://gitlab.pavlovia.org/lpxrh6/textboxbug2


Discussion:

Resolving patch:
diff --git a/psychopy/visual/basevisual.py b/psychopy/visual/basevisual.py
--- a/psychopy/visual/basevisual.py
+++ b/psychopy/visual/basevisual.py
@@ -165,15 +165,17 @@ def autoDraw(self, value):
             else:
                 toDraw.append(self)
                 toDrawDepths.append(self.depth)
+            # Add to editable list (if needed)
+            self.win.addEditable(self)
+            # Mark as started
             self.status = STARTED
         elif value == False:
             # remove from autodraw lists
             toDrawDepths.pop(toDraw.index(self))  # remove from depths
             toDraw.remove(self)  # remove from draw list
-            # Remove from editable list (if present)
-            for c in self.win._editableChildren:
-                if c() == self:
-                    self.win._editableChildren.remove(c)
+            # Remove from editable list (if needed)
+            self.win.removeEditable(self)
+            # Mark as stopped
             self.status = STOPPED
 
     def setAutoDraw(self, value, log=None):
diff --git a/psychopy/visual/textbox2/textbox2.py b/psychopy/visual/textbox2/textbox2.py
--- a/psychopy/visual/textbox2/textbox2.py
+++ b/psychopy/visual/textbox2/textbox2.py
@@ -215,8 +215,6 @@ def __init__(self, win, text, font,
         self.editable = editable
         self.caret = Caret(self, color=self.color, width=5)
         self._hasFocus = False
-        if editable:  # may yet gain focus if the first editable obj
-            self.win.addEditable(self)
 
         self.autoLog = autoLog
 
@@ -542,9 +540,6 @@ def draw(self):
     def reset(self):
         # Reset contents
         self.text = self.startText
-        # Make sure box is still editable (if needed)
-        if self.editable and self not in self.win._editableChildren:  # may yet gain focus if the first editable obj
-            self.win.addEditable(self)
 
 
     def contains(self, x, y=None, units=None, tight=False):
diff --git a/psychopy/visual/window.py b/psychopy/visual/window.py
--- a/psychopy/visual/window.py
+++ b/psychopy/visual/window.py
@@ -922,20 +922,14 @@ def currentEditable(self):
     @currentEditable.setter
     def currentEditable(self, editable):
         """Keeps the current editable stored as a weak ref"""
+        # Save previous editable
         lastEditable = self.currentEditable
+        # Remove focus from last editable
         if lastEditable is not None and lastEditable is not editable:
             lastEditable.hasFocus = False
-        # we want both the weakref and the actual object
-        if not isinstance(editable, weakref.ref):
-            thisRef = weakref.ref(editable)
-        else:
-            thisRef = editable
-            editable = thisRef()
-        # then get/set index in list
-        if thisRef not in self._editableChildren:
-            self._currentEditableIndex = self.addEditable(thisRef)
-        else:
-            self._currentEditableIndex = self._editableChildren.index(thisRef)
+        # Ensure that item is added to editables list
+        self._currentEditableIndex = self.addEditable(editable)
+        # Give focus to new current editable
         editable.hasFocus = True
 
     def addEditable(self, editable):
@@ -947,12 +941,32 @@ def addEditable(self, editable):
         :param editable:
         :return:
         """
+        # Ignore if object is not editable
+        if not hasattr(editable, "editable"):
+            return
+        if not editable.editable:
+            return
+        # If editable is already present, return its index
+        for ref in weakref.getweakrefs(editable):
+            if ref in self._editableChildren:
+                return self._editableChildren.index(ref)
+        # If editable is not already present, add it to the editables list
         self._editableChildren.append(weakref.ref(editable))
-        ii = len(self._editableChildren)-1  # the index of appended item
-        # if this is the first editable obj then make it the
+        # If this is the first editable obj then make it the current
         if len(self._editableChildren) == 1:
             self.currentEditable = editable
-        return ii
+        # Return the index of the appended item
+        return len(self._editableChildren) - 1
+
+
+    def removeEditable(self, editable):
+        # If editable is present, remove it from editables list
+        for ref in weakref.getweakrefs(editable):
+            if ref in self._editableChildren:
+                self._editableChildren.remove(ref)
+                # If editable was current, move on to next current
+                if self.currentEditable == self:
+                    self.nextEditable()
 
     def nextEditable(self, chars=''):
         """Moves focus of the cursor to the next editable window"""


Test patch: