diff --git a/tests/ui/test_ui_tools.py b/tests/ui/test_ui_tools.py index 0a4d9ec030..17968c1cdc 100644 --- a/tests/ui/test_ui_tools.py +++ b/tests/ui/test_ui_tools.py @@ -7,7 +7,7 @@ from zulipterminal.config.keys import keys_for_command, primary_key_for_command from zulipterminal.config.symbols import STATUS_ACTIVE -from zulipterminal.helper import powerset +from zulipterminal.helper import SearchStatus, powerset from zulipterminal.ui_tools.views import ( SIDE_PANELS_MOUSE_SCROLL_LINES, LeftColumnView, @@ -565,6 +565,7 @@ def test_keypress_CLEAR_SEARCH(self, mocker, stream_view, key, widget_size): mocker.patch.object(stream_view, "set_focus") mocker.patch(VIEWS + ".urwid.Frame.keypress") mocker.patch.object(stream_view.stream_search_box, "reset_search_text") + stream_view.search_status = SearchStatus.FILTERED stream_view.streams_btn_list = ["FOO", "foo", "fan", "boo", "BOO"] stream_view.focus_index_before_search = 3 @@ -731,6 +732,7 @@ def test_keypress_CLEAR_SEARCH(self, mocker, topic_view, key, widget_size): mocker.patch(VIEWS + ".TopicsView.set_focus") mocker.patch(VIEWS + ".urwid.Frame.keypress") mocker.patch.object(topic_view.topic_search_box, "reset_search_text") + topic_view.search_status = SearchStatus.FILTERED topic_view.topics_btn_list = ["FOO", "foo", "fan", "boo", "BOO"] topic_view.focus_index_before_search = 3 @@ -1112,6 +1114,7 @@ def test_keypress_CLEAR_SEARCH(self, right_col_view, mocker, key, widget_size): mocker.patch(VIEWS + ".RightColumnView.set_focus") mocker.patch(VIEWS + ".RightColumnView.set_body") mocker.patch.object(right_col_view.user_search, "reset_search_text") + right_col_view.search_status = SearchStatus.FILTERED right_col_view.users_btn_list = [] right_col_view.keypress(size, key) diff --git a/tests/ui_tools/test_boxes.py b/tests/ui_tools/test_boxes.py index c4e89a2b58..729efeee8c 100644 --- a/tests/ui_tools/test_boxes.py +++ b/tests/ui_tools/test_boxes.py @@ -24,7 +24,7 @@ STREAM_MARKER_WEB_PUBLIC, ) from zulipterminal.config.ui_mappings import StreamAccessType -from zulipterminal.helper import Index, MinimalUserData +from zulipterminal.helper import Index, MinimalUserData, SearchStatus from zulipterminal.ui_tools.boxes import ( MAX_MESSAGE_LENGTH_CONFIRMATION_POPUP, PanelSearchBox, @@ -1903,8 +1903,8 @@ def test_keypress_ENTER( size = widget_size(panel_search_box) panel_search_box.panel_view.view.controller.is_in_editor_mode = lambda: True panel_search_box.panel_view.log = log - empty_search = not log - panel_search_box.panel_view.empty_search = empty_search + search_status = SearchStatus.FILTERED if log else SearchStatus.EMPTY + panel_search_box.panel_view.search_status = search_status panel_search_box.set_caption("") panel_search_box.edit_text = "key words" diff --git a/zulipterminal/helper.py b/zulipterminal/helper.py index a71d055b74..733e1159fe 100644 --- a/zulipterminal/helper.py +++ b/zulipterminal/helper.py @@ -7,6 +7,7 @@ import time from collections import defaultdict from contextlib import contextmanager +from enum import Enum from functools import partial, wraps from itertools import chain, combinations from re import ASCII, MULTILINE, findall, match @@ -49,6 +50,12 @@ StreamAccessType = Literal["public", "private", "web-public"] +class SearchStatus(Enum): + DEFAULT = 0 + FILTERED = 1 + EMPTY = 2 + + class StreamData(TypedDict): name: str id: int diff --git a/zulipterminal/ui_tools/boxes.py b/zulipterminal/ui_tools/boxes.py index 9856685336..da0a5acfc7 100644 --- a/zulipterminal/ui_tools/boxes.py +++ b/zulipterminal/ui_tools/boxes.py @@ -36,6 +36,7 @@ ) from zulipterminal.config.ui_mappings import STREAM_ACCESS_TYPE from zulipterminal.helper import ( + SearchStatus, asynch, format_string, match_emoji, @@ -1041,9 +1042,15 @@ def keypress(self, size: urwid_Size, key: str) -> Optional[str]: self.reset_search_text() self.panel_view.set_focus("body") # Don't call 'Esc' when inside a popup search-box. - if not self.panel_view.view.controller.is_any_popup_open(): + if ( + not self.panel_view.view.controller.is_any_popup_open() + and self.panel_view.search_status != SearchStatus.DEFAULT + ): self.panel_view.keypress(size, primary_key_for_command("CLEAR_SEARCH")) - elif is_command_key("EXECUTE_SEARCH", key) and not self.panel_view.empty_search: + elif ( + is_command_key("EXECUTE_SEARCH", key) + and self.panel_view.search_status != SearchStatus.EMPTY + ): 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/views.py b/zulipterminal/ui_tools/views.py index c2034b3ef7..776cdaa387 100644 --- a/zulipterminal/ui_tools/views.py +++ b/zulipterminal/ui_tools/views.py @@ -36,6 +36,7 @@ ) from zulipterminal.config.ui_sizes import LEFT_WIDTH from zulipterminal.helper import ( + SearchStatus, TidiedUserInfo, asynch, match_emoji, @@ -335,7 +336,7 @@ def __init__(self, streams_btn_list: List[Any], view: Any) -> None: ), ) self.search_lock = threading.Lock() - self.empty_search = False + self.search_status = SearchStatus.DEFAULT @asynch def update_streams(self, search_box: Any, new_text: str) -> None: @@ -352,7 +353,11 @@ def update_streams(self, search_box: Any, new_text: str) -> None: )[0] streams_display_num = len(streams_display) - self.empty_search = streams_display_num == 0 + self.search_status = ( + SearchStatus.EMPTY + if streams_display_num == 0 + else SearchStatus.FILTERED + ) # Add a divider to separate pinned streams from the rest. pinned_stream_names = [ @@ -371,7 +376,7 @@ def update_streams(self, search_box: Any, new_text: str) -> None: streams_display.insert(first_unpinned_index, StreamsViewDivider()) self.log.clear() - if not self.empty_search: + if self.search_status == SearchStatus.FILTERED: self.log.extend(streams_display) else: self.log.extend([self.stream_search_box.search_error]) @@ -398,14 +403,20 @@ def keypress(self, size: urwid_Size, key: str) -> Optional[str]: self.stream_search_box.set_caption(" ") self.view.controller.enter_editor_mode_with(self.stream_search_box) return key - elif is_command_key("CLEAR_SEARCH", key): + elif ( + is_command_key("CLEAR_SEARCH", key) + and self.search_status != SearchStatus.DEFAULT + ): self.stream_search_box.reset_search_text() self.log.clear() self.log.extend(self.streams_btn_list) self.set_focus("body") self.log.set_focus(self.focus_index_before_search) + self.search_status = SearchStatus.DEFAULT self.view.controller.update_screen() return key + elif is_command_key("ALL_MESSAGES", key): + self.view.home_button.activate(key) return super().keypress(size, key) @@ -436,7 +447,7 @@ def __init__( header=self.header_list, ) self.search_lock = threading.Lock() - self.empty_search = False + self.search_status = SearchStatus.DEFAULT def _focus_position_for_topic_name(self) -> int: saved_topic_state = self.view.saved_topic_in_stream_id( @@ -461,10 +472,14 @@ def update_topics(self, search_box: Any, new_text: str) -> None: for topic in self.topics_btn_list.copy() if new_text in topic.topic_name.lower() ] - self.empty_search = len(topics_to_display) == 0 + self.search_status = ( + SearchStatus.EMPTY + if len(topics_to_display) == 0 + else SearchStatus.FILTERED + ) self.log.clear() - if not self.empty_search: + if self.search_status == SearchStatus.FILTERED: self.log.extend(topics_to_display) else: self.log.extend([self.topic_search_box.search_error]) @@ -518,14 +533,20 @@ def keypress(self, size: urwid_Size, key: str) -> Optional[str]: self.topic_search_box.set_caption(" ") self.view.controller.enter_editor_mode_with(self.topic_search_box) return key - elif is_command_key("CLEAR_SEARCH", key): + elif ( + is_command_key("CLEAR_SEARCH", key) + and self.search_status != SearchStatus.DEFAULT + ): self.topic_search_box.reset_search_text() self.log.clear() self.log.extend(self.topics_btn_list) self.set_focus("body") self.log.set_focus(self.focus_index_before_search) + self.search_status = SearchStatus.DEFAULT self.view.controller.update_screen() return key + elif is_command_key("ALL_MESSAGES", key): + self.view.home_button.activate(key) return super().keypress(size, key) @@ -665,7 +686,7 @@ def __init__(self, view: Any) -> None: self.allow_update_user_list = True self.search_lock = threading.Lock() - self.empty_search = False + self.search_status = SearchStatus.DEFAULT super().__init__(self.users_view(), header=search_box) @asynch @@ -706,10 +727,12 @@ def update_user_list( else: users_display = users - self.empty_search = len(users_display) == 0 + self.search_status = ( + SearchStatus.EMPTY if len(users_display) == 0 else SearchStatus.FILTERED + ) # FIXME Update log directly? - if not self.empty_search: + if self.search_status != SearchStatus.EMPTY: self.body = self.users_view(users_display) else: self.body = UsersView( @@ -759,14 +782,20 @@ def keypress(self, size: urwid_Size, key: str) -> Optional[str]: self.user_search.set_caption(" ") self.view.controller.enter_editor_mode_with(self.user_search) return key - elif is_command_key("CLEAR_SEARCH", key): + elif ( + is_command_key("CLEAR_SEARCH", key) + and self.search_status != SearchStatus.DEFAULT + ): self.user_search.reset_search_text() self.allow_update_user_list = True self.body = UsersView(self.view.controller, self.users_btn_list) self.set_body(self.body) self.set_focus("body") + self.search_status = SearchStatus.DEFAULT self.view.controller.update_screen() return key + elif is_command_key("ALL_MESSAGES", key): + self.view.home_button.activate(key) elif is_command_key("GO_LEFT", key): self.view.show_right_panel(visible=False) return super().keypress(size, key) @@ -926,6 +955,8 @@ def keypress(self, size: urwid_Size, key: str) -> Optional[str]: return key elif is_command_key("GO_RIGHT", key): self.view.show_left_panel(visible=False) + elif is_command_key("ALL_MESSAGES", key) and self.get_focus() is self.menu_v: + self.view.home_button.activate(key) return super().keypress(size, key) @@ -2027,7 +2058,7 @@ def __init__( search_box = urwid.Pile( [self.emoji_search, urwid.Divider(SECTION_DIVIDER_LINE)] ) - self.empty_search = False + self.search_status = SearchStatus.DEFAULT self.search_lock = threading.Lock() super().__init__( controller, @@ -2073,10 +2104,14 @@ def update_emoji_list( else: self.emojis_display = self.emoji_buttons - self.empty_search = len(self.emojis_display) == 0 + self.search_status = ( + SearchStatus.EMPTY + if len(self.emojis_display) == 0 + else SearchStatus.FILTERED + ) body_content = self.emojis_display - if self.empty_search: + if self.search_status == SearchStatus.EMPTY: body_content = [self.emoji_search.search_error] self.contents["body"] = ( @@ -2150,5 +2185,6 @@ def keypress(self, size: urwid_Size, key: str) -> str: self.emoji_search.reset_search_text() self.controller.exit_editor_mode() self.controller.exit_popup() + self.search_status = SearchStatus.DEFAULT return key return super().keypress(size, key)