forked from bitcoinafterlife/bal-electrum-plugin
add bal/gui
This commit is contained in:
119
bal/gui/qt/window_utils.py
Normal file
119
bal/gui/qt/window_utils.py
Normal file
@@ -0,0 +1,119 @@
|
||||
"""
|
||||
bal.gui.qt.window_utils
|
||||
=======================
|
||||
|
||||
Centralized window/dialog presentation helpers.
|
||||
|
||||
The original plugin opened dialogs inconsistently: some with ``exec()``
|
||||
(modal, stays on top) and some with ``show()`` (modeless, can fall *behind*
|
||||
the main Electrum window). It also relied on a per-instance ``self.parent``
|
||||
attribute that shadows :meth:`QWidget.parent`, and it never gave non-modal
|
||||
dialogs focus, so they could disappear behind Electrum.
|
||||
|
||||
To fix this *without changing the business logic*, all the "how is this window
|
||||
shown / focused / parented" concerns are collected here. The rest of the GUI
|
||||
code just calls these helpers, so the behaviour is consistent and easy to
|
||||
audit.
|
||||
|
||||
None of these helpers change *what* a dialog does — only its parenting,
|
||||
modality and z-order/focus.
|
||||
"""
|
||||
|
||||
from PyQt6.QtCore import Qt
|
||||
from PyQt6.QtWidgets import QWidget
|
||||
|
||||
|
||||
def top_level_of(widget):
|
||||
"""Return the proper top-level window to use as a dialog parent.
|
||||
|
||||
Electrum widgets expose ``top_level_window()``; when available we use it so
|
||||
the dialog is anchored to the real top-level Electrum window (and therefore
|
||||
stays in front of it). Falls back to the widget's own ``window()`` or the
|
||||
widget itself.
|
||||
"""
|
||||
if widget is None:
|
||||
return None
|
||||
# Electrum's MessageBoxMixin / ElectrumWindow provide top_level_window().
|
||||
tlw = getattr(widget, "top_level_window", None)
|
||||
if callable(tlw):
|
||||
try:
|
||||
return tlw()
|
||||
except Exception:
|
||||
pass
|
||||
# Plain QWidget: window() returns the top-level container.
|
||||
if isinstance(widget, QWidget):
|
||||
try:
|
||||
return widget.window()
|
||||
except Exception:
|
||||
pass
|
||||
return widget
|
||||
|
||||
|
||||
def bring_to_front(dialog):
|
||||
"""Make a *visible* dialog actually appear in front and take focus.
|
||||
|
||||
``raise_()`` alone is not enough on some window managers (notably Windows):
|
||||
without ``activateWindow()`` the dialog can stay behind the main window.
|
||||
"""
|
||||
try:
|
||||
dialog.raise_()
|
||||
dialog.activateWindow()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
def stop_thread(thread):
|
||||
"""Safely stop and join an Electrum ``TaskThread`` if present.
|
||||
|
||||
The original code commented out thread teardown, leaving background
|
||||
threads running after a dialog closed (which could touch destroyed widgets
|
||||
or keep network connections open until Electrum was restarted). This
|
||||
stops the thread and waits for it to finish, guarding against ``None`` and
|
||||
any teardown error.
|
||||
"""
|
||||
if thread is None:
|
||||
return
|
||||
try:
|
||||
thread.stop()
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
thread.wait()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
def show_modal(dialog):
|
||||
"""Show *dialog* modally and return the result of ``exec()``.
|
||||
|
||||
Modal dialogs always stay in front of their parent, which is the desired
|
||||
behaviour for the plugin's editing/confirmation dialogs.
|
||||
"""
|
||||
try:
|
||||
dialog.setWindowModality(Qt.WindowModality.WindowModal)
|
||||
except Exception:
|
||||
pass
|
||||
bring_to_front(dialog)
|
||||
return dialog.exec()
|
||||
|
||||
|
||||
def show_on_top(dialog, *, modal_to_window=True):
|
||||
"""Show *dialog* non-modally but guaranteed in front of Electrum.
|
||||
|
||||
Use this for the few dialogs that must remain non-modal (e.g. the
|
||||
transaction dialog the user may want to keep open alongside the wallet).
|
||||
It sets window-modality (so it stays above its parent window without
|
||||
blocking the whole application) and gives it focus.
|
||||
|
||||
Set ``modal_to_window=False`` for a completely modeless window.
|
||||
"""
|
||||
try:
|
||||
if modal_to_window:
|
||||
dialog.setWindowModality(Qt.WindowModality.WindowModal)
|
||||
else:
|
||||
dialog.setWindowModality(Qt.WindowModality.NonModal)
|
||||
except Exception:
|
||||
pass
|
||||
dialog.show()
|
||||
bring_to_front(dialog)
|
||||
return dialog
|
||||
Reference in New Issue
Block a user