From 65e56688886c66af2db369c398ec6b6beacd5c98 Mon Sep 17 00:00:00 2001
From: Niloth-p <20315308+Niloth-p@users.noreply.github.com>
Date: Tue, 14 May 2024 17:52:08 +0530
Subject: [PATCH 1/2] keys/ui_tools: Break down the generalized ENTER command.
Breaks down the ENTER command into more specific commands.
- ACTIVATE_BUTTON
- EXECUTE_SEARCH
- NEW_LINE
Tests and docs updated.
---
docs/hotkeys.md | 4 +++-
tests/ui_tools/test_boxes.py | 2 +-
tests/ui_tools/test_buttons.py | 4 ++--
tests/ui_tools/test_messages.py | 4 +++-
tests/ui_tools/test_popups.py | 8 ++++----
zulipterminal/config/keys.py | 23 ++++++++++++++++++-----
zulipterminal/ui_tools/boxes.py | 8 ++++----
zulipterminal/ui_tools/buttons.py | 4 ++--
zulipterminal/ui_tools/messages.py | 2 +-
zulipterminal/ui_tools/views.py | 6 +++++-
10 files changed, 43 insertions(+), 22 deletions(-)
diff --git a/docs/hotkeys.md b/docs/hotkeys.md
index 1c9e4bf528..f91e8ab029 100644
--- a/docs/hotkeys.md
+++ b/docs/hotkeys.md
@@ -24,13 +24,13 @@
|Scroll up|PgUp / K|
|Scroll down|PgDn / J|
|Go to bottom / Last message|End / G|
+|Trigger the selected entry|Enter|
|Narrow to all messages|a / Esc|
|Narrow to all direct messages|P|
|Narrow to all starred messages|f|
|Narrow to messages in which you're mentioned|#|
|Next unread topic|n|
|Next unread direct message|p|
-|Perform current action|Enter|
## Searching
|Command|Key Combination|
@@ -40,6 +40,7 @@
|Search streams|q|
|Search topics in a stream|q|
|Search emojis from emoji picker|p|
+|Submit search and browse results|Enter|
## Message actions
|Command|Key Combination|
@@ -83,6 +84,7 @@
|Autocomplete @mentions, #stream_names, :emoji: and topics|Ctrl + f|
|Cycle through autocomplete suggestions in reverse|Ctrl + r|
|Narrow to compose box message recipient|Meta + .|
+|Insert new line|Enter|
## Editor: Navigation
|Command|Key Combination|
diff --git a/tests/ui_tools/test_boxes.py b/tests/ui_tools/test_boxes.py
index 8fc050391f..b6c763c0cb 100644
--- a/tests/ui_tools/test_boxes.py
+++ b/tests/ui_tools/test_boxes.py
@@ -1819,7 +1819,7 @@ def test_valid_char(
@pytest.mark.parametrize(
"log, expect_body_focus_set", [([], False), (["SOMETHING"], True)]
)
- @pytest.mark.parametrize("enter_key", keys_for_command("ENTER"))
+ @pytest.mark.parametrize("enter_key", keys_for_command("EXECUTE_SEARCH"))
def test_keypress_ENTER(
self,
panel_search_box: PanelSearchBox,
diff --git a/tests/ui_tools/test_buttons.py b/tests/ui_tools/test_buttons.py
index 3705bbaefe..adc2faa127 100644
--- a/tests/ui_tools/test_buttons.py
+++ b/tests/ui_tools/test_buttons.py
@@ -262,7 +262,7 @@ def test_keypress_TOGGLE_MUTE_STREAM(
class TestUserButton:
# FIXME Place this in a general test of a derived class?
- @pytest.mark.parametrize("enter_key", keys_for_command("ENTER"))
+ @pytest.mark.parametrize("enter_key", keys_for_command("ACTIVATE_BUTTON"))
def test_activate_called_once_on_keypress(
self,
mocker: MockerFixture,
@@ -358,7 +358,7 @@ def test_init_calls_top_button(
assert emoji_button.emoji_name == emoji_unit[0]
assert emoji_button.reaction_count == count
- @pytest.mark.parametrize("key", keys_for_command("ENTER"))
+ @pytest.mark.parametrize("key", keys_for_command("ACTIVATE_BUTTON"))
@pytest.mark.parametrize(
"emoji, has_user_reacted, is_selected_final, expected_reaction_count",
[
diff --git a/tests/ui_tools/test_messages.py b/tests/ui_tools/test_messages.py
index 8deb4ebf3c..1635468edb 100644
--- a/tests/ui_tools/test_messages.py
+++ b/tests/ui_tools/test_messages.py
@@ -1961,7 +1961,9 @@ def test_footlinks_limit(self, maximum_footlinks, expected_instance):
assert isinstance(footlinks, expected_instance)
@pytest.mark.parametrize(
- "key", keys_for_command("ENTER"), ids=lambda param: f"left_click-key:{param}"
+ "key",
+ keys_for_command("ACTIVATE_BUTTON"),
+ ids=lambda param: f"left_click-key:{param}",
)
def test_mouse_event_left_click(
self, mocker, msg_box, key, widget_size, compose_box_is_open
diff --git a/tests/ui_tools/test_popups.py b/tests/ui_tools/test_popups.py
index cb26395896..c4f55a1bb3 100644
--- a/tests/ui_tools/test_popups.py
+++ b/tests/ui_tools/test_popups.py
@@ -830,7 +830,7 @@ def test_init(self, edit_mode_view: EditModeView) -> None:
(2, "change_all"),
],
)
- @pytest.mark.parametrize("key", keys_for_command("ENTER"))
+ @pytest.mark.parametrize("key", keys_for_command("ACTIVATE_BUTTON"))
def test_select_edit_mode(
self,
edit_mode_view: EditModeView,
@@ -1523,7 +1523,7 @@ def test_keypress_exit_popup(
self.stream_info_view.keypress(size, key)
assert self.controller.exit_popup.called
- @pytest.mark.parametrize("key", (*keys_for_command("ENTER"), " "))
+ @pytest.mark.parametrize("key", (*keys_for_command("ACTIVATE_BUTTON"), " "))
def test_checkbox_toggle_mute_stream(
self, key: str, widget_size: Callable[[Widget], urwid_Size]
) -> None:
@@ -1536,7 +1536,7 @@ def test_checkbox_toggle_mute_stream(
toggle_mute_status.assert_called_once_with(stream_id)
- @pytest.mark.parametrize("key", (*keys_for_command("ENTER"), " "))
+ @pytest.mark.parametrize("key", (*keys_for_command("ACTIVATE_BUTTON"), " "))
def test_checkbox_toggle_pin_stream(
self, key: str, widget_size: Callable[[Widget], urwid_Size]
) -> None:
@@ -1549,7 +1549,7 @@ def test_checkbox_toggle_pin_stream(
toggle_pin_status.assert_called_once_with(stream_id)
- @pytest.mark.parametrize("key", (*keys_for_command("ENTER"), " "))
+ @pytest.mark.parametrize("key", (*keys_for_command("ACTIVATE_BUTTON"), " "))
def test_checkbox_toggle_visual_notification(
self, key: str, widget_size: Callable[[Widget], urwid_Size]
) -> None:
diff --git a/zulipterminal/config/keys.py b/zulipterminal/config/keys.py
index 473d19c0f9..74128975e1 100644
--- a/zulipterminal/config/keys.py
+++ b/zulipterminal/config/keys.py
@@ -91,6 +91,11 @@ class KeyBinding(TypedDict):
'help_text': 'Go to bottom / Last message',
'key_category': 'navigation',
},
+ 'ACTIVATE_BUTTON': {
+ 'keys': ['enter'],
+ 'help_text': 'Trigger the selected entry',
+ 'key_category': 'navigation',
+ },
'REPLY_MESSAGE': {
'keys': ['r', 'enter'],
'help_text': 'Reply to the current message',
@@ -244,16 +249,16 @@ class KeyBinding(TypedDict):
'excluded_from_random_tips': True,
'key_category': 'searching',
},
+ 'EXECUTE_SEARCH': {
+ 'keys': ['enter'],
+ 'help_text': 'Submit search and browse results',
+ 'key_category': 'searching',
+ },
'TOGGLE_MUTE_STREAM': {
'keys': ['m'],
'help_text': 'Mute/unmute streams',
'key_category': 'stream_list',
},
- 'ENTER': {
- 'keys': ['enter'],
- 'help_text': 'Perform current action',
- 'key_category': 'navigation',
- },
'THUMBS_UP': {
'keys': ['+'],
'help_text': 'Toggle thumbs-up reaction to the current message',
@@ -400,6 +405,14 @@ class KeyBinding(TypedDict):
'help_text': 'Swap with previous character',
'key_category': 'editor_text_manipulation',
},
+ 'NEW_LINE': {
+ # urwid_readline's command
+ # This obvious hotkey is added to clarify against 'enter' to send
+ # and to differentiate from other hotkeys using 'enter'.
+ 'keys': ['enter'],
+ 'help_text': 'Insert new line',
+ 'key_category': 'msg_compose',
+ },
'FULL_RENDERED_MESSAGE': {
'keys': ['f'],
'help_text': 'Show/hide full rendered message (from message information)',
diff --git a/zulipterminal/ui_tools/boxes.py b/zulipterminal/ui_tools/boxes.py
index f6d3294241..91e2f80966 100644
--- a/zulipterminal/ui_tools/boxes.py
+++ b/zulipterminal/ui_tools/boxes.py
@@ -947,14 +947,14 @@ def main_view(self) -> Any:
def keypress(self, size: urwid_Size, key: str) -> Optional[str]:
if (
- is_command_key("ENTER", key) and self.text_box.edit_text == ""
+ is_command_key("EXECUTE_SEARCH", key) and self.text_box.edit_text == ""
) or is_command_key("GO_BACK", key):
self.text_box.set_edit_text("")
self.controller.exit_editor_mode()
self.controller.view.middle_column.set_focus("body")
return key
- elif is_command_key("ENTER", key):
+ elif is_command_key("EXECUTE_SEARCH", key):
self.controller.exit_editor_mode()
self.controller.search_messages(self.text_box.edit_text)
self.controller.view.middle_column.set_focus("body")
@@ -1003,7 +1003,7 @@ def valid_char(self, ch: str) -> bool:
def keypress(self, size: urwid_Size, key: str) -> Optional[str]:
if (
- is_command_key("ENTER", key) and self.get_edit_text() == ""
+ is_command_key("EXECUTE_SEARCH", key) and self.get_edit_text() == ""
) or is_command_key("GO_BACK", key):
self.panel_view.view.controller.exit_editor_mode()
self.reset_search_text()
@@ -1011,7 +1011,7 @@ def keypress(self, size: urwid_Size, key: str) -> Optional[str]:
# Don't call 'Esc' when inside a popup search-box.
if not self.panel_view.view.controller.is_any_popup_open():
self.panel_view.keypress(size, primary_key_for_command("GO_BACK"))
- elif is_command_key("ENTER", key) and not self.panel_view.empty_search:
+ elif is_command_key("EXECUTE_SEARCH", key) and not self.panel_view.empty_search:
self.panel_view.view.controller.exit_editor_mode()
self.set_caption([("filter_results", " Search Results "), " "])
self.panel_view.set_focus("body")
diff --git a/zulipterminal/ui_tools/buttons.py b/zulipterminal/ui_tools/buttons.py
index 9ecb2d32e0..91c428a2b7 100644
--- a/zulipterminal/ui_tools/buttons.py
+++ b/zulipterminal/ui_tools/buttons.py
@@ -120,7 +120,7 @@ def activate(self, key: Any) -> None:
self.show_function()
def keypress(self, size: urwid_Size, key: str) -> Optional[str]:
- if is_command_key("ENTER", key):
+ if is_command_key("ACTIVATE_BUTTON", key):
self.activate(key)
return None
else: # This is in the else clause, to avoid multiple activation
@@ -417,7 +417,7 @@ def mouse_event(
self, size: urwid_Size, event: str, button: int, col: int, row: int, focus: int
) -> bool:
if event == "mouse press" and button == 1:
- self.keypress(size, primary_key_for_command("ENTER"))
+ self.keypress(size, primary_key_for_command("ACTIVATE_BUTTON"))
return True
return super().mouse_event(size, event, button, col, row, focus)
diff --git a/zulipterminal/ui_tools/messages.py b/zulipterminal/ui_tools/messages.py
index 3ddfa10a70..b8552fdc92 100644
--- a/zulipterminal/ui_tools/messages.py
+++ b/zulipterminal/ui_tools/messages.py
@@ -898,7 +898,7 @@ def mouse_event(
if event == "mouse press" and button == 1:
if self.model.controller.is_in_editor_mode():
return True
- self.keypress(size, primary_key_for_command("ENTER"))
+ self.keypress(size, primary_key_for_command("ACTIVATE_BUTTON"))
return True
return super().mouse_event(size, event, button, col, row, focus)
diff --git a/zulipterminal/ui_tools/views.py b/zulipterminal/ui_tools/views.py
index 30fcc42a49..85dad25b79 100644
--- a/zulipterminal/ui_tools/views.py
+++ b/zulipterminal/ui_tools/views.py
@@ -1740,7 +1740,11 @@ def __init__(self, controller: Any, button: Any) -> None:
for mode in EDIT_MODE_CAPTIONS:
self.add_radio_button(mode)
super().__init__(
- controller, self.widgets, "ENTER", 62, "Topic edit propagation mode"
+ controller,
+ self.widgets,
+ "ACTIVATE_BUTTON",
+ 62,
+ "Topic edit propagation mode",
)
# Set cursor to marked checkbox.
for i in range(len(self.widgets)):
From f423e9482cda5b9332c2ff185d1193f09d174a23 Mon Sep 17 00:00:00 2001
From: Niloth-p <20315308+Niloth-p@users.noreply.github.com>
Date: Sun, 16 Jun 2024 14:08:45 +0530
Subject: [PATCH 2/2] keys: Add space key as an additional trigger of
ACTIVATE_BUTTON command.
For consistency with Urwid's ACTIVATE command.
Urwid's command map is updated to link Urwid's ACTIVATE with our
ACTIVATE_BUTTON command. Although they currently use the same keys,
they are synced to ensure future compatibility.
Hotkeys document regenerated.
Tests updated.
---
docs/hotkeys.md | 2 +-
tests/ui_tools/test_messages.py | 5 +++--
zulipterminal/config/keys.py | 7 ++++++-
3 files changed, 10 insertions(+), 4 deletions(-)
diff --git a/docs/hotkeys.md b/docs/hotkeys.md
index f91e8ab029..f2819fe8c1 100644
--- a/docs/hotkeys.md
+++ b/docs/hotkeys.md
@@ -24,7 +24,7 @@
|Scroll up|PgUp / K|
|Scroll down|PgDn / J|
|Go to bottom / Last message|End / G|
-|Trigger the selected entry|Enter|
+|Trigger the selected entry|Enter / Space|
|Narrow to all messages|a / Esc|
|Narrow to all direct messages|P|
|Narrow to all starred messages|f|
diff --git a/tests/ui_tools/test_messages.py b/tests/ui_tools/test_messages.py
index 1635468edb..08ba7d0661 100644
--- a/tests/ui_tools/test_messages.py
+++ b/tests/ui_tools/test_messages.py
@@ -7,7 +7,7 @@
from pytest import param as case
from urwid import Columns, Divider, Padding, Text
-from zulipterminal.config.keys import keys_for_command
+from zulipterminal.config.keys import keys_for_command, primary_key_for_command
from zulipterminal.config.symbols import (
ALL_MESSAGES_MARKER,
DIRECT_MESSAGE_MARKER,
@@ -1968,6 +1968,7 @@ def test_footlinks_limit(self, maximum_footlinks, expected_instance):
def test_mouse_event_left_click(
self, mocker, msg_box, key, widget_size, compose_box_is_open
):
+ expected_keypress = primary_key_for_command("ACTIVATE_BUTTON")
size = widget_size(msg_box)
col = 1
row = 1
@@ -1981,4 +1982,4 @@ def test_mouse_event_left_click(
if compose_box_is_open:
msg_box.keypress.assert_not_called()
else:
- msg_box.keypress.assert_called_once_with(size, key)
+ msg_box.keypress.assert_called_once_with(size, expected_keypress)
diff --git a/zulipterminal/config/keys.py b/zulipterminal/config/keys.py
index 74128975e1..89e25355fa 100644
--- a/zulipterminal/config/keys.py
+++ b/zulipterminal/config/keys.py
@@ -6,6 +6,7 @@
from typing_extensions import NotRequired, TypedDict
from urwid.command_map import (
+ ACTIVATE,
CURSOR_DOWN,
CURSOR_LEFT,
CURSOR_MAX_RIGHT,
@@ -92,7 +93,7 @@ class KeyBinding(TypedDict):
'key_category': 'navigation',
},
'ACTIVATE_BUTTON': {
- 'keys': ['enter'],
+ 'keys': ['enter', ' '],
'help_text': 'Trigger the selected entry',
'key_category': 'navigation',
},
@@ -445,6 +446,7 @@ class KeyBinding(TypedDict):
"SCROLL_UP": CURSOR_PAGE_UP,
"SCROLL_DOWN": CURSOR_PAGE_DOWN,
"GO_TO_BOTTOM": CURSOR_MAX_RIGHT,
+ "ACTIVATE_BUTTON": ACTIVATE,
}
@@ -490,6 +492,9 @@ def display_key_for_urwid_key(urwid_key: str) -> str:
"""
Returns a displayable user-centric format of the urwid key.
"""
+ if urwid_key == " ":
+ return "Space"
+
for urwid_map_key, display_map_key in URWID_KEY_TO_DISPLAY_KEY_MAPPING.items():
if urwid_map_key in urwid_key:
urwid_key = urwid_key.replace(urwid_map_key, display_map_key)