diff --git a/src/calibre/gui2/actions/show_template_tester.py b/src/calibre/gui2/actions/show_template_tester.py index 5b5ae8208d..be163e8639 100644 --- a/src/calibre/gui2/actions/show_template_tester.py +++ b/src/calibre/gui2/actions/show_template_tester.py @@ -5,8 +5,6 @@ __license__ = 'GPL v3' __copyright__ = '2010, Kovid Goyal ' __docformat__ = 'restructuredtext en' - -from qt.core import QDialog from calibre.gui2.actions import InterfaceAction from calibre.gui2.dialogs.template_dialog import TemplateDialog from calibre.gui2 import error_dialog @@ -23,6 +21,17 @@ class ShowTemplateTesterAction(InterfaceAction): self.previous_text = _('Enter a template to test using data from the selected book') self.first_time = True self.qaction.triggered.connect(self.show_template_editor) + self.window_title = self.action_spec[0] + + # self.hidden_menu = QMenu() + # self.non_modal_window_title = _('Template tester -- separate dialog') + # self.shortcut_action = self.create_menu_action( + # menu=self.hidden_menu, + # unique_name='Template tester', + # text=self.non_modal_window_title, + # icon='debug.png', + # triggered=partial(self.show_template_editor, modal=False)) + self.non_modal_dialogs = list() def last_template_text(self): return self.previous_text @@ -44,9 +53,27 @@ class ShowTemplateTesterAction(InterfaceAction): if row.isValid(): mi.append(db.new_api.get_proxy_metadata(db.data.index_to_id(row.row()))) if mi: + for dn in range(-1, len(self.non_modal_dialogs)): + if dn < 0: + continue + if self.non_modal_dialogs[dn] is None: + break + else: + dn = len(self.non_modal_dialogs) + if dn == len(self.non_modal_dialogs): + self.non_modal_dialogs.append(True) + else: + self.non_modal_dialogs[dn] = True t = TemplateDialog(self.gui, self.previous_text, - mi, text_is_placeholder=self.first_time) - t.setWindowTitle(_('Template tester')) - if t.exec() == QDialog.DialogCode.Accepted: - self.previous_text = t.rule[1] - self.first_time = False + mi, text_is_placeholder=self.first_time, + dialog_number=dn) + self.non_modal_dialogs[dn] = t + t.setWindowTitle(self.window_title, dialog_number=dn+1) + t.tester_closed.connect(self.save_template_text) + t.show() + + def save_template_text(self, txt, dialog_number): + if txt is not None: + self.previous_text = txt + self.first_time = False + self.non_modal_dialogs[dialog_number] = None diff --git a/src/calibre/gui2/dialogs/quickview.py b/src/calibre/gui2/dialogs/quickview.py index 3c77b86a12..98b033d1f5 100644 --- a/src/calibre/gui2/dialogs/quickview.py +++ b/src/calibre/gui2/dialogs/quickview.py @@ -154,11 +154,10 @@ class Quickview(QDialog, Ui_Quickview): def __init__(self, gui, row, toggle_shortcut): self.is_pane = gprefs.get('quickview_is_pane', False) - if not self.is_pane: - QDialog.__init__(self, gui, flags=Qt.WindowType.Widget) + QDialog.__init__(self, None, flags=Qt.WindowType.Window) else: - QDialog.__init__(self, gui) + QDialog.__init__(self, None, flags=Qt.WindowType.Dialog) Ui_Quickview.__init__(self) self.setupUi(self) self.isClosed = False diff --git a/src/calibre/gui2/dialogs/template_dialog.py b/src/calibre/gui2/dialogs/template_dialog.py index 5077bb980f..96e73e0be6 100644 --- a/src/calibre/gui2/dialogs/template_dialog.py +++ b/src/calibre/gui2/dialogs/template_dialog.py @@ -15,7 +15,7 @@ from qt.core import ( QAbstractItemView, QApplication, QColor, QComboBox, QCursor, QDialog, QDialogButtonBox, QFont, QFontDatabase, QFontInfo, QFontMetrics, QIcon, QLineEdit, QPalette, QSize, QSyntaxHighlighter, Qt, QTableWidget, QTableWidgetItem, - QTextCharFormat, QTextOption, QVBoxLayout, + QTextCharFormat, QTextOption, QVBoxLayout, pyqtSignal ) from calibre import sanitize_file_name @@ -316,15 +316,35 @@ translate_table = str.maketrans({ class TemplateDialog(QDialog, Ui_TemplateDialog): + tester_closed = pyqtSignal(object, object) + + def setWindowTitle(self, title, dialog_number=None): + if dialog_number is None: + title = _('{title} (only one template dialog allowed)').format(title=title) + else: + title = _('{title} dialog number {number} (multiple template dialogs allowed)').format( + title=title, number=dialog_number) + super().setWindowTitle(title) + def __init__(self, parent, text, mi=None, fm=None, color_field=None, icon_field_key=None, icon_rule_kind=None, doing_emblem=False, text_is_placeholder=False, dialog_is_st_editor=False, global_vars=None, all_functions=None, builtin_functions=None, - python_context_object=None): - QDialog.__init__(self, parent) + python_context_object=None, dialog_number=None): + # If dialog_number isn't None then we want separate non-modal windows + # that don't stay on top of the main dialog. This lets Alt-Tab work to + # switch between them. dialog_number must be set only by the template + # tester, not the rules dialogs etc that depend on modality. + if dialog_number is None: + QDialog.__init__(self, parent, flags=Qt.WindowType.Dialog) + else: + QDialog.__init__(self, None, flags=Qt.WindowType.Window) + self.raise_() # Not needed on windows but here just in case Ui_TemplateDialog.__init__(self) self.setupUi(self) + self.setWindowIcon(self.windowIcon()) + self.dialog_number = dialog_number self.coloring = color_field is not None self.iconing = icon_field_key is not None self.embleming = doing_emblem @@ -387,10 +407,6 @@ class TemplateDialog(QDialog, Ui_TemplateDialog): self.icon_field.setCurrentIndex(self.icon_field.findData(icon_field_key)) self.setup_saved_template_editor(not dialog_is_st_editor, dialog_is_st_editor) - # Remove help icon on title bar - icon = self.windowIcon() - self.setWindowFlags(self.windowFlags()&(~Qt.WindowType.WindowContextHelpButtonHint)) - self.setWindowIcon(icon) self.all_functions = all_functions if all_functions else formatter_functions().get_functions() self.builtins = (builtin_functions if builtin_functions else @@ -411,8 +427,7 @@ class TemplateDialog(QDialog, Ui_TemplateDialog): # Set up the display table self.table_column_widths = None try: - self.table_column_widths = \ - gprefs.get('template_editor_table_widths', None) + self.table_column_widths = gprefs.get(self.geometry_string('template_editor_table_widths'), None) except: pass self.set_mi(mi, fm) @@ -483,7 +498,12 @@ class TemplateDialog(QDialog, Ui_TemplateDialog): self.textbox.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu) self.textbox.customContextMenuRequested.connect(self.show_context_menu) # Now geometry - self.restore_geometry(gprefs, 'template_editor_dialog_geometry') + self.restore_geometry(gprefs, self.geometry_string('template_editor_dialog_geometry')) + + def geometry_string(self, txt): + if self.dialog_number is None or self.dialog_number == 0: + return txt + return txt + '_' + str(self.dialog_number) def setup_saved_template_editor(self, show_buttonbox, show_doc_and_name): self.buttonBox.setVisible(show_buttonbox) @@ -890,8 +910,8 @@ def evaluate(book, context): self.table_column_widths.append(self.template_value.columnWidth(c)) def save_geometry(self): - gprefs['template_editor_table_widths'] = self.table_column_widths - super().save_geometry(gprefs, 'template_editor_dialog_geometry') + gprefs[self.geometry_string('template_editor_table_widths')] = self.table_column_widths + super().save_geometry(gprefs, self.geometry_string('template_editor_dialog_geometry')) def keyPressEvent(self, ev): if ev.key() == Qt.Key.Key_Escape: @@ -932,8 +952,11 @@ def evaluate(book, context): self.rule = ('', txt) self.save_geometry() QDialog.accept(self) + if self.dialog_number is not None: + self.tester_closed.emit(txt, self.dialog_number) def reject(self): + self.save_geometry() QDialog.reject(self) if self.dialog_is_st_editor: parent = self.parent() @@ -944,6 +967,8 @@ def evaluate(book, context): parent = parent.parent() if parent is None: break + if self.dialog_number is not None: + self.tester_closed.emit(None, self.dialog_number) class BreakReporterItem(QTableWidgetItem):