mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Book List: When editing data int he book list directly, expand the editor widget to use all available width if the data does not fit inside the current column width
Merge branch 'master' of https://github.com/cbhaley/calibre
This commit is contained in:
commit
87832d2275
@ -8,8 +8,9 @@ __docformat__ = 'restructuredtext en'
|
||||
import sys
|
||||
|
||||
from PyQt5.Qt import (Qt, QApplication, QStyle, QIcon, QDoubleSpinBox, QStyleOptionViewItem,
|
||||
QSpinBox, QStyledItemDelegate, QComboBox, QTextDocument, QSize, QMenu, QKeySequence,
|
||||
QAbstractTextDocumentLayout, QFont, QFontInfo, QDate, QDateTimeEdit, QDateTime)
|
||||
QSpinBox, QStyledItemDelegate, QComboBox, QTextDocument, QMenu, QKeySequence,
|
||||
QAbstractTextDocumentLayout, QFont, QFontInfo, QDate, QDateTimeEdit, QDateTime,
|
||||
QStyleOptionComboBox, QStyleOptionSpinBox, QLocale)
|
||||
|
||||
from calibre.gui2 import UNDEFINED_QDATETIME, error_dialog, rating_font
|
||||
from calibre.constants import iswindows
|
||||
@ -24,6 +25,75 @@ from calibre.gui2.dialogs.comments_dialog import CommentsDialog
|
||||
from calibre.gui2.dialogs.template_dialog import TemplateDialog
|
||||
from calibre.gui2.languages import LanguagesEdit
|
||||
|
||||
class UpdateEditorGeometry(object):
|
||||
|
||||
def updateEditorGeometry(self, editor, option, index):
|
||||
if editor is None:
|
||||
return
|
||||
fm = editor.fontMetrics()
|
||||
|
||||
# get the original size of the edit widget
|
||||
opt = QStyleOptionViewItem(option)
|
||||
self.initStyleOption(opt, index)
|
||||
style = QApplication.style()
|
||||
initial_geometry = style.subElementRect(style.SE_ItemViewItemText, opt, None)
|
||||
orig_width = initial_geometry.width()
|
||||
|
||||
# Compute the required width: the width that can show all of the current value
|
||||
if hasattr(self, 'get_required_width'):
|
||||
new_width = self.get_required_width(editor, index, style, fm)
|
||||
else:
|
||||
# The line edit box seems to extend by the space consumed by an 'M'.
|
||||
# So add that to the text
|
||||
text = self.displayText(index.data(Qt.DisplayRole), QLocale()) + u'M'
|
||||
srect = style.itemTextRect(fm, editor.geometry(), Qt.AlignLeft, False, text)
|
||||
new_width = srect.width()
|
||||
|
||||
# Now get the size of the combo/spinner arrows and add them to the needed width
|
||||
if isinstance(editor, (QComboBox, QDateTimeEdit)):
|
||||
r = style.subControlRect(QStyle.CC_ComboBox, QStyleOptionComboBox(),
|
||||
QStyle.SC_ComboBoxArrow)
|
||||
new_width += r.width()
|
||||
elif isinstance(editor, (QSpinBox, QDoubleSpinBox)):
|
||||
r = style.subControlRect(QStyle.CC_SpinBox, QStyleOptionSpinBox(),
|
||||
QStyle.SC_SpinBoxUp)
|
||||
new_width += r.width()
|
||||
|
||||
# Compute the maximum we can show if we consume the entire viewport
|
||||
max_width = (self.table_widget.horizontalScrollBar().geometry().width() -
|
||||
self.table_widget.verticalHeader().width())
|
||||
# What we have to display might not fit. If so, adjust down
|
||||
new_width = new_width if new_width < max_width else max_width
|
||||
|
||||
# See if we need to change the editor's geometry
|
||||
if new_width <= orig_width:
|
||||
delta_x = 0
|
||||
delta_width = 0
|
||||
else:
|
||||
# Compute the space available from the left edge of the widget to
|
||||
# the right edge of the displayed table (the viewport) and the left
|
||||
# edge of the widget to the left edge of the viewport. These are
|
||||
# used to position the edit box
|
||||
space_left = initial_geometry.x()
|
||||
space_right = max_width - space_left
|
||||
|
||||
if editor.layoutDirection() == Qt.RightToLeft:
|
||||
# If language is RtL, align to the cell's right edge if possible
|
||||
cw = initial_geometry.width()
|
||||
consume_on_left = min(space_left, new_width - cw)
|
||||
consume_on_right = max(0, new_width - (consume_on_left + cw))
|
||||
delta_x = -consume_on_left
|
||||
delta_width = consume_on_right
|
||||
else:
|
||||
# If language is LtR, align to the left if possible
|
||||
consume_on_right = min(space_right, new_width)
|
||||
consume_on_left = max(0, new_width - consume_on_right)
|
||||
delta_x = -consume_on_left
|
||||
delta_width = consume_on_right - initial_geometry.width()
|
||||
|
||||
initial_geometry.adjust(delta_x, 0, delta_width, 0)
|
||||
editor.setGeometry(initial_geometry)
|
||||
|
||||
class DateTimeEdit(QDateTimeEdit): # {{{
|
||||
|
||||
def __init__(self, parent, format):
|
||||
@ -85,10 +155,11 @@ ClearingDoubleSpinBox = make_clearing_spinbox(QDoubleSpinBox)
|
||||
|
||||
# }}}
|
||||
|
||||
class RatingDelegate(QStyledItemDelegate): # {{{
|
||||
class RatingDelegate(QStyledItemDelegate, UpdateEditorGeometry): # {{{
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
QStyledItemDelegate.__init__(self, *args, **kwargs)
|
||||
self.table_widget = args[0]
|
||||
self.rf = QFont(rating_font())
|
||||
self.em = Qt.ElideMiddle
|
||||
delta = 0
|
||||
@ -97,12 +168,19 @@ class RatingDelegate(QStyledItemDelegate): # {{{
|
||||
self.rf.setPointSize(QFontInfo(QApplication.font()).pointSize()+delta)
|
||||
|
||||
def createEditor(self, parent, option, index):
|
||||
sb = QStyledItemDelegate.createEditor(self, parent, option, index)
|
||||
sb = QSpinBox(parent)
|
||||
sb.setMinimum(0)
|
||||
sb.setMaximum(5)
|
||||
sb.setSuffix(' ' + _('stars'))
|
||||
return sb
|
||||
|
||||
def get_required_width(self, editor, index, style, fm):
|
||||
val = editor.maximum()
|
||||
text = editor.textFromValue(val) + editor.suffix()
|
||||
srect = style.itemTextRect(fm, editor.geometry(), Qt.AlignLeft, False,
|
||||
text + u'M')
|
||||
return srect.width()
|
||||
|
||||
def displayText(self, value, locale):
|
||||
r = int(value)
|
||||
if r < 0 or r > 5:
|
||||
@ -121,11 +199,12 @@ class RatingDelegate(QStyledItemDelegate): # {{{
|
||||
|
||||
# }}}
|
||||
|
||||
class DateDelegate(QStyledItemDelegate): # {{{
|
||||
class DateDelegate(QStyledItemDelegate, UpdateEditorGeometry): # {{{
|
||||
|
||||
def __init__(self, parent, tweak_name='gui_timestamp_display_format',
|
||||
default_format='dd MMM yyyy'):
|
||||
QStyledItemDelegate.__init__(self, parent)
|
||||
self.table_widget = parent
|
||||
self.tweak_name = tweak_name
|
||||
self.format = tweaks[self.tweak_name]
|
||||
if self.format is None:
|
||||
@ -140,13 +219,17 @@ class DateDelegate(QStyledItemDelegate): # {{{
|
||||
def createEditor(self, parent, option, index):
|
||||
return DateTimeEdit(parent, self.format)
|
||||
|
||||
def setEditorData(self, editor, index):
|
||||
QStyledItemDelegate.setEditorData(self, editor, index)
|
||||
|
||||
# }}}
|
||||
|
||||
class PubDateDelegate(QStyledItemDelegate): # {{{
|
||||
class PubDateDelegate(QStyledItemDelegate, UpdateEditorGeometry): # {{{
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
QStyledItemDelegate.__init__(self, *args, **kwargs)
|
||||
self.format = tweaks['gui_pubdate_display_format']
|
||||
self.table_widget = args[0]
|
||||
if self.format is None:
|
||||
self.format = 'MMM yyyy'
|
||||
|
||||
@ -169,7 +252,7 @@ class PubDateDelegate(QStyledItemDelegate): # {{{
|
||||
|
||||
# }}}
|
||||
|
||||
class TextDelegate(QStyledItemDelegate): # {{{
|
||||
class TextDelegate(QStyledItemDelegate, UpdateEditorGeometry): # {{{
|
||||
|
||||
def __init__(self, parent):
|
||||
'''
|
||||
@ -178,6 +261,7 @@ class TextDelegate(QStyledItemDelegate): # {{{
|
||||
auto-complete will be used.
|
||||
'''
|
||||
QStyledItemDelegate.__init__(self, parent)
|
||||
self.table_widget = parent
|
||||
self.auto_complete_function = None
|
||||
|
||||
def set_auto_complete_function(self, f):
|
||||
@ -207,13 +291,14 @@ class TextDelegate(QStyledItemDelegate): # {{{
|
||||
|
||||
# }}}
|
||||
|
||||
class CompleteDelegate(QStyledItemDelegate): # {{{
|
||||
class CompleteDelegate(QStyledItemDelegate, UpdateEditorGeometry): # {{{
|
||||
|
||||
def __init__(self, parent, sep, items_func_name, space_before_sep=False):
|
||||
QStyledItemDelegate.__init__(self, parent)
|
||||
self.sep = sep
|
||||
self.items_func_name = items_func_name
|
||||
self.space_before_sep = space_before_sep
|
||||
self.table_widget = parent
|
||||
|
||||
def set_database(self, db):
|
||||
self.db = db
|
||||
@ -249,7 +334,11 @@ class CompleteDelegate(QStyledItemDelegate): # {{{
|
||||
QStyledItemDelegate.setModelData(self, editor, model, index)
|
||||
# }}}
|
||||
|
||||
class LanguagesDelegate(QStyledItemDelegate): # {{{
|
||||
class LanguagesDelegate(QStyledItemDelegate, UpdateEditorGeometry): # {{{
|
||||
|
||||
def __init__(self, parent):
|
||||
QStyledItemDelegate.__init__(self, parent)
|
||||
self.table_widget = parent
|
||||
|
||||
def createEditor(self, parent, option, index):
|
||||
editor = LanguagesEdit(parent=parent)
|
||||
@ -265,7 +354,7 @@ class LanguagesDelegate(QStyledItemDelegate): # {{{
|
||||
model.setData(index, (val), Qt.EditRole)
|
||||
# }}}
|
||||
|
||||
class CcDateDelegate(QStyledItemDelegate): # {{{
|
||||
class CcDateDelegate(QStyledItemDelegate, UpdateEditorGeometry): # {{{
|
||||
|
||||
'''
|
||||
Delegate for custom columns dates. Because this delegate stores the
|
||||
@ -273,6 +362,10 @@ class CcDateDelegate(QStyledItemDelegate): # {{{
|
||||
column. This differs from all the other delegates.
|
||||
'''
|
||||
|
||||
def __init__(self, parent):
|
||||
QStyledItemDelegate.__init__(self, parent)
|
||||
self.table_widget = parent
|
||||
|
||||
def set_format(self, format):
|
||||
if not format:
|
||||
self.format = 'dd MMM yyyy'
|
||||
@ -305,12 +398,16 @@ class CcDateDelegate(QStyledItemDelegate): # {{{
|
||||
|
||||
# }}}
|
||||
|
||||
class CcTextDelegate(QStyledItemDelegate): # {{{
|
||||
class CcTextDelegate(QStyledItemDelegate, UpdateEditorGeometry): # {{{
|
||||
|
||||
'''
|
||||
Delegate for text data.
|
||||
'''
|
||||
|
||||
def __init__(self, parent):
|
||||
QStyledItemDelegate.__init__(self, parent)
|
||||
self.table_widget = parent
|
||||
|
||||
def createEditor(self, parent, option, index):
|
||||
m = index.model()
|
||||
col = m.column_map[index.column()]
|
||||
@ -331,12 +428,16 @@ class CcTextDelegate(QStyledItemDelegate): # {{{
|
||||
model.setData(index, (val), Qt.EditRole)
|
||||
# }}}
|
||||
|
||||
class CcNumberDelegate(QStyledItemDelegate): # {{{
|
||||
class CcNumberDelegate(QStyledItemDelegate, UpdateEditorGeometry): # {{{
|
||||
|
||||
'''
|
||||
Delegate for text/int/float data.
|
||||
'''
|
||||
|
||||
def __init__(self, parent):
|
||||
QStyledItemDelegate.__init__(self, parent)
|
||||
self.table_widget = parent
|
||||
|
||||
def createEditor(self, parent, option, index):
|
||||
m = index.model()
|
||||
col = m.column_map[index.column()]
|
||||
@ -357,6 +458,7 @@ class CcNumberDelegate(QStyledItemDelegate): # {{{
|
||||
if val == editor.minimum():
|
||||
val = None
|
||||
model.setData(index, (val), Qt.EditRole)
|
||||
editor.adjustSize()
|
||||
|
||||
def setEditorData(self, editor, index):
|
||||
m = index.model()
|
||||
@ -365,21 +467,37 @@ class CcNumberDelegate(QStyledItemDelegate): # {{{
|
||||
val = 0
|
||||
editor.setValue(val)
|
||||
|
||||
def get_required_width(self, editor, index, style, fm):
|
||||
val = editor.maximum()
|
||||
text = editor.textFromValue(val)
|
||||
srect = style.itemTextRect(fm, editor.geometry(), Qt.AlignLeft, False,
|
||||
text + u'M')
|
||||
return srect.width()
|
||||
|
||||
# }}}
|
||||
|
||||
class CcEnumDelegate(QStyledItemDelegate): # {{{
|
||||
class CcEnumDelegate(QStyledItemDelegate, UpdateEditorGeometry): # {{{
|
||||
|
||||
'''
|
||||
Delegate for text/int/float data.
|
||||
'''
|
||||
|
||||
def __init__(self, parent):
|
||||
QStyledItemDelegate.__init__(self, parent)
|
||||
self.table_widget = parent
|
||||
self.longest_text = ''
|
||||
|
||||
def createEditor(self, parent, option, index):
|
||||
m = index.model()
|
||||
col = m.column_map[index.column()]
|
||||
editor = DelegateCB(parent)
|
||||
editor.addItem('')
|
||||
max_len = 0
|
||||
self.longest_text = ''
|
||||
for v in m.custom_columns[col]['display']['enum_values']:
|
||||
editor.addItem(v)
|
||||
if len(v) > max_len:
|
||||
self.longest_text = v
|
||||
return editor
|
||||
|
||||
def setModelData(self, editor, model, index):
|
||||
@ -388,6 +506,14 @@ class CcEnumDelegate(QStyledItemDelegate): # {{{
|
||||
val = None
|
||||
model.setData(index, (val), Qt.EditRole)
|
||||
|
||||
def get_required_width(self, editor, index, style, fm):
|
||||
m = index.model()
|
||||
col = m.column_map[index.column()]
|
||||
use_decorations = m.custom_columns[col]['display'].get('use_decorations', False)
|
||||
srect = style.itemTextRect(fm, editor.geometry(), Qt.AlignLeft, False,
|
||||
self.longest_text + u'M')
|
||||
return srect.width() + editor.iconSize().width() if use_decorations else 0
|
||||
|
||||
def setEditorData(self, editor, index):
|
||||
m = index.model()
|
||||
val = m.db.data[index.row()][m.custom_columns[m.column_map[index.column()]]['rec_index']]
|
||||
@ -457,13 +583,14 @@ class DelegateCB(QComboBox): # {{{
|
||||
return QComboBox.event(self, e)
|
||||
# }}}
|
||||
|
||||
class CcBoolDelegate(QStyledItemDelegate): # {{{
|
||||
class CcBoolDelegate(QStyledItemDelegate, UpdateEditorGeometry): # {{{
|
||||
|
||||
def __init__(self, parent):
|
||||
'''
|
||||
Delegate for custom_column bool data.
|
||||
'''
|
||||
QStyledItemDelegate.__init__(self, parent)
|
||||
self.table_widget = parent
|
||||
|
||||
def createEditor(self, parent, option, index):
|
||||
editor = DelegateCB(parent)
|
||||
@ -472,10 +599,18 @@ class CcBoolDelegate(QStyledItemDelegate): # {{{
|
||||
if not index.model().db.prefs.get('bools_are_tristate'):
|
||||
items = items[:-1]
|
||||
icons = icons[:-1]
|
||||
self.longest_text = ''
|
||||
for icon, text in zip(icons, items):
|
||||
editor.addItem(QIcon(icon), text)
|
||||
if len(text) > len(self.longest_text):
|
||||
self.longest_text = text
|
||||
return editor
|
||||
|
||||
def get_required_width(self, editor, index, style, fm):
|
||||
srect = style.itemTextRect(fm, editor.geometry(), Qt.AlignLeft, False,
|
||||
self.longest_text + u'M')
|
||||
return srect.width() + editor.iconSize().width()
|
||||
|
||||
def setModelData(self, editor, model, index):
|
||||
val = {0:True, 1:False, 2:None}[editor.currentIndex()]
|
||||
model.setData(index, (val), Qt.EditRole)
|
||||
@ -489,21 +624,6 @@ class CcBoolDelegate(QStyledItemDelegate): # {{{
|
||||
val = 2 if val is None else 1 if not val else 0
|
||||
editor.setCurrentIndex(val)
|
||||
|
||||
def updateEditorGeometry(self, editor, option, index):
|
||||
if editor is None:
|
||||
return
|
||||
opt = QStyleOptionViewItem(option)
|
||||
self.initStyleOption(opt, index)
|
||||
opt.showDecorationSelected = True
|
||||
opt.decorationSize = QSize(0, 0) # We want the editor to cover the decoration
|
||||
style = QApplication.style()
|
||||
geom = style.subElementRect(style.SE_ItemViewItemText, opt, None)
|
||||
|
||||
if editor.layoutDirection() == Qt.RightToLeft:
|
||||
delta = editor.sizeHint().width() - geom.width()
|
||||
if delta > 0:
|
||||
geom.adjust(-delta, 0, 0, 0)
|
||||
editor.setGeometry(geom)
|
||||
# }}}
|
||||
|
||||
class CcTemplateDelegate(QStyledItemDelegate): # {{{
|
||||
|
Loading…
x
Reference in New Issue
Block a user