mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Better fix for the Qt book list phantom edits issue
The phantom edits are happening because of the mirroring with the pin view. Apparently in some update Qt has started triggering edits on currentChanged events. Sigh. For the moment we disable editing of cells in the mirrored view when it is hidden this worksaround the problem for most people, need a better fix for when the view is being used.
This commit is contained in:
parent
b56849623f
commit
f6167d7f0e
@ -206,23 +206,13 @@ def get_val_for_textlike_columns(index_):
|
||||
|
||||
class StyledItemDelegate(QStyledItemDelegate):
|
||||
'''
|
||||
When closing an editor and opening another, Qt sometimes picks what appears
|
||||
to be a random line and column for the second editor. This function checks
|
||||
that the current index for a new editor is the same as the current view. If
|
||||
it isn't then the editor shouldn't be opened.
|
||||
|
||||
Set the flag ignore_kb_mods_on_edit before opening an editor if you don't
|
||||
want keyboard modifiers taken into account, for example when using Shift-Tab
|
||||
as a backtab when editing cells. This prevents opening dialogs by mistake.
|
||||
See giu2.library.views.closeEditor() for an example.
|
||||
'''
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.table_widget = args[0]
|
||||
# Set this to True here. It is up the the subclasses to set it to False if needed.
|
||||
self.is_editable_with_tab = True
|
||||
self.ignore_kb_mods_on_edit = False
|
||||
is_editable_with_tab = True # sub-classes set to False is needed
|
||||
ignore_kb_mods_on_edit = False
|
||||
|
||||
def createEditor(self, parent, option, index):
|
||||
current_indices = [self.table_widget.currentIndex()]
|
||||
|
@ -449,7 +449,7 @@ class BooksView(QTableView): # {{{
|
||||
for wv in self, self.pin_view:
|
||||
wv.setSelectionBehavior(QAbstractItemView.SelectionBehavior.SelectRows)
|
||||
wv.setSortingEnabled(True)
|
||||
self.selectionModel().currentRowChanged.connect(self._model.current_changed, type=Qt.ConnectionType.QueuedConnection)
|
||||
self.selectionModel().currentRowChanged.connect(self.on_current_row_change)
|
||||
self.selectionModel().selectionChanged.connect(self.selection_changed.emit)
|
||||
self.preserve_state = partial(PreserveViewState, self)
|
||||
self.marked_changed_listener = FunctionDispatcher(self.marked_changed)
|
||||
@ -1637,48 +1637,65 @@ class BooksView(QTableView): # {{{
|
||||
def close(self):
|
||||
self._model.close()
|
||||
|
||||
def is_index_editable_with_tab(self, index) -> bool:
|
||||
if not index.isValid():
|
||||
return False
|
||||
col = self.column_map[index.column()]
|
||||
m = self.model()
|
||||
if m.is_custom_column(col):
|
||||
# Don't try to open editors implemented by dialogs such as
|
||||
# markdown, composites and comments
|
||||
return self.itemDelegateForIndex(index).is_editable_with_tab
|
||||
return bool(m.flags(index) & Qt.ItemFlag.ItemIsEditable)
|
||||
|
||||
def closeEditor(self, editor, hint):
|
||||
# As of Qt 6.7.2, for some reason, Qt opens the next editor after
|
||||
# closing this editor and then immediately closes it again. So
|
||||
# workaround the bug by opening the editor again after an event loop
|
||||
# tick.
|
||||
# We want to implement our own go to next/previous cell behavior
|
||||
# so do it here.
|
||||
orig = self.currentIndex()
|
||||
move_by = None
|
||||
do_move = False
|
||||
delta = 1
|
||||
if hint is QAbstractItemDelegate.EndEditHint.EditNextItem:
|
||||
move_by = QAbstractItemView.CursorAction.MoveNext
|
||||
do_move = True
|
||||
elif hint is QAbstractItemDelegate.EndEditHint.EditPreviousItem:
|
||||
move_by = QAbstractItemView.CursorAction.MovePrevious
|
||||
if move_by is not None:
|
||||
hint = QAbstractItemDelegate.EndEditHint.NoHint
|
||||
super().closeEditor(editor, hint)
|
||||
if move_by is not None and self.currentIndex() == orig and self.state() is not QAbstractItemView.State.EditingState:
|
||||
# Skip over columns that aren't editable or are implemented by a dialog
|
||||
m = self._model
|
||||
while True:
|
||||
index = self.moveCursor(move_by, Qt.KeyboardModifier.NoModifier)
|
||||
if not index.isValid():
|
||||
break
|
||||
self.setCurrentIndex(index)
|
||||
col = self.column_map[index.column()]
|
||||
if m.is_custom_column(col):
|
||||
do_move = True
|
||||
delta = -1
|
||||
super().closeEditor(editor, QAbstractItemDelegate.EndEditHint.NoHint)
|
||||
self.selectionModel().setCurrentIndex(orig, QItemSelectionModel.SelectionFlag.NoUpdate)
|
||||
if do_move:
|
||||
QTimer.singleShot(0, lambda: self.edit_next_cell(orig, delta))
|
||||
|
||||
def on_current_row_change(self, current, previous):
|
||||
self._model.current_changed(current, previous)
|
||||
|
||||
def edit(self, index, trigger=QAbstractItemView.EditTrigger.AllEditTriggers, event=None):
|
||||
edited = super().edit(index, trigger, event)
|
||||
return edited
|
||||
|
||||
def edit_next_cell(self, current, delta=1):
|
||||
m = self.model()
|
||||
idx = m.index(current.row(), current.column(), current.parent())
|
||||
while True:
|
||||
col = idx.column() + delta
|
||||
if col < 0 or col >= len(self.column_map):
|
||||
return
|
||||
colname = self.column_map[col]
|
||||
idx = m.index(current.row(), col, current.parent())
|
||||
if m.is_custom_column(colname):
|
||||
if self.itemDelegateForIndex(idx).is_editable_with_tab:
|
||||
# Don't try to open editors implemented by dialogs such as
|
||||
# markdown, composites and comments
|
||||
if self.itemDelegateForIndex(index).is_editable_with_tab:
|
||||
break
|
||||
elif m.flags(index) & Qt.ItemFlag.ItemIsEditable:
|
||||
# Standard editable column
|
||||
break
|
||||
if index.isValid():
|
||||
def edit():
|
||||
if index.isValid():
|
||||
self.setCurrentIndex(index)
|
||||
# Tell the delegate to ignore keyboard modifiers in case
|
||||
# Shift-Tab is being used to move the cell.
|
||||
d = self.itemDelegateForIndex(index)
|
||||
if d is not None:
|
||||
d.ignore_kb_mods_on_edit = True
|
||||
self.edit(index)
|
||||
QTimer.singleShot(0, edit)
|
||||
elif m.flags(idx) & Qt.ItemFlag.ItemIsEditable:
|
||||
break
|
||||
|
||||
if idx.isValid():
|
||||
# Tell the delegate to ignore keyboard modifiers in case
|
||||
# Shift-Tab is being used to move the cell.
|
||||
d = self.itemDelegateForIndex(idx)
|
||||
if d is not None:
|
||||
d.ignore_kb_mods_on_edit = True
|
||||
self.setCurrentIndex(idx)
|
||||
self.edit(idx)
|
||||
|
||||
def set_editable(self, editable, supports_backloading):
|
||||
self._model.set_editable(editable)
|
||||
|
@ -2,7 +2,7 @@
|
||||
# License: GPLv3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>
|
||||
|
||||
|
||||
from qt.core import QSplitter, QTableView
|
||||
from qt.core import QAbstractItemView, QSplitter, QTableView
|
||||
|
||||
from calibre.gui2 import gprefs
|
||||
from calibre.gui2.library import DEFAULT_SORT
|
||||
@ -17,6 +17,11 @@ class PinTableView(QTableView):
|
||||
self.splitter = None
|
||||
self.disable_save_state = False
|
||||
|
||||
def edit(self, index, trigger=QAbstractItemView.EditTrigger.AllEditTriggers, event=None):
|
||||
if not self.isVisible():
|
||||
return False
|
||||
return super().edit(index, trigger, event)
|
||||
|
||||
@property
|
||||
def column_map(self):
|
||||
return self.books_view.column_map
|
||||
|
Loading…
x
Reference in New Issue
Block a user