diff --git a/gotify_tray/database/default_settings.py b/gotify_tray/database/default_settings.py index 9bd7d1f..e79c6c6 100644 --- a/gotify_tray/database/default_settings.py +++ b/gotify_tray/database/default_settings.py @@ -27,6 +27,7 @@ DEFAULT_SETTINGS = { "MainWindow/label/size": 25, "MainWindow/button/size": 33, "MainWindow/application/icon/size": 40, + "ApplicationModel/sort": False, "ImagePopup/enabled": False, "ImagePopup/extensions": [".jpg", ".jpeg", ".png", ".svg"], "ImagePopup/w": 400, diff --git a/gotify_tray/gui/MainApplication.py b/gotify_tray/gui/MainApplication.py index 9416937..6db3837 100644 --- a/gotify_tray/gui/MainApplication.py +++ b/gotify_tray/gui/MainApplication.py @@ -29,6 +29,7 @@ from .models import ( ApplicationItemDataRole, ApplicationModel, ApplicationModelItem, + ApplicationProxyModel, MessagesModel, MessagesModelItem, MessageItemDataRole, @@ -69,8 +70,9 @@ class MainApplication(QtWidgets.QApplication): self.messages_model = MessagesModel() self.application_model = ApplicationModel() + self.application_proxy_model = ApplicationProxyModel(self.application_model) - self.main_window = MainWindow(self.application_model, self.messages_model) + self.main_window = MainWindow(self.application_model, self.application_proxy_model, self.messages_model) self.main_window.show() # The initial .show() is necessary to get the correct sizes when adding MessageWigets QtCore.QTimer.singleShot(0, self.main_window.hide) @@ -195,7 +197,7 @@ class MainApplication(QtWidgets.QApplication): def add_message_to_model(self, message: gotify.GotifyMessageModel, process: bool = True): if self.application_model.itemFromId(message.appid): application_index = self.main_window.currentApplicationIndex() - if selected_application_item := self.application_model.itemFromIndex(application_index): + if selected_application_item := self.application_model.itemFromIndex(self.application_proxy_model.mapToSource(application_index)): def insert_message_helper(): if isinstance(selected_application_item, ApplicationModelItem): diff --git a/gotify_tray/gui/designs/widget_settings.py b/gotify_tray/gui/designs/widget_settings.py index 6230206..92adc27 100644 --- a/gotify_tray/gui/designs/widget_settings.py +++ b/gotify_tray/gui/designs/widget_settings.py @@ -1,6 +1,6 @@ # Form implementation generated from reading ui file 'gotify_tray/gui/designs\widget_settings.ui' # -# Created by: PyQt6 UI code generator 6.5.1 +# Created by: PyQt6 UI code generator 6.5.2 # # WARNING: Any manual changes made to this file will be lost when pyuic6 is # run again. Do not edit this file unless you know what you are doing. @@ -12,7 +12,7 @@ from PyQt6 import QtCore, QtGui, QtWidgets class Ui_Dialog(object): def setupUi(self, Dialog): Dialog.setObjectName("Dialog") - Dialog.resize(415, 426) + Dialog.resize(415, 446) self.gridLayout = QtWidgets.QGridLayout(Dialog) self.gridLayout.setObjectName("gridLayout") self.buttonBox = QtWidgets.QDialogButtonBox(parent=Dialog) @@ -73,6 +73,9 @@ class Ui_Dialog(object): self.cb_locale = QtWidgets.QCheckBox(parent=self.groupBox_2) self.cb_locale.setObjectName("cb_locale") self.verticalLayout_2.addWidget(self.cb_locale) + self.cb_sort_applications = QtWidgets.QCheckBox(parent=self.groupBox_2) + self.cb_sort_applications.setObjectName("cb_sort_applications") + self.verticalLayout_2.addWidget(self.cb_sort_applications) self.verticalLayout_4.addWidget(self.groupBox_2) self.groupBox_server_info = QtWidgets.QGroupBox(parent=self.tab_general) self.groupBox_server_info.setObjectName("groupBox_server_info") @@ -254,6 +257,7 @@ class Ui_Dialog(object): "8..10 -> high")) self.cb_priority_colors.setText(_translate("Dialog", "Show message priority colors")) self.cb_locale.setText(_translate("Dialog", "Display date in the system locale format")) + self.cb_sort_applications.setText(_translate("Dialog", "Sort the application list alphabetically (requires restart)")) self.groupBox_server_info.setTitle(_translate("Dialog", "Server info")) self.pb_change_server_info.setText(_translate("Dialog", "Change server info")) self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_general), _translate("Dialog", "General")) diff --git a/gotify_tray/gui/designs/widget_settings.ui b/gotify_tray/gui/designs/widget_settings.ui index 4908dad..9cec302 100644 --- a/gotify_tray/gui/designs/widget_settings.ui +++ b/gotify_tray/gui/designs/widget_settings.ui @@ -7,7 +7,7 @@ 0 0 415 - 426 + 446 @@ -148,6 +148,13 @@ + + + + Sort the application list alphabetically (requires restart) + + + diff --git a/gotify_tray/gui/models/ApplicationModel.py b/gotify_tray/gui/models/ApplicationModel.py index 870094b..aff32ce 100644 --- a/gotify_tray/gui/models/ApplicationModel.py +++ b/gotify_tray/gui/models/ApplicationModel.py @@ -77,3 +77,22 @@ class ApplicationModel(QtGui.QStandardItemModel): if item.data(ApplicationItemDataRole.ApplicationRole).id == appid: return item return None + + +class ApplicationProxyModel(QtCore.QSortFilterProxyModel): + def __init__(self, application_model: ApplicationModel) -> None: + super(ApplicationProxyModel, self).__init__() + self.setSourceModel(application_model) + self.setSortCaseSensitivity(QtCore.Qt.CaseSensitivity.CaseInsensitive) + if settings.value("ApplicationModel/sort", type=bool): + self.sort(0, QtCore.Qt.SortOrder.AscendingOrder) + + def lessThan(self, left: QtCore.QModelIndex, right: QtCore.QModelIndex) -> bool: + """Make sure ApplicationAllMessagesItem remains at the top of the model -- ApplicationAllMessagesItem doesn't have any ApplicationRole data + """ + if not self.sourceModel().data(left, ApplicationItemDataRole.ApplicationRole): + return True + elif not self.sourceModel().data(right, ApplicationItemDataRole.ApplicationRole): + return False + + return super().lessThan(left, right) diff --git a/gotify_tray/gui/models/__init__.py b/gotify_tray/gui/models/__init__.py index e298d93..602ae59 100644 --- a/gotify_tray/gui/models/__init__.py +++ b/gotify_tray/gui/models/__init__.py @@ -2,6 +2,7 @@ from .ApplicationModel import ( ApplicationAllMessagesItem, ApplicationModelItem, ApplicationModel, + ApplicationProxyModel, ApplicationItemDataRole, ) from .MessagesModel import MessagesModelItem, MessagesModel, MessageItemDataRole diff --git a/gotify_tray/gui/widgets/MainWindow.py b/gotify_tray/gui/widgets/MainWindow.py index b6cf24f..5ad9cc6 100644 --- a/gotify_tray/gui/widgets/MainWindow.py +++ b/gotify_tray/gui/widgets/MainWindow.py @@ -27,7 +27,7 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow): hidden = QtCore.pyqtSignal() activated = QtCore.pyqtSignal() - def __init__(self, application_model: ApplicationModel, messages_model: MessagesModel): + def __init__(self, application_model: ApplicationModel, application_proxy_model: QtCore.QSortFilterProxyModel, messages_model: MessagesModel): super(MainWindow, self).__init__() self.setupUi(self) @@ -36,9 +36,10 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow): self.setWindowTitle(__title__) self.application_model = application_model + self.application_proxy_model = application_proxy_model self.messages_model = messages_model - self.listView_applications.setModel(application_model) + self.listView_applications.setModel(application_proxy_model) self.listView_messages.setModel(messages_model) # Do not expand the applications listview when resizing @@ -122,7 +123,7 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow): def application_selection_changed_callback( self, current: QtCore.QModelIndex, previous: QtCore.QModelIndex ): - if item := self.application_model.itemFromIndex(current): + if item := self.application_model.itemFromIndex(self.application_proxy_model.mapToSource(current)): self.label_application.setText(item.text()) self.application_selection_changed.emit(item) @@ -142,7 +143,7 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow): return index = self.currentApplicationIndex() - if item := self.application_model.itemFromIndex(index): + if item := self.application_model.itemFromIndex(self.application_proxy_model.mapToSource(index)): self.delete_all.emit(item) def disable_applications(self): @@ -151,7 +152,7 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow): def enable_applications(self): self.listView_applications.setEnabled(True) - self.listView_applications.setCurrentIndex(self.application_model.index(0, 0)) + self.listView_applications.setCurrentIndex(self.application_proxy_model.index(0, 0)) def disable_buttons(self): self.pb_delete_all.setDisabled(True) @@ -176,9 +177,7 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow): self.pb_refresh.clicked.connect(self.refresh.emit) self.pb_delete_all.clicked.connect(self.delete_all_callback) - self.listView_applications.selectionModel().currentChanged.connect( - self.application_selection_changed_callback - ) + self.listView_applications.selectionModel().currentChanged.connect(self.application_selection_changed_callback) def store_state(self): settings.setValue("MainWindow/geometry", self.saveGeometry()) diff --git a/gotify_tray/gui/widgets/SettingsDialog.py b/gotify_tray/gui/widgets/SettingsDialog.py index 462f4e1..11bc353 100644 --- a/gotify_tray/gui/widgets/SettingsDialog.py +++ b/gotify_tray/gui/widgets/SettingsDialog.py @@ -60,6 +60,7 @@ class SettingsDialog(QtWidgets.QDialog, Ui_Dialog): # Interface self.cb_priority_colors.setChecked(settings.value("MessageWidget/priority_color", type=bool)) self.cb_locale.setChecked(settings.value("locale", type=bool)) + self.cb_sort_applications.setChecked(settings.value("ApplicationModel/sort", type=bool)) # Logging self.combo_logging.addItems( @@ -193,6 +194,7 @@ class SettingsDialog(QtWidgets.QDialog, Ui_Dialog): # Interface self.cb_priority_colors.stateChanged.connect(self.settings_changed_callback) self.cb_locale.stateChanged.connect(self.settings_changed_callback) + self.cb_sort_applications.stateChanged.connect(self.settings_changed_callback) # Server info self.pb_change_server_info.clicked.connect(self.change_server_info_callback) @@ -231,6 +233,7 @@ class SettingsDialog(QtWidgets.QDialog, Ui_Dialog): # Interface settings.setValue("MessageWidget/priority_color", self.cb_priority_colors.isChecked()) settings.setValue("locale", self.cb_locale.isChecked()) + settings.setValue("ApplicationModel/sort", self.cb_sort_applications.isChecked()) # Logging selected_level = self.combo_logging.currentText()