diff --git a/TODO.md b/TODO.md index d1a1ce43..c0179ff7 100644 --- a/TODO.md +++ b/TODO.md @@ -49,6 +49,7 @@ - Links preview - Client improvements + - Filtering rooms: smart case, fuzzy filter, search more than display names - nio.MatrixRoom has `typing_users`, no need to handle it on our own - Don't send setTypingState False when focus lost if nothing in sendbox - Initial sync filter and lazy load, see weechat-matrix `_handle_login()` diff --git a/harmonyqml/backend/backend.py b/harmonyqml/backend/backend.py index 72d48003..485ed109 100644 --- a/harmonyqml/backend/backend.py +++ b/harmonyqml/backend/backend.py @@ -113,6 +113,14 @@ class Backend(QObject): ) break + + @pyqtSlot(str) + def setRoomFilter(self, pattern: str) -> None: + for account in self.accounts: + for categ in account.roomCategories: + categ.sortedRooms.filter = pattern + + @staticmethod def getDir(standard_dir: QStandardPaths.StandardLocation) -> str: path = QStandardPaths.writableLocation(standard_dir) diff --git a/harmonyqml/backend/model/sort_filter_proxy.py b/harmonyqml/backend/model/sort_filter_proxy.py index 3c58128e..a775fa7e 100644 --- a/harmonyqml/backend/model/sort_filter_proxy.py +++ b/harmonyqml/backend/model/sort_filter_proxy.py @@ -8,27 +8,40 @@ from .list_model import ListModel class SortFilterProxy(QSortFilterProxyModel): - sortByRoleChanged = pyqtSignal() - countChanged = pyqtSignal(int) + sortByRoleChanged = pyqtSignal() + filterByRoleChanged = pyqtSignal() + filterChanged = pyqtSignal() + countChanged = pyqtSignal(int) def __init__(self, - source_model: ListModel, - sort_by_role: str, - ascending: bool = True, - parent: QObject = None) -> None: + source_model: ListModel, + sort_by_role: str, + filter_by_role: str, + ascending: bool = True, + parent: QObject = None) -> None: super().__init__(parent) self.setDynamicSortFilter(False) self.setFilterCaseSensitivity(Qt.CaseInsensitive) self.setSourceModel(source_model) - source_model.rolesSet.connect(self._set_internal_sort_role) + source_model.rolesSet.connect(self._set_internal_sort_filter_role) source_model.countChanged.connect(self.countChanged.emit) - source_model.changed.connect(self._sort) + source_model.changed.connect(self._apply_sort) self._sort_by_role = "" self.sortByRole = sort_by_role self.ascending = ascending + self._filter_by_role = "" + self.filterByRole = filter_by_role + + self._filter = None + self.filterChanged.connect( + lambda: self.countChanged.emit(self.rowCount()) + ) + + + # Sorting and filtering @pyqtProperty(str, notify=sortByRoleChanged) def sortByRole(self) -> str: @@ -38,18 +51,50 @@ class SortFilterProxy(QSortFilterProxyModel): @sortByRole.setter # type: ignore def sortByRole(self, role: str) -> None: self._sort_by_role = role - self._set_internal_sort_role() + self._set_internal_sort_filter_role() self.sortByRoleChanged.emit() - def _set_internal_sort_role(self) -> None: + @pyqtProperty(str, notify=filterByRoleChanged) + def filterByRole(self) -> str: + return self._filter_by_role + + + @filterByRole.setter # type: ignore + def filterByRole(self, role: str) -> None: + self._filter_by_role = role + self._set_internal_sort_filter_role() + self.filterByRoleChanged.emit() + + + @pyqtProperty(str, notify=filterChanged) + def filter(self) -> str: + return self._filter + + + @filter.setter # type: ignore + def filter(self, pattern: str) -> None: + self._filter = pattern + self.setFilterWildcard(pattern or "*") + self.filterChanged.emit() + + + def _set_internal_sort_filter_role(self) -> None: numbers = self.sourceModel().roleNumbers() try: - self.setSortRole(numbers[self._sort_by_role]) + self.setSortRole(numbers[self.sortByRole]) + self.setFilterRole(numbers[self.filterByRole]) except KeyError: pass # Model doesn't have its roles set yet (empty model) + def _apply_sort(self) -> None: + order = Qt.AscendingOrder if self.ascending else Qt.DescendingOrder + self.sort(0, order) + + + # The rest + def __repr__(self) -> str: return "%s(sortByRole=%r, sourceModel=%s)" % ( type(self).__name__, @@ -73,8 +118,3 @@ class SortFilterProxy(QSortFilterProxyModel): def roleNames(self) -> Dict[int, bytes]: return self.sourceModel().roleNames() - - - def _sort(self) -> None: - order = Qt.AscendingOrder if self.ascending else Qt.DescendingOrder - self.sort(0, order) diff --git a/harmonyqml/backend/signal_manager.py b/harmonyqml/backend/signal_manager.py index 5471e486..ad17c475 100644 --- a/harmonyqml/backend/signal_manager.py +++ b/harmonyqml/backend/signal_manager.py @@ -66,9 +66,10 @@ class SignalManager(QObject): for i, _ in enumerate(room_categories_kwargs): proxy = SortFilterProxy( - source_model = room_categories_kwargs[i]["rooms"], - sort_by_role = "lastEventDateTime", - ascending = False, + source_model = room_categories_kwargs[i]["rooms"], + sort_by_role = "lastEventDateTime", + filter_by_role = "displayName", + ascending = False, ) room_categories_kwargs[i]["sortedRooms"] = proxy diff --git a/harmonyqml/components/SidePane/PaneToolBar.qml b/harmonyqml/components/SidePane/PaneToolBar.qml index 27fb3627..4fa66c06 100644 --- a/harmonyqml/components/SidePane/PaneToolBar.qml +++ b/harmonyqml/components/SidePane/PaneToolBar.qml @@ -17,6 +17,8 @@ HRowLayout { placeholderText: qsTr("Filter rooms") backgroundColor: HStyle.sidePane.filterRooms.background + onTextChanged: Backend.setRoomFilter(text) + Layout.fillWidth: true Layout.preferredHeight: 32 }