From 970404d00dc6eb855ff5532bb278445fd2994c28 Mon Sep 17 00:00:00 2001 From: soleuniverse Date: Wed, 5 Mar 2025 22:18:55 +0100 Subject: [PATCH 1/8] feat: prevent selection of preferences spinbox suffixes --- qt/aqt/preferences.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/qt/aqt/preferences.py b/qt/aqt/preferences.py index 7befe4ca2d8..6505ccf3cfe 100644 --- a/qt/aqt/preferences.py +++ b/qt/aqt/preferences.py @@ -6,6 +6,7 @@ import functools import re from collections.abc import Callable +from typing import cast import anki.lang import aqt @@ -46,6 +47,24 @@ def __init__(self, mw: AnkiQt) -> None: self.form.network_timeout, ): spinbox.setSuffix(f" {spinbox.suffix()}") + + # Prevent selecting spinboxes' suffixes or placing the cursor after them + class UnitSpinBoxEventFilter(QObject): + def eventFilter(self, spinbox: QObject | None, evt: QEvent | None) -> bool: + assert isinstance(spinbox, QSpinBox) + # maximum position of the cursor + posAfterValue = len(str(spinbox.value())) + lineEdit = cast(QLineEdit, spinbox.lineEdit()) + if evt.type() == QEvent.Type.InputMethodQuery and lineEdit.cursorPosition() > posAfterValue: + if lineEdit.hasSelectedText(): + absoluteSelectionEnd = lineEdit.selectionStart() + lineEdit.selectionLength() + if absoluteSelectionEnd > posAfterValue: + lineEdit.setSelection(lineEdit.selectionStart(), posAfterValue - lineEdit.selectionStart()) + else: + lineEdit.setCursorPosition(posAfterValue) + return False + + spinbox.installEventFilter(UnitSpinBoxEventFilter(mw)) disable_help_button(self) help_button = self.form.buttonBox.button(QDialogButtonBox.StandardButton.Help) From 273870180512cc2bef94de7b4e6fe8a65367e9fb Mon Sep 17 00:00:00 2001 From: soleuniverse Date: Wed, 5 Mar 2025 22:26:50 +0100 Subject: [PATCH 2/8] fix: add name to CONTRIBUTORS --- CONTRIBUTORS | 1 + 1 file changed, 1 insertion(+) diff --git a/CONTRIBUTORS b/CONTRIBUTORS index 0fb4726d234..7b73cb4e8d0 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -213,6 +213,7 @@ GithubAnon0000 Mike Hardy Danika_Dakika Mumtaz Hajjo Alrifai +Adnane Taghi ******************** From 0e803fc99e558d1fa7084877ef33ac356a0fc2ec Mon Sep 17 00:00:00 2001 From: soleuniverse Date: Wed, 5 Mar 2025 22:38:56 +0100 Subject: [PATCH 3/8] refactor: fix formatting --- qt/aqt/preferences.py | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/qt/aqt/preferences.py b/qt/aqt/preferences.py index 6505ccf3cfe..009280b719c 100644 --- a/qt/aqt/preferences.py +++ b/qt/aqt/preferences.py @@ -47,19 +47,29 @@ def __init__(self, mw: AnkiQt) -> None: self.form.network_timeout, ): spinbox.setSuffix(f" {spinbox.suffix()}") - + # Prevent selecting spinboxes' suffixes or placing the cursor after them class UnitSpinBoxEventFilter(QObject): - def eventFilter(self, spinbox: QObject | None, evt: QEvent | None) -> bool: + def eventFilter( + self, spinbox: QObject | None, evt: QEvent | None + ) -> bool: assert isinstance(spinbox, QSpinBox) # maximum position of the cursor posAfterValue = len(str(spinbox.value())) lineEdit = cast(QLineEdit, spinbox.lineEdit()) - if evt.type() == QEvent.Type.InputMethodQuery and lineEdit.cursorPosition() > posAfterValue: + if ( + evt.type() == QEvent.Type.InputMethodQuery + and lineEdit.cursorPosition() > posAfterValue + ): if lineEdit.hasSelectedText(): - absoluteSelectionEnd = lineEdit.selectionStart() + lineEdit.selectionLength() + absoluteSelectionEnd = ( + lineEdit.selectionStart() + lineEdit.selectionLength() + ) if absoluteSelectionEnd > posAfterValue: - lineEdit.setSelection(lineEdit.selectionStart(), posAfterValue - lineEdit.selectionStart()) + lineEdit.setSelection( + lineEdit.selectionStart(), + posAfterValue - lineEdit.selectionStart(), + ) else: lineEdit.setCursorPosition(posAfterValue) return False From ad3ad214cf92169372a0c5f68a0a8fe6e7937793 Mon Sep 17 00:00:00 2001 From: soleuniverse Date: Wed, 5 Mar 2025 22:39:32 +0100 Subject: [PATCH 4/8] fix: assert QEvent is not None --- qt/aqt/preferences.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qt/aqt/preferences.py b/qt/aqt/preferences.py index 009280b719c..1765a4d2b9b 100644 --- a/qt/aqt/preferences.py +++ b/qt/aqt/preferences.py @@ -58,7 +58,7 @@ def eventFilter( posAfterValue = len(str(spinbox.value())) lineEdit = cast(QLineEdit, spinbox.lineEdit()) if ( - evt.type() == QEvent.Type.InputMethodQuery + cast(QEvent, evt).type() == QEvent.Type.InputMethodQuery and lineEdit.cursorPosition() > posAfterValue ): if lineEdit.hasSelectedText(): From a0947168614604d8fd6fcf56875df8f3cb4ffc6a Mon Sep 17 00:00:00 2001 From: soleuniverse Date: Tue, 11 Mar 2025 00:22:14 +0100 Subject: [PATCH 5/8] fix: properly test types instead of assert --- qt/aqt/preferences.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/qt/aqt/preferences.py b/qt/aqt/preferences.py index 1765a4d2b9b..6af5d6a6bc8 100644 --- a/qt/aqt/preferences.py +++ b/qt/aqt/preferences.py @@ -53,12 +53,13 @@ class UnitSpinBoxEventFilter(QObject): def eventFilter( self, spinbox: QObject | None, evt: QEvent | None ) -> bool: - assert isinstance(spinbox, QSpinBox) + if not isinstance(spinbox, QSpinBox) or not isinstance(evt, QEvent): + return False # maximum position of the cursor posAfterValue = len(str(spinbox.value())) lineEdit = cast(QLineEdit, spinbox.lineEdit()) if ( - cast(QEvent, evt).type() == QEvent.Type.InputMethodQuery + evt.type() == QEvent.Type.InputMethodQuery and lineEdit.cursorPosition() > posAfterValue ): if lineEdit.hasSelectedText(): From 7fbe3b6494ee28dc2faa6057a226a43e81b2fbdd Mon Sep 17 00:00:00 2001 From: soleuniverse Date: Tue, 11 Mar 2025 00:26:59 +0100 Subject: [PATCH 6/8] fix: conflict in CONTRIBUTORS --- CONTRIBUTORS | 1 + 1 file changed, 1 insertion(+) diff --git a/CONTRIBUTORS b/CONTRIBUTORS index 7b73cb4e8d0..8ecf8419a7a 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -213,6 +213,7 @@ GithubAnon0000 Mike Hardy Danika_Dakika Mumtaz Hajjo Alrifai +Jakub Fidler Adnane Taghi ******************** From 945a6abe0c62a115d0cf9f47c249bba6db06a85b Mon Sep 17 00:00:00 2001 From: soleuniverse Date: Tue, 11 Mar 2025 00:37:14 +0100 Subject: [PATCH 7/8] fix: add name to about.py --- qt/aqt/about.py | 1 + 1 file changed, 1 insertion(+) diff --git a/qt/aqt/about.py b/qt/aqt/about.py index 18a7b2427dc..2e5f8f49721 100644 --- a/qt/aqt/about.py +++ b/qt/aqt/about.py @@ -217,6 +217,7 @@ def on_dialog_destroyed() -> None: "Mumtaz Hajjo Alrifai", "Luc Mcgrady", "Brayan Oliveira", + "Adnane Taghi", ) ) From 8381c18feb508eac455c18d971a773a68ba76379 Mon Sep 17 00:00:00 2001 From: soleuniverse Date: Tue, 11 Mar 2025 00:43:34 +0100 Subject: [PATCH 8/8] fix: remove type cast --- qt/aqt/preferences.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/qt/aqt/preferences.py b/qt/aqt/preferences.py index 6af5d6a6bc8..8e1d3ed52a4 100644 --- a/qt/aqt/preferences.py +++ b/qt/aqt/preferences.py @@ -6,7 +6,6 @@ import functools import re from collections.abc import Callable -from typing import cast import anki.lang import aqt @@ -57,7 +56,9 @@ def eventFilter( return False # maximum position of the cursor posAfterValue = len(str(spinbox.value())) - lineEdit = cast(QLineEdit, spinbox.lineEdit()) + lineEdit = spinbox.lineEdit() + if lineEdit is None: + return False if ( evt.type() == QEvent.Type.InputMethodQuery and lineEdit.cursorPosition() > posAfterValue