diff --git a/CMakeLists.txt b/CMakeLists.txt index f13925b6722..bbf34936ec9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -877,6 +877,7 @@ add_library(mixxx-lib STATIC EXCLUDE_FROM_ALL src/library/locationdelegate.cpp src/library/missingtablemodel.cpp src/library/mixxxlibraryfeature.cpp + src/library/multilineeditdelegate.cpp src/library/parser.cpp src/library/parsercsv.cpp src/library/parserm3u.cpp diff --git a/res/skins/Deere/style.qss b/res/skins/Deere/style.qss index f2d06b87589..f421d2f2668 100644 --- a/res/skins/Deere/style.qss +++ b/res/skins/Deere/style.qss @@ -263,15 +263,13 @@ WLibraryTextBrowser { /* Table cell in edit mode */ WLibrary QLineEdit, +WLibrary QPlainTextEdit, WBeatSpinBox, -#LibraryBPMSpinBox { - selection-color: #000; - selection-background-color: #ccc; -} -WLibrary QLineEdit, #LibraryBPMSpinBox { color: #ddd; background-color: #0f0f0f; + selection-color: #000; + selection-background-color: #ccc; border: 1px solid #006596; } @@ -847,6 +845,7 @@ WTrackTableViewHeader::section, WLibraryTextBrowser, WLibraryTextBrowser QMenu, QLineEdit QMenu, +QPlainTextEdit QMenu, WCueMenuPopup, WCueMenuPopup QLabel, WCueMenuPopup QLineEdit, @@ -1961,6 +1960,8 @@ WTrackMenu QMenu::item, WTrackMenu QMenu QCheckBox, QLineEdit QMenu, QLineEdit QMenu::item, +QPlainTextEdit QMenu, +QPlainTextEdit QMenu::item, WCoverArtMenu, WCoverArtMenu::item, WCueMenuPopup, @@ -1983,6 +1984,7 @@ WLibraryTextBrowser QMenu, WTrackMenu, WTrackMenu QMenu, QLineEdit QMenu, +QPlainTextEdit QMenu, WCueMenuPopup, WCoverArtMenu, WEffectSelector QAbstractScrollArea, @@ -2012,6 +2014,7 @@ WSearchLineEdit QAbstractScrollArea, WTrackMenu QMenu QCheckBox:focus, WTrackMenu QMenu QCheckBox:hover, QLineEdit QMenu::item:selected, + QPlainTextEdit QMenu::item:selected, WCoverArtMenu::item:selected, WEffectSelector::item:selected, WEffectSelector::indicator:unchecked:selected, @@ -2196,7 +2199,8 @@ WTrackTableViewHeader QMenu::separator, WTrackMenu::separator, WTrackMenu QMenu::separator, WLibraryTextBrowser QMenu::separator, -QLineEdit QMenu::separator { +QLineEdit QMenu::separator, +QPlainTextEdit QMenu::separator { border-top: 1px solid #999; } @@ -2211,6 +2215,7 @@ WTrackMenu QMenu QCheckBox::indicator { } QLineEdit QMenu::icon:selected, +QPlainTextEdit QMenu::icon:selected, WLibrarySidebar QMenu::indicator:selected, WTrackTableViewHeader QMenu::indicator:selected, WTrackMenu QMenu QCheckBox::indicator:selected { @@ -2223,7 +2228,8 @@ WLibrarySidebar QMenu::item:disabled, WTrackMenu::item:disabled, WTrackMenu QMenu::item:disabled, WTrackMenu QMenu QCheckBox:disabled, -QLineEdit QMenu::item:disabled { +QLineEdit QMenu::item:disabled, +QPlainTextEdit QMenu::item:disabled { color: #555; } WTrackMenu QMenu QCheckBox::indicator:disabled { diff --git a/res/skins/LateNight/style_classic.qss b/res/skins/LateNight/style_classic.qss index 1123047665d..639c6172185 100644 --- a/res/skins/LateNight/style_classic.qss +++ b/res/skins/LateNight/style_classic.qss @@ -1115,6 +1115,7 @@ WTrackMenu, WTrackMenu QMenu, WTrackMenu QMenu QCheckBox, QLineEdit QMenu, +QPlainTextEdit QMenu, WEffectChainPresetButton QMenu, WEffectChainPresetButton QMenu QCheckBox, WCoverArtMenu, @@ -2101,6 +2102,7 @@ WLibrarySidebar { /* Table cell in edit mode */ WLibrary QLineEdit, +WLibrary QPlainTextEdit, #LibraryBPMSpinBox { color: #ddd; background-color: #0f0f0f; @@ -2415,6 +2417,7 @@ WLibraryTextBrowser QMenu, WTrackMenu, WTrackMenu QMenu, QLineEdit QMenu, +QPlainTextEdit QMenu, WCueMenuPopup, WCoverArtMenu, WEffectSelector QAbstractScrollArea, @@ -2447,6 +2450,8 @@ WTrackMenu QMenu::item, WTrackMenu QMenu QCheckBox, QLineEdit QMenu, QLineEdit QMenu::item, +QPlainTextEdit QMenu, +QPlainTextEdit QMenu::item, WCoverArtMenu, WCoverArtMenu::item, WCueMenuPopup, @@ -2484,6 +2489,7 @@ WTrackMenu QMenu QCheckBox:selected, WTrackMenu QMenu QCheckBox:focus, WTrackMenu QMenu QCheckBox:hover, QLineEdit QMenu::item:selected, +QPlainTextEdit QMenu::item:selected, WCoverArtMenu::item:selected, WEffectSelector::item:selected, WEffectChainPresetSelector:item:selected, @@ -2525,7 +2531,8 @@ WLibrarySidebar QMenu::item:disabled, WTrackMenu::item:disabled, WTrackMenu QMenu::item:disabled, WTrackMenu QMenu QCheckBox:disabled, -QLineEdit QMenu::item:disabled { +QLineEdit QMenu::item:disabled, +QPlainTextEdit QMenu::item:disabled { color: #494949; } @@ -2601,6 +2608,7 @@ QLineEdit QMenu::item:disabled { WTrackMenu QMenu::separator, WLibraryTextBrowser QMenu::separator, QLineEdit QMenu::separator, + QPlainTextEdit QMenu::separator, WEffectChainPresetButton QMenu::separator, #SkinSettingsSeparator { border-top: 1px solid #000; diff --git a/res/skins/LateNight/style_palemoon.qss b/res/skins/LateNight/style_palemoon.qss index b3cb97691c2..429e64127e3 100644 --- a/res/skins/LateNight/style_palemoon.qss +++ b/res/skins/LateNight/style_palemoon.qss @@ -1209,6 +1209,7 @@ WTrackMenu QMenu QCheckBox, WEffectChainPresetButton QMenu, WEffectChainPresetButton QMenu QCheckBox, QLineEdit QMenu, +QPlainTextEdit QMenu, WCueMenuPopup, WCueMenuPopup QLabel, #CueLabelEdit, @@ -2527,6 +2528,7 @@ WLibrarySidebar, #SkinSettings, WSearchLineEdit, WLibrary QLineEdit, +WLibrary QPlainTextEdit, #spinBoxTransition, #LibraryBPMSpinBox { background-color: #0f0f0f; @@ -2589,6 +2591,7 @@ WTrackTableView { /* Table cell in edit mode */ WLibrary QLineEdit, +WLibrary QPlainTextEdit, #LibraryBPMSpinBox { color: #ddd; selection-color: #000; @@ -2909,6 +2912,7 @@ WLibraryTextBrowser QMenu, WTrackMenu, WTrackMenu QMenu, QLineEdit QMenu, +QPlainTextEdit QMenu, WCueMenuPopup, WCoverArtMenu, WEffectSelector QAbstractScrollArea, @@ -2939,6 +2943,8 @@ WTrackMenu QMenu::item, WTrackMenu QMenu QCheckBox, QLineEdit QMenu, QLineEdit QMenu::item, +QPlainTextEdit QMenu, +QPlainTextEdit QMenu::item, WCoverArtMenu, WCoverArtMenu::item, WCueMenuPopup, @@ -2972,6 +2978,7 @@ WTrackMenu QMenu QCheckBox:selected, WTrackMenu QMenu QCheckBox:focus, WTrackMenu QMenu QCheckBox:hover, QLineEdit QMenu::item:selected, +QPlainTextEdit QMenu::item:selected, WCoverArtMenu::item:selected, WEffectSelector::item:selected, WEffectChainPresetSelector::item:selected, @@ -3018,7 +3025,8 @@ WLibrarySidebar QMenu::item:disabled, WTrackMenu::item:disabled, WTrackMenu QMenu::item:disabled, WTrackMenu QMenu QCheckBox:disabled, -QLineEdit QMenu::item:disabled { +QLineEdit QMenu::item:disabled, +QPlainTextEdit QMenu::item:disabled { color: #494949; } @@ -3029,6 +3037,7 @@ QLineEdit QMenu::item:disabled { WTrackMenu QMenu::separator, WLibraryTextBrowser QMenu::separator, QLineEdit QMenu::separator, + QPlainTextEdit QMenu::separator, WEffectChainPresetButton QMenu::separator, #SkinSettingsSeparator { border-top: 1px solid #000; diff --git a/res/skins/Shade/style.qss b/res/skins/Shade/style.qss index 0f6f4c3c636..6d05e7c41ae 100644 --- a/res/skins/Shade/style.qss +++ b/res/skins/Shade/style.qss @@ -36,6 +36,7 @@ WLibrarySidebar QMenu, WLibraryTextBrowser, WLibraryTextBrowser QMenu, QLineEdit QMenu, +QPlainTextEdit QMenu, WCueMenuPopup, WCueMenuPopup QMenu, WCueMenuPopup QLabel, @@ -109,6 +110,8 @@ WTrackMenu QMenu::item, WTrackMenu QMenu QCheckBox, QLineEdit QMenu, QLineEdit QMenu::item, +QPlainTextEdit QMenu, +QPlainTextEdit QMenu::item, WBeatSpinBox::up-button, WBeatSpinBox::down-button, WCueMenuPopup, @@ -159,6 +162,7 @@ WTrackMenu QMenu QCheckBox:selected, WTrackMenu QMenu QCheckBox:focus, WTrackMenu QMenu QCheckBox:hover, QLineEdit QMenu::item:selected, +QPlainTextEdit QMenu::item:selected, WCoverArtMenu::item:selected, WEffectSelector::item:selected, WEffectSelector::indicator:unchecked:selected, @@ -239,6 +243,7 @@ WLibraryTextBrowser QMenu, WTrackMenu, WTrackMenu QMenu, QLineEdit QMenu, +QPlainTextEdit QMenu, WCueMenuPopup, WCoverArtMenu, WEffectSelector QAbstractScrollArea, @@ -354,7 +359,8 @@ WEffectSelector QAbstractScrollArea, WTrackMenu::item:disabled, WTrackMenu QMenu::item:disabled, WTrackMenu QMenu QCheckBox:disabled, - QLineEdit QMenu::item:disabled { + QLineEdit QMenu::item:disabled, + QPlainTextEdit QMenu::item:disabled { color: #666; } WTrackMenu QMenu QCheckBox::indicator:disabled { @@ -367,7 +373,8 @@ WEffectSelector QAbstractScrollArea, WTrackMenu::separator, WTrackMenu QMenu::separator, WLibraryTextBrowser QMenu::separator, - QLineEdit QMenu::separator { + QLineEdit QMenu::separator, + QPlainTextEdit QMenu::separator { border-top: 1px solid #71777a; } @@ -498,6 +505,7 @@ WTrackTableView { /* Table cell in edit mode */ WLibrary QLineEdit, + WLibrary QPlainTextEdit, #LibraryBPMSpinBox { color: #ddd; background-color: #0f0f0f; diff --git a/res/skins/Shade/style_dark.qss b/res/skins/Shade/style_dark.qss index e607f6c9023..edb6d1ac61a 100644 --- a/res/skins/Shade/style_dark.qss +++ b/res/skins/Shade/style_dark.qss @@ -16,6 +16,8 @@ WTrackMenu QMenu::item, WTrackMenu QMenu QCheckBox, QLineEdit QMenu, QLineEdit QMenu::item, +QPlainTextEdit QMenu, +QPlainTextEdit QMenu::item, WBeatSpinBox::up-button, WBeatSpinBox::down-button, WCueMenuPopup, @@ -61,6 +63,7 @@ WTrackMenu QMenu QCheckBox:selected, WTrackMenu QMenu QCheckBox:focus, WTrackMenu QMenu QCheckBox:hover, QLineEdit QMenu::item:selected, +QPlainTextEdit QMenu::item:selected, WCoverArtMenu::item:selected, WEffectSelector::item:selected, WEffectSelector::indicator:unchecked:selected, @@ -84,7 +87,8 @@ WEffectSelector::indicator:unchecked:selected, WTrackMenu::separator, WTrackMenu QMenu::separator, WLibraryTextBrowser QMenu::separator, - QLineEdit QMenu::separator { + QLineEdit QMenu::separator, + QPlainTextEdit QMenu::separator { border-top: 1px solid #3F3041; } /* checked checkbox */ @@ -108,7 +112,8 @@ WEffectSelector::indicator:unchecked:selected, WTrackMenu::item:disabled, WTrackMenu QMenu::item:disabled, WTrackMenu QMenu QCheckBox:disabled, - QLineEdit QMenu::item:disabled { + QLineEdit QMenu::item:disabled, + QPlainTextEdit QMenu::item:disabled { border-color: #444; color: #444; } @@ -188,14 +193,12 @@ WTrackTableView { /* Table cell in edit mode */ WLibrary QLineEdit, + WLibrary QPlainTextEdit, #LibraryBPMSpinBox { color: #ddd; background-color: #0f0f0f; selection-color: #000; selection-background-color: #aaa; - } - WLibrary QLineEdit, - #LibraryBPMSpinBox { border: 1px solid #666; } #LibraryBPMSpinBox::up-button, diff --git a/res/skins/Shade/style_summer_sunset.qss b/res/skins/Shade/style_summer_sunset.qss index 3a4d19d121d..b6d449a02d8 100644 --- a/res/skins/Shade/style_summer_sunset.qss +++ b/res/skins/Shade/style_summer_sunset.qss @@ -16,6 +16,8 @@ WTrackMenu QMenu::item, WTrackMenu QMenu QCheckBox, QLineEdit QMenu, QLineEdit QMenu::item, +QPlainTextEdit QMenu, +QPlainTextEdit QMenu::item, WBeatSpinBox::up-button, WBeatSpinBox::down-button, WCueMenuPopup, @@ -50,7 +52,8 @@ WBeatSpinBox, WTrackMenu::separator, WTrackMenu QMenu::separator, WLibraryTextBrowser QMenu::separator, - QLineEdit QMenu::separator { + QLineEdit QMenu::separator, + QPlainTextEdit QMenu::separator { border-top: 1px solid #222; } #MainMenu::item:selected, @@ -70,6 +73,7 @@ WTrackMenu QMenu QCheckBox:selected, WTrackMenu QMenu QCheckBox:focus, WTrackMenu QMenu QCheckBox:hover, QLineEdit QMenu::item:selected, +QPlainTextEdit QMenu::item:selected, WCoverArtMenu::item:selected, WEffectSelector::item:selected, WEffectSelector::indicator:unchecked:selected, diff --git a/res/skins/Tango/style.qss b/res/skins/Tango/style.qss index 2570e895eb5..0f7ac4a8626 100644 --- a/res/skins/Tango/style.qss +++ b/res/skins/Tango/style.qss @@ -49,6 +49,7 @@ WTrackMenu, WTrackMenu QMenu, WTrackMenu QMenu QCheckBox, QLineEdit QMenu, +QPlainTextEdit QMenu, WCoverArtMenu, WLibrarySidebar QMenu, WLibraryTextBrowser QMenu, @@ -2296,6 +2297,8 @@ WTrackMenu QMenu::item, WTrackMenu QMenu QCheckBox, QLineEdit QMenu, QLineEdit QMenu::item, +QPlainTextEdit QMenu, +QPlainTextEdit QMenu::item, WCoverArtMenu, WCoverArtMenu::item, WCueMenuPopup, @@ -2326,6 +2329,7 @@ WLibraryTextBrowser QMenu, WTrackMenu, WTrackMenu QMenu, QLineEdit QMenu, +QPlainTextEdit QMenu, WCueMenuPopup, WCoverArtMenu, WEffectSelector QAbstractScrollArea, @@ -2359,6 +2363,7 @@ WTrackMenu QMenu QCheckBox:selected, WTrackMenu QMenu QCheckBox:focus, WTrackMenu QMenu QCheckBox:hover, QLineEdit QMenu::item:selected, +QPlainTextEdit QMenu::item:selected, WCoverArtMenu::item:selected, WEffectSelector::item:selected, WEffectChainPresetSelector::item:selected, @@ -2381,7 +2386,8 @@ WLibrarySidebar QMenu::item:disabled, WTrackMenu::item:disabled, WTrackMenu QMenu::item:disabled, WTrackMenu QMenu QCheckBox:disabled, -QLineEdit QMenu::item:disabled { +QLineEdit QMenu::item:disabled, +QPlainTextEdit QMenu::item:disabled { color: #555; } @@ -2392,7 +2398,8 @@ QLineEdit QMenu::item:disabled { WTrackMenu::separator, WTrackMenu QMenu::separator, WLibraryTextBrowser QMenu::separator, - QLineEdit QMenu::separator { + QLineEdit QMenu::separator, + QPlainTextEdit QMenu::separator { border-top: 1px solid #0a0a0a; } @@ -2578,6 +2585,7 @@ WTrackTableView { /* Table cell in edit mode */ WLibrary QLineEdit, + WLibrary QPlainTextEdit, #LibraryBPMSpinBox { color: #ddd; background-color: #0f0f0f; diff --git a/src/library/basetracktablemodel.cpp b/src/library/basetracktablemodel.cpp index e3414e29338..9ef7aca66ca 100644 --- a/src/library/basetracktablemodel.cpp +++ b/src/library/basetracktablemodel.cpp @@ -9,6 +9,7 @@ #include "library/coverartdelegate.h" #include "library/dao/trackschema.h" #include "library/locationdelegate.h" +#include "library/multilineeditdelegate.h" #include "library/previewbuttondelegate.h" #include "library/stardelegate.h" #include "library/starrating.h" @@ -394,6 +395,8 @@ QAbstractItemDelegate* BaseTrackTableModel::delegateForColumn( } else if (PlayerManager::numPreviewDecks() > 0 && index == fieldIndex(ColumnCache::COLUMN_LIBRARYTABLE_PREVIEW)) { return new PreviewButtonDelegate(pTableView, index); + } else if (index == fieldIndex(ColumnCache::COLUMN_LIBRARYTABLE_COMMENT)) { + return new MultiLineEditDelegate(pTableView); } else if (index == fieldIndex(ColumnCache::COLUMN_TRACKLOCATIONSTABLE_LOCATION)) { return new LocationDelegate(pTableView); } else if (index == fieldIndex(ColumnCache::COLUMN_LIBRARYTABLE_COLOR)) { diff --git a/src/library/bpmdelegate.h b/src/library/bpmdelegate.h index 02db4ad09b5..9d543a22c0b 100644 --- a/src/library/bpmdelegate.h +++ b/src/library/bpmdelegate.h @@ -13,8 +13,9 @@ class BPMDelegate : public TableItemDelegate { explicit BPMDelegate(QTableView* pTableView); virtual ~BPMDelegate(); - void paintItem(QPainter* painter, const QStyleOptionViewItem& option, - const QModelIndex& index) const; + void paintItem(QPainter* painter, + const QStyleOptionViewItem& option, + const QModelIndex& index) const override; private: QTableView* m_pTableView; diff --git a/src/library/multilineeditdelegate.cpp b/src/library/multilineeditdelegate.cpp new file mode 100644 index 00000000000..f6f9611cef3 --- /dev/null +++ b/src/library/multilineeditdelegate.cpp @@ -0,0 +1,192 @@ +#include "library/multilineeditdelegate.h" + +#include +#include +#include +#include + +#include "moc_multilineeditdelegate.cpp" + +MultiLineEditor::MultiLineEditor(QWidget* pParent, + QTableView* pTableView, + const QModelIndex& index) + : QPlainTextEdit(pParent), + m_pTableView(pTableView), + m_index(index) { + setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); + // Disable line wrap for a predictable view (like QLineEdit). Horizontal + // scrollbars will show up automatically. + setLineWrapMode(QPlainTextEdit::NoWrap); + // Remove content offset, most notable with one-liners + document()->setDocumentMargin(0); + setContentsMargins(0, 0, 0, 0); + setCenterOnScroll(false); + // Paint the entire rectangle, i.e. expand document background in order to + // cover all underlying index text. Seems to be required for one-liners on macOS. + setBackgroundVisible(true); + // Add event filter to catch right-clicks and key presses, see eventFilter() + installEventFilter(this); + + // Adjust size to fit content and maybe shift vertically to fit into the + // library view. documentSizeChanged() is emitted when the layout has been + // adjusted according to text changes, incl. initial fill. + auto* pDocLayout = document()->documentLayout(); + connect(pDocLayout, + &QAbstractTextDocumentLayout::documentSizeChanged, + this, + &MultiLineEditor::adjustSize); + + // Also adjust size if the table is scrolled: maybe we can now expand horizontally + // to show all content, or need to shift the editor vertically. + connect(m_pTableView->horizontalScrollBar(), + &QScrollBar::valueChanged, + this, + [this]() { + adjustSize(document()->size()); + }); + connect(m_pTableView->verticalScrollBar(), + &QScrollBar::valueChanged, + this, + [this]() { + adjustSize(document()->size()); + }); +}; + +bool MultiLineEditor::eventFilter(QObject* obj, QEvent* event) { + if (event->type() == QEvent::MouseButtonPress) { + // Work around a strange quirk: right-clicks outside the rectangle of the + // underlying table index are not triggering the document context menu. + // Simply returning true fixes it. + QMouseEvent* me = static_cast(event); + if (me->button() == Qt::RightButton && rect().contains(me->pos(), false)) { + return true; + } + } else if (event->type() == QEvent::KeyPress) { + // Finish editing with Return key like in QLineEdit + QKeyEvent* ke = static_cast(event); + if ((ke->key() == Qt::Key_Return || ke->key() == Qt::Key_Enter) && + ke->modifiers().testFlag(Qt::NoModifier)) { + emit editingFinished(); + return false; + } + } + return QPlainTextEdit::eventFilter(obj, event); +} + +// The editor can grow vertically and to the right to show all content and avoid +// scrollbars as long as possible. It may be shifted up/down if it would exceed +// the table view. Size and position are adjusted if the table view is scrolled. +// The only constraints are: +// * minimum rectangle is the index rectangle +// * it's Left edge is always the left edge of the index +// * the editor must always include the index rectangle, hence it may be scrolled +// out of view along with the table content (it remains open) +void MultiLineEditor::adjustSize(const QSizeF size) { + // Compared to QTextEdit, size.height() is the paragraph/line count (Qt speak: blocks) + int lines = static_cast(size.height()); + int docW = static_cast(std::ceil(size.width())); + const QRect indexRect = m_pTableView->visualRect(m_index); + const QRect tableRect = m_pTableView->viewport()->rect(); + + // Remove the scrollbars if content is just one line to emulate QLineEdit + // appearance, else enable auto mode. + Qt::ScrollBarPolicy pol(lines > 1 ? Qt::ScrollBarAsNeeded : Qt::ScrollBarAlwaysOff); + setVerticalScrollBarPolicy(pol); + setHorizontalScrollBarPolicy(pol); + + // If we have more than one line, add extra bottom margin so the horizontal + // scrollbar that may pop up will not obstruct the last line (which also avoids the + // vertical scrollbar as long as possible) + lines += lines > 1 ? 1 : 0; + + // Height + // Don't let the editor shrink smaller than the height of the table index. + int optH = lines * QFontMetrics(document()->defaultFont()).height() + frameWidth() * 2; + int newH = std::max(optH, indexRect.height()); + // If it's just one line center the text vertically like in QLineEdit. + int vMargin = (indexRect.height() - optH) / 2; + if (lines == 1 && vMargin > 0) { + setContentsMargins(0, vMargin, 0, vMargin); // left/right > 0 are not applied + } else { // Reset if lines were added + setContentsMargins(0, 0, 0, 0); + } + // Avoid clipping if the editor overflows the table view at the bottom + int newY = indexRect.y(); + bool vScrollbarVisible = false; + int tableH = tableRect.height(); + if (newY + newH > tableH) { + // First, try to shift the editor up + if (newY >= 0) { + newY = std::max(0, tableH - newH); // Keep top edge inside table view + } + } + if (newY + newH > tableH) { + // If that doesn't suffice reduce height + newH = tableH - newY; + vScrollbarVisible = true; + } + // The editor must always include the index rectangle + if (newY + newH < indexRect.bottom()) { + newY = indexRect.bottom() - newH; + } + + // Width + // Let the editor expand horizontally like QLineEdit (max. to right table edge, + // to not scroll the table horizontally if the cursor is moved), but don't + // shrink smaller than the index width. + int vScrollW = vScrollbarVisible ? verticalScrollBar()->width() : 0; + // TODO For some reason the width isn't enough for all content, h-scrollbars show up + // BUT after v- or h-scroll, the document is suddenly 8px wider, no idea where those + // are coming from. Adding these magic 8px fixes it. + int optW = docW + frameWidth() * 2 + 8 + vScrollW; + int newW = std::max(indexRect.width(), optW); + int tableW = tableRect.width(); + if (indexRect.x() + newW > tableW) { + newW = std::max(indexRect.width(), tableW - indexRect.x()); + } + +#ifdef __APPLE__ + // macOS' transient (table view) scrollbars are drawn inside the table, hence + // the cover content. Don't let them cover the editor, instead shrink or shift + // it as required. + int tableVScrollW = m_pTableView->verticalScrollBar()->width(); + if (tableVScrollW > 0 && (indexRect.x() + newW > tableW - tableVScrollW)) { + newW -= tableVScrollW; + } + int tableHscrollW = m_pTableView->horizontalScrollBar()->height(); + if (tableHscrollW > 0 && newY + newH > tableH - tableHscrollW) { + if (newY >= tableHscrollW) { + // shift it up + newY -= tableHscrollW; + } else { + // reduce height + newH -= tableHscrollW; + } + } +#endif + + setGeometry(QRect(indexRect.x(), newY, newW, newH)); +} + +MultiLineEditDelegate::MultiLineEditDelegate(QTableView* pTableView) + : TableItemDelegate(pTableView) { +} + +QWidget* MultiLineEditDelegate::createEditor(QWidget* pParent, + const QStyleOptionViewItem& option, + const QModelIndex& index) const { + Q_UNUSED(index); + auto* pEditor = new MultiLineEditor(pParent, m_pTableView, index); + // Also emitted when pressing Return key, see MultiLineEditor::keyPressEvent() + connect(pEditor, + &MultiLineEditor::editingFinished, + this, + &MultiLineEditDelegate::commitAndCloseEditor); + return pEditor; +} + +void MultiLineEditDelegate::commitAndCloseEditor() { + MultiLineEditor* pEditor = qobject_cast(sender()); + emit commitData(pEditor); + emit closeEditor(pEditor); +} diff --git a/src/library/multilineeditdelegate.h b/src/library/multilineeditdelegate.h new file mode 100644 index 00000000000..47fa22704f9 --- /dev/null +++ b/src/library/multilineeditdelegate.h @@ -0,0 +1,47 @@ +#pragma once + +#include + +#include "library/tableitemdelegate.h" + +/// A QPlainTextEdit to show all content lines in a scrollable view. +/// * finish editing with Return key, like QLineEdit used for other text columns +/// * add new line with Shift+Return +/// Horizontal scrollbar is hidden as long as content is just one line. +/// Note: QTextEdit is no option here since it seems to transform content with +/// line breaks to html doc when committing data. +class MultiLineEditor : public QPlainTextEdit { + Q_OBJECT + public: + MultiLineEditor(QWidget* pParent, + QTableView* pTableView, + const QModelIndex& index); + + bool eventFilter(QObject* obj, QEvent* event) override; + + void adjustSize(const QSizeF size); + + signals: + void editingFinished(); + + private: + QTableView* m_pTableView; + const QModelIndex m_index; +}; + +/// A delegate for text value columns that allows editing content +/// content in a multi-line editor instead of default QLineEdit +class MultiLineEditDelegate : public TableItemDelegate { + Q_OBJECT + public: + explicit MultiLineEditDelegate(QTableView* pTrackTable); + ~MultiLineEditDelegate() override = default; + + // called when the user starts editing an item + QWidget* createEditor(QWidget* parent, + const QStyleOptionViewItem& option, + const QModelIndex& index) const override; + + private slots: + void commitAndCloseEditor(); +}; diff --git a/src/library/tableitemdelegate.cpp b/src/library/tableitemdelegate.cpp index 48f8d70f258..83b2ccdfed1 100644 --- a/src/library/tableitemdelegate.cpp +++ b/src/library/tableitemdelegate.cpp @@ -73,3 +73,10 @@ void TableItemDelegate::paintItemBackground( const auto bgBrush = qvariant_cast(bgValue); painter->fillRect(option.rect, bgBrush); } + +void TableItemDelegate::paintItem( + QPainter* painter, + const QStyleOptionViewItem& option, + const QModelIndex& index) const { + QStyledItemDelegate::paint(painter, option, index); +} diff --git a/src/library/tableitemdelegate.h b/src/library/tableitemdelegate.h index a9cb54d7969..058df3f9875 100644 --- a/src/library/tableitemdelegate.h +++ b/src/library/tableitemdelegate.h @@ -18,7 +18,7 @@ class TableItemDelegate : public QStyledItemDelegate { virtual void paintItem( QPainter* painter, const QStyleOptionViewItem& option, - const QModelIndex& index) const = 0; + const QModelIndex& index) const; protected: static void paintItemBackground( @@ -29,7 +29,5 @@ class TableItemDelegate : public QStyledItemDelegate { int columnWidth(const QModelIndex &index) const; QColor m_pFocusBorderColor; - - private: QTableView* m_pTableView; };