Repository: spyder-ide/spyder

Issue:
Tab in-between find and replace text boxes
## Description of your problem
The tab key doesn't move the cursor from the find to replace text boxes as expected.

**What steps will reproduce the problem?**

1. Hit control-F to open find text box.  Type in some text.
2. Hit control-R to open replace text box.  Type in some other text.
3. Press shift-tab to move from replace box back into text box.
4. Press tab to move from text box back forward into replace box. (This step doesn't do anything)

**What is the expected output? What do you see instead?**
The tab to move from the text to replace box doesn't work.  You stay within the text box.  Pressing shift-tab to move backwards works as expected.

## Versions and main components

* Spyder Version: 3.1.0
* Python Version: 3.5.2
* Operating system: Win 7

I remember this working fine a while ago, but I've since noticed this on some earlier versions of Spyder.  It's finally started to bother me enough to open the issue.  Thanks for any help!

## Dependencies

jedi >=0.8.1     :  0.9.0 (OK)
matplotlib >=1.0 :  1.5.3 (OK)
nbconvert >=4.0  :  4.2.0 (OK)
numpy >=1.7      :  1.11.2 (OK)
pandas >=0.13.1  :  0.19.1 (OK)
pep8 >=0.6       :  1.7.0 (OK)
pyflakes >=0.6.0 :  1.3.0 (OK)
pygments >=2.0   :  2.1.3 (OK)
pylint >=0.25    :  1.6.4 (OK)
qtconsole >=4.2.0:  4.2.1 (OK)
rope >=0.9.4     :  0.9.4-1 (OK)
sphinx >=0.6.6   :  1.4.8 (OK)
sympy >=0.7.3    :  1.0 (OK)


Tab in-between find and replace text boxes
## Description of your problem
The tab key doesn't move the cursor from the find to replace text boxes as expected.

**What steps will reproduce the problem?**

1. Hit control-F to open find text box.  Type in some text.
2. Hit control-R to open replace text box.  Type in some other text.
3. Press shift-tab to move from replace box back into text box.
4. Press tab to move from text box back forward into replace box. (This step doesn't do anything)

**What is the expected output? What do you see instead?**
The tab to move from the text to replace box doesn't work.  You stay within the text box.  Pressing shift-tab to move backwards works as expected.

## Versions and main components

* Spyder Version: 3.1.0
* Python Version: 3.5.2
* Operating system: Win 7

I remember this working fine a while ago, but I've since noticed this on some earlier versions of Spyder.  It's finally started to bother me enough to open the issue.  Thanks for any help!

## Dependencies

jedi >=0.8.1     :  0.9.0 (OK)
matplotlib >=1.0 :  1.5.3 (OK)
nbconvert >=4.0  :  4.2.0 (OK)
numpy >=1.7      :  1.11.2 (OK)
pandas >=0.13.1  :  0.19.1 (OK)
pep8 >=0.6       :  1.7.0 (OK)
pyflakes >=0.6.0 :  1.3.0 (OK)
pygments >=2.0   :  2.1.3 (OK)
pylint >=0.25    :  1.6.4 (OK)
qtconsole >=4.2.0:  4.2.1 (OK)
rope >=0.9.4     :  0.9.4-1 (OK)
sphinx >=0.6.6   :  1.4.8 (OK)
sympy >=0.7.3    :  1.0 (OK)




Discussion:
Thanks for reporting. We'll improve this behavior in a future version of Spyder.

Would be nice to have some `Alt+letter` shortcuts to the actions. Replace all, for example, with `Alt+a`.
Hey, is this still being looked at by any chance? I was going to file an issue report myself on this if this one wasn't already open, as it is a pretty major problem on a regular basis. 

I'm not @ccordoba12 or one of the contributors (at least not yet, anyway...hopefully someday) putting in all the amazing effort here so its probably not my place to say, but if it was, IMO, I'd consider this a type:bug to be dealt with accordingly, as it breaks core UI functionality and basic user expectations—Tab's otherwise consistent behavior across the interface—and the real smoking gun is that Shift-Tab works to go back a field, as other commenters mention, but not Tab to go forward, which doesn't seem like it could possibly be intended.
@rlaverde, please work on this one.
@CAM-Gerlach, would you like to take a look at this one? I think it's an easy fix ;-)
Sure thing; was just thinking of taking a look actually.
Thanks for reporting. We'll improve this behavior in a future version of Spyder.

Would be nice to have some `Alt+letter` shortcuts to the actions. Replace all, for example, with `Alt+a`.
Hey, is this still being looked at by any chance? I was going to file an issue report myself on this if this one wasn't already open, as it is a pretty major problem on a regular basis. 

I'm not @ccordoba12 or one of the contributors (at least not yet, anyway...hopefully someday) putting in all the amazing effort here so its probably not my place to say, but if it was, IMO, I'd consider this a type:bug to be dealt with accordingly, as it breaks core UI functionality and basic user expectations—Tab's otherwise consistent behavior across the interface—and the real smoking gun is that Shift-Tab works to go back a field, as other commenters mention, but not Tab to go forward, which doesn't seem like it could possibly be intended.
@rlaverde, please work on this one.
@CAM-Gerlach, would you like to take a look at this one? I think it's an easy fix ;-)
Sure thing; was just thinking of taking a look actually.

Resolving patch:
diff --git a/spyder/widgets/findreplace.py b/spyder/widgets/findreplace.py
--- a/spyder/widgets/findreplace.py
+++ b/spyder/widgets/findreplace.py
@@ -176,14 +176,14 @@ def __init__(self, parent, enable_replace=False):
         self.highlight_timer.timeout.connect(self.highlight_matches)
         self.search_text.installEventFilter(self)
 
-
     def eventFilter(self, widget, event):
         """Event filter for search_text widget.
 
         Emits signals when presing Enter and Shift+Enter.
         This signals are used for search forward and backward.
+        Also, a crude hack to get tab working in the Find/Replace boxes.
         """
-        if (event.type() == QEvent.KeyPress):
+        if event.type() == QEvent.KeyPress:
             key = event.key()
             shift = event.modifiers() & Qt.ShiftModifier
 
@@ -193,8 +193,10 @@ def eventFilter(self, widget, event):
                 else:
                     self.return_pressed.emit()
 
-        return super(FindReplace, self).eventFilter(widget, event)
+            if key == Qt.Key_Tab:
+                self.focusNextChild()
 
+        return super(FindReplace, self).eventFilter(widget, event)
 
     def create_shortcuts(self, parent):
         """Create shortcuts for this widget"""


Test patch:
diff --git a/spyder/widgets/tests/test_editor.py b/spyder/widgets/tests/test_editor.py
--- a/spyder/widgets/tests/test_editor.py
+++ b/spyder/widgets/tests/test_editor.py
@@ -9,13 +9,15 @@
 """
 
 # Standard library imports
+from sys import platform
 try:
-    from unittest.mock import Mock
+    from unittest.mock import Mock, MagicMock
 except ImportError:
-    from mock import Mock # Python 2
+    from mock import Mock, MagicMock  # Python 2
 
 # Third party imports
 import pytest
+from flaky import flaky
 from qtpy.QtCore import Qt
 from qtpy.QtGui import QTextCursor
 
@@ -468,5 +470,42 @@ def test_editor_splitter_init(editor_splitter_bot):
     assert es.widget(0) == es.editorstack
 
 
+def test_tab_keypress_properly_caught_find_replace(editor_find_replace_bot):
+    """Check that tab works in find/replace dialog. Regression test #3674.
+    Mock test—more isolated but less flimsy."""
+    editor_stack, editor, finder, qtbot = editor_find_replace_bot
+    text = '  \nspam \nspam \nspam '
+    editor.set_text(text)
+    finder.show()
+    finder.show_replace()
+
+    finder.focusNextChild = MagicMock(name="focusNextChild")
+    qtbot.keyPress(finder.search_text, Qt.Key_Tab)
+    finder.focusNextChild.assert_called_once_with()
+
+
+@flaky(max_runs=3)
+@pytest.mark.skipif(platform.startswith('linux'),
+                    reason="This test fails on Linux, for unknown reasons.")
+def test_tab_moves_focus_from_search_to_replace(editor_find_replace_bot):
+    """Check that tab works in find/replace dialog. Regression test #3674.
+    "Real world" test—more comprehensive but potentially less robust."""
+    editor_stack, editor, finder, qtbot = editor_find_replace_bot
+    text = '  \nspam \nspam \nspam '
+    editor.set_text(text)
+    finder.show()
+    finder.show_replace()
+
+    qtbot.wait(100)
+    finder.search_text.setFocus()
+    qtbot.wait(100)
+    assert finder.search_text.hasFocus()
+    assert not finder.replace_text.hasFocus()
+    qtbot.keyPress(finder.search_text, Qt.Key_Tab)
+    qtbot.wait(100)
+    assert not finder.search_text.hasFocus()
+    assert finder.replace_text.hasFocus()
+
+
 if __name__ == "__main__":
     pytest.main()