From daedd72caab43fb9e1cdb3bb8ca1aca4d01e5e87 Mon Sep 17 00:00:00 2001 From: junlu592 Date: Mon, 3 Nov 2025 17:02:13 +0100 Subject: [PATCH 1/6] Close sync completion tooltip when window loses focus --- qt/aqt/sync.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/qt/aqt/sync.py b/qt/aqt/sync.py index 75bdeca89..0ce02866a 100644 --- a/qt/aqt/sync.py +++ b/qt/aqt/sync.py @@ -121,6 +121,29 @@ def sync_collection(mw: aqt.main.AnkiQt, on_done: Callable[[], None]) -> None: showText(out.server_message, parent=mw) if out.required == out.NO_CHANGES: tooltip(parent=mw, msg=tr.sync_collection_complete()) + + # Monitor window focus and close tooltip if window loses focus + def check_focus() -> None: + from aqt.utils import closeTooltip + + # Close tooltip if window loses focus, becomes invisible, or is minimized + if ( + not mw.isActiveWindow() + or not mw.isVisible() + or (mw.windowState() & Qt.WindowState.WindowMinimized) + ): + closeTooltip() + focus_timer.stop() + + focus_timer = QTimer(mw) + qconnect(focus_timer.timeout, check_focus) + focus_timer.start(100) # Check every 100ms + + # Stop monitoring after tooltip's natural expiry time + def stop_monitoring() -> None: + focus_timer.stop() + + QTimer.singleShot(3000, stop_monitoring) # all done; track media progress mw.media_syncer.start_monitoring() return on_done() From 0ecf7084213914f8e313db51f9c1b512baffd19b Mon Sep 17 00:00:00 2001 From: junlu592 Date: Mon, 3 Nov 2025 17:19:06 +0100 Subject: [PATCH 2/6] Add Junia Mannervik to CONTRIBUTORS --- CONTRIBUTORS | 1 + 1 file changed, 1 insertion(+) diff --git a/CONTRIBUTORS b/CONTRIBUTORS index d90b7dbcc..d19044034 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -254,6 +254,7 @@ nav1s Ranjit Odedra Eltaurus jariji +Junia Mannervik ******************** From eb42e901d7af59693da400121cf45248dea8097a Mon Sep 17 00:00:00 2001 From: junlu592 Date: Fri, 7 Nov 2025 12:47:32 +0100 Subject: [PATCH 3/6] moved monotoring of tooltip to be centralized --- qt/aqt/sync.py | 23 ----------------------- qt/aqt/utils.py | 39 +++++++++++++++++++++++++++++++++++++-- 2 files changed, 37 insertions(+), 25 deletions(-) diff --git a/qt/aqt/sync.py b/qt/aqt/sync.py index 0ce02866a..75bdeca89 100644 --- a/qt/aqt/sync.py +++ b/qt/aqt/sync.py @@ -121,29 +121,6 @@ def sync_collection(mw: aqt.main.AnkiQt, on_done: Callable[[], None]) -> None: showText(out.server_message, parent=mw) if out.required == out.NO_CHANGES: tooltip(parent=mw, msg=tr.sync_collection_complete()) - - # Monitor window focus and close tooltip if window loses focus - def check_focus() -> None: - from aqt.utils import closeTooltip - - # Close tooltip if window loses focus, becomes invisible, or is minimized - if ( - not mw.isActiveWindow() - or not mw.isVisible() - or (mw.windowState() & Qt.WindowState.WindowMinimized) - ): - closeTooltip() - focus_timer.stop() - - focus_timer = QTimer(mw) - qconnect(focus_timer.timeout, check_focus) - focus_timer.start(100) # Check every 100ms - - # Stop monitoring after tooltip's natural expiry time - def stop_monitoring() -> None: - focus_timer.stop() - - QTimer.singleShot(3000, stop_monitoring) # all done; track media progress mw.media_syncer.start_monitoring() return on_done() diff --git a/qt/aqt/utils.py b/qt/aqt/utils.py index 43efc513f..68054e128 100644 --- a/qt/aqt/utils.py +++ b/qt/aqt/utils.py @@ -1061,6 +1061,7 @@ def send_to_trash(path: Path) -> None: ###################################################################### _tooltipTimer: QTimer | None = None +_tooltipFocusTimer: QTimer | None = None _tooltipLabel: QLabel | None = None @@ -1071,7 +1072,7 @@ def tooltip( x_offset: int = 0, y_offset: int = 100, ) -> None: - global _tooltipTimer, _tooltipLabel + global _tooltipTimer, _tooltipLabel, _tooltipFocusTimer class CustomLabel(QLabel): silentlyClose = True @@ -1101,6 +1102,33 @@ def tooltip( lab.setPalette(p) lab.move(aw.mapToGlobal(QPoint(0 + x_offset, aw.height() - y_offset))) lab.show() + window = aw.window() if hasattr(aw, "window") else aw + if window is None: + window = aw + + def close_if_parent_inactive() -> None: + if not _tooltipLabel or window is None: + return + if ( + not window.isActiveWindow() + or not window.isVisible() + or (window.windowState() & Qt.WindowState.WindowMinimized) + ): + closeTooltip() + + if _tooltipFocusTimer: + try: + _tooltipFocusTimer.stop() + _tooltipFocusTimer.deleteLater() + except RuntimeError: + pass + _tooltipFocusTimer = None + + focus_timer = QTimer(lab) + focus_timer.setInterval(100) + qconnect(focus_timer.timeout, close_if_parent_inactive) + focus_timer.start() + _tooltipFocusTimer = focus_timer _tooltipTimer = aqt.mw.progress.timer( period, closeTooltip, False, requiresCollection=False, parent=aw ) @@ -1108,7 +1136,7 @@ def tooltip( def closeTooltip() -> None: - global _tooltipLabel, _tooltipTimer + global _tooltipLabel, _tooltipTimer, _tooltipFocusTimer if _tooltipLabel: try: _tooltipLabel.deleteLater() @@ -1122,6 +1150,13 @@ def closeTooltip() -> None: except RuntimeError: pass _tooltipTimer = None + if _tooltipFocusTimer: + try: + _tooltipFocusTimer.stop() + _tooltipFocusTimer.deleteLater() + except RuntimeError: + pass + _tooltipFocusTimer = None # true if invalid; print warning From c3239453a4de528fafd82a22365fd7d1e8226243 Mon Sep 17 00:00:00 2001 From: junlu592 Date: Fri, 14 Nov 2025 12:23:57 +0100 Subject: [PATCH 4/6] Updated the code to only monitor focus if the window was active when the tooltip was created --- ftl/qt-repo | 2 +- qt/aqt/utils.py | 19 ++++++++++--------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/ftl/qt-repo b/ftl/qt-repo index 0b7c53023..dad4e2736 160000 --- a/ftl/qt-repo +++ b/ftl/qt-repo @@ -1 +1 @@ -Subproject commit 0b7c530233390d73b706f012bbe7489539925c7d +Subproject commit dad4e2736a2b53dcdb52d79b5703dd464c05d666 diff --git a/qt/aqt/utils.py b/qt/aqt/utils.py index 68054e128..2abfbf72c 100644 --- a/qt/aqt/utils.py +++ b/qt/aqt/utils.py @@ -1103,16 +1103,16 @@ def tooltip( lab.move(aw.mapToGlobal(QPoint(0 + x_offset, aw.height() - y_offset))) lab.show() window = aw.window() if hasattr(aw, "window") else aw - if window is None: - window = aw + + was_active = window.isActiveWindow() if window else False def close_if_parent_inactive() -> None: if not _tooltipLabel or window is None: return if ( - not window.isActiveWindow() - or not window.isVisible() + not window.isVisible() or (window.windowState() & Qt.WindowState.WindowMinimized) + or (was_active and not window.isActiveWindow()) ): closeTooltip() @@ -1124,11 +1124,12 @@ def tooltip( pass _tooltipFocusTimer = None - focus_timer = QTimer(lab) - focus_timer.setInterval(100) - qconnect(focus_timer.timeout, close_if_parent_inactive) - focus_timer.start() - _tooltipFocusTimer = focus_timer + if was_active: + focus_timer = QTimer(lab) + focus_timer.setInterval(100) + qconnect(focus_timer.timeout, close_if_parent_inactive) + focus_timer.start() + _tooltipFocusTimer = focus_timer _tooltipTimer = aqt.mw.progress.timer( period, closeTooltip, False, requiresCollection=False, parent=aw ) From 53eb33fafecefeb413052a3c8fe31b4a09b956cf Mon Sep 17 00:00:00 2001 From: junlu592 Date: Fri, 14 Nov 2025 12:30:53 +0100 Subject: [PATCH 5/6] formatting changes to pass the tests --- qt/aqt/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qt/aqt/utils.py b/qt/aqt/utils.py index 42d46053c..0de171d06 100644 --- a/qt/aqt/utils.py +++ b/qt/aqt/utils.py @@ -1103,7 +1103,7 @@ def tooltip( lab.move(aw.mapToGlobal(QPoint(0 + x_offset, aw.height() - y_offset))) lab.show() window = aw.window() if hasattr(aw, "window") else aw - + was_active = window.isActiveWindow() if window else False def close_if_parent_inactive() -> None: From a1a50659d8d76517a954d44a32d9d668d4225369 Mon Sep 17 00:00:00 2001 From: junlu592 Date: Sat, 15 Nov 2025 12:23:39 +0100 Subject: [PATCH 6/6] added new app.focusWindow() check that might work on windows --- qt/aqt/utils.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/qt/aqt/utils.py b/qt/aqt/utils.py index 0de171d06..0899abfb8 100644 --- a/qt/aqt/utils.py +++ b/qt/aqt/utils.py @@ -1109,10 +1109,13 @@ def tooltip( def close_if_parent_inactive() -> None: if not _tooltipLabel or window is None: return + # Check if window is still active and application has focus + app_has_focus = aqt.mw.app.focusWindow() is not None + window_is_active = window.isActiveWindow() if ( not window.isVisible() or (window.windowState() & Qt.WindowState.WindowMinimized) - or (was_active and not window.isActiveWindow()) + or (was_active and (not window_is_active or not app_has_focus)) ): closeTooltip()