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:
Kovid Goyal 2015-03-16 17:24:44 +05:30
commit 87832d2275

View File

@ -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): # {{{