diff --git a/.gitignore b/.gitignore index 8f82fb7..07d3264 100644 --- a/.gitignore +++ b/.gitignore @@ -12,5 +12,4 @@ pglabAll.pro.user.4.8-pre1 *.user /pglabAll.pro.user* /public/ -/Testing/Temporary -.junie/ +/Testing/Temporary \ No newline at end of file diff --git a/pglab/CMakeLists.txt b/pglab/CMakeLists.txt index 46ace2b..8f3e85d 100644 --- a/pglab/CMakeLists.txt +++ b/pglab/CMakeLists.txt @@ -31,7 +31,6 @@ set(PGLAB_SOURCES crud/CrudModel.cpp crud/CrudTab.cpp crud/DataGridWidget.cpp - crud/RecordPanel.cpp querytool/QueryExplainModel.cpp querytool/QueryParamListController.cpp querytool/QueryTool.cpp @@ -72,6 +71,8 @@ set(PGLAB_SOURCES EditTableWidget.cpp EditColumnTableModel.cpp DatabaseWindow.cpp + widgets/SingleRecordModel.cpp + widgets/SingleRecordWidget.cpp ) set(PGLAB_FORMS @@ -83,6 +84,7 @@ set(PGLAB_FORMS ProcessStdioWidget.ui crud/CrudTab.ui CodeGenerator.ui + widgets/SingleRecordWidget.ui ) add_executable(pglab diff --git a/pglab/crud/CrudModel.h b/pglab/crud/CrudModel.h index 3fbe91e..f2c8f78 100644 --- a/pglab/crud/CrudModel.h +++ b/pglab/crud/CrudModel.h @@ -31,9 +31,6 @@ public: /// Show an arbitrary query result, cells are editable where the result maps them to a table with a primary key void setResult(std::shared_ptr db, std::shared_ptr result); - /// Number of grid columns in front of the result columns (the row number column) - static constexpr int PreColumnCount = 1; - QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; @@ -65,6 +62,10 @@ signals: private: + /// Number of grid columns in front of the result columns (the row number column) + static constexpr int PreColumnCount = 1; + + class ColumnSort { public: enum Direction { Ascending, Descending }; diff --git a/pglab/crud/DataGridWidget.cpp b/pglab/crud/DataGridWidget.cpp index bd9e2ae..65708f7 100644 --- a/pglab/crud/DataGridWidget.cpp +++ b/pglab/crud/DataGridWidget.cpp @@ -1,13 +1,10 @@ #include "DataGridWidget.h" #include "CrudModel.h" #include "CustomDataRole.h" -#include "RecordPanel.h" #include "ResultTableModelUtil.h" #include "util/PgLabItemDelegate.h" #include -#include #include -#include #include #include #include @@ -39,28 +36,12 @@ DataGridWidget::DataGridWidget(QWidget *parent) m_toolbar->addAction(m_actRevert); connect(m_actRevert, &QAction::triggered, this, &DataGridWidget::onRevert); - m_actRecord = new QAction(tr("Record"), this); - m_actRecord->setCheckable(true); - m_actRecord->setChecked(true); - m_actRecord->setToolTip(tr("Show the values of the current row next to the grid")); - m_toolbar->addAction(m_actRecord); - m_view = new QTableView(this); + layout->addWidget(m_view); + SetTableViewDefault(m_view); auto delegate = new PgLabItemDelegate(m_view); m_view->setItemDelegate(delegate); - - m_splitter = new QSplitter(Qt::Horizontal, this); - layout->addWidget(m_splitter); - m_splitter->addWidget(m_view); - - m_recordPanel = new RecordPanel(this); - m_recordPanel->setFirstDataColumn(CrudModel::PreColumnCount); - m_splitter->addWidget(m_recordPanel); - m_splitter->setStretchFactor(0, 1); - m_splitter->setStretchFactor(1, 0); - - connect(m_actRecord, &QAction::toggled, m_recordPanel, &QWidget::setVisible); } void DataGridWidget::setSourceModel(QAbstractItemModel *model) @@ -70,18 +51,13 @@ void DataGridWidget::setSourceModel(QAbstractItemModel *model) m_proxy->setSortRole(CustomSortRole); } disconnect(m_pendingChangesConnection); - disconnect(m_currentRowConnection); m_proxy->setSourceModel(model); m_view->setModel(m_proxy); m_view->setSortingEnabled(true); m_proxy->sort(0, Qt::AscendingOrder); - m_recordPanel->setModel(m_proxy); - m_currentRowConnection = connect(m_view->selectionModel(), &QItemSelectionModel::currentRowChanged, this, &DataGridWidget::onCurrentRowChanged); - auto crud = qobject_cast(model); - m_actSave->setVisible(crud != nullptr); - m_actRevert->setVisible(crud != nullptr); + m_toolbar->setVisible(crud != nullptr); if (crud) { m_pendingChangesConnection = connect(crud, &CrudModel::pendingChangesChanged, this, &DataGridWidget::onPendingChangesChanged); onPendingChangesChanged(crud->hasPendingChanges()); @@ -91,11 +67,6 @@ void DataGridWidget::setSourceModel(QAbstractItemModel *model) } } -void DataGridWidget::onCurrentRowChanged(const QModelIndex ¤t) -{ - m_recordPanel->setCurrentRow(current.row()); -} - void DataGridWidget::onPendingChangesChanged(bool hasChanges) { m_actSave->setEnabled(hasChanges); diff --git a/pglab/crud/DataGridWidget.h b/pglab/crud/DataGridWidget.h index bfcda78..d605053 100644 --- a/pglab/crud/DataGridWidget.h +++ b/pglab/crud/DataGridWidget.h @@ -5,12 +5,9 @@ class QAbstractItemModel; class QAction; -class QModelIndex; class QSortFilterProxyModel; -class QSplitter; class QTableView; class QToolBar; -class RecordPanel; /** Sortable and editable grid shared by the CRUD tab and the query result tab. */ @@ -29,7 +26,6 @@ private slots: void onPendingChangesChanged(bool hasChanges); void onSave(); void onRevert(); - void onCurrentRowChanged(const QModelIndex ¤t); private: /// Closes an open cell editor so its value is written to the model before saving or reverting @@ -37,14 +33,10 @@ private: QTableView *m_view = nullptr; QSortFilterProxyModel *m_proxy = nullptr; - QSplitter *m_splitter = nullptr; - RecordPanel *m_recordPanel = nullptr; QToolBar *m_toolbar = nullptr; QAction *m_actSave = nullptr; QAction *m_actRevert = nullptr; - QAction *m_actRecord = nullptr; QMetaObject::Connection m_pendingChangesConnection; - QMetaObject::Connection m_currentRowConnection; }; #endif // DATAGRIDWIDGET_H diff --git a/pglab/crud/RecordPanel.cpp b/pglab/crud/RecordPanel.cpp deleted file mode 100644 index 8183771..0000000 --- a/pglab/crud/RecordPanel.cpp +++ /dev/null @@ -1,268 +0,0 @@ -#include "RecordPanel.h" -#include "CustomDataRole.h" -#include "Pgsql_oids.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace { - -constexpr int CollapsedTextLines = 2; -constexpr int ExpandedTextLines = 10; - -bool isLongTextType(Oid oid) -{ - switch (oid) { - case Pgsql::text_oid: - case Pgsql::varchar_oid: - case Pgsql::bpchar_oid: - case Pgsql::json_oid: - case Pgsql::jsonb_oid: - case Pgsql::xml_oid: - return true; - default: - return false; - } -} - -int textEditHeight(const QPlainTextEdit *edit, int lines) -{ - return edit->fontMetrics().lineSpacing() * lines + 2 * edit->frameWidth() + 6; -} - -QString editorText(const QWidget *editor) -{ - if (auto line = qobject_cast(editor)) - return line->text(); - if (auto plain = qobject_cast(editor)) - return plain->toPlainText(); - return {}; -} - -} // namespace - -RecordPanel::RecordPanel(QWidget *parent) - : QScrollArea(parent) -{ - setWidgetResizable(true); -} - -void RecordPanel::setModel(QAbstractItemModel *model) -{ - if (m_model) - m_model->disconnect(this); - - m_model = model; - if (m_model) { - connect(m_model, &QAbstractItemModel::dataChanged, this, &RecordPanel::refresh); - connect(m_model, &QAbstractItemModel::layoutChanged, this, &RecordPanel::refresh); - connect(m_model, &QAbstractItemModel::rowsInserted, this, &RecordPanel::refresh); - connect(m_model, &QAbstractItemModel::rowsRemoved, this, &RecordPanel::refresh); - connect(m_model, &QAbstractItemModel::modelReset, this, &RecordPanel::onModelReset); - } - onModelReset(); -} - -void RecordPanel::setFirstDataColumn(int column) -{ - m_firstDataColumn = column; - clearFields(); - refresh(); -} - -void RecordPanel::setCurrentRow(int row) -{ - if (m_model && row >= 0 && row < m_model->rowCount()) - m_rowIndex = QPersistentModelIndex(m_model->index(row, 0)); - else - m_rowIndex = QPersistentModelIndex(); - refresh(); -} - -void RecordPanel::onModelReset() -{ - m_rowIndex = QPersistentModelIndex(); - refresh(); -} - -void RecordPanel::refresh() -{ - if (!m_model || !m_rowIndex.isValid()) { - clearFields(); - return; - } - - int columns = m_model->columnCount() - m_firstDataColumn; - if (columns < 0) - columns = 0; - if (m_fields.size() != static_cast(columns)) - rebuild(); - - for (auto &field : m_fields) { - QModelIndex index = m_model->index(m_rowIndex.row(), field.column); - bool editable = m_model->flags(index).testFlag(Qt::ItemIsEditable); - QVariant value = index.data(Qt::EditRole); - QString text; - if (value.isValid() && !value.isNull()) { - text = value.toString(); - // An empty string is shown as '' so it can be told apart from NULL, the placeholder shows NULL - if (text.isEmpty()) - text = QStringLiteral("''"); - } - showValue(field, text, editable); - } -} - -void RecordPanel::clearFields() -{ - m_fields.clear(); - if (QWidget *old = takeWidget()) - old->deleteLater(); -} - -void RecordPanel::rebuild() -{ - clearFields(); - - auto content = new QWidget; - auto layout = new QFormLayout(content); - layout->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow); - - int row = m_rowIndex.row(); - for (int column = m_firstDataColumn; column < m_model->columnCount(); ++column) { - QString header = m_model->headerData(column, Qt::Horizontal, Qt::DisplayRole).toString(); - int newline = header.indexOf('\n'); - QString name = newline < 0 ? header : header.left(newline); - QString typeName = newline < 0 ? QString() : header.mid(newline + 1); - - auto caption = new QWidget(content); - auto captionLayout = new QVBoxLayout(caption); - captionLayout->setContentsMargins(0, 0, 0, 0); - auto nameLabel = new QLabel(name, caption); - QFont nameFont = nameLabel->font(); - nameFont.setBold(true); - nameLabel->setFont(nameFont); - auto typeLabel = new QLabel(typeName, caption); - captionLayout->addWidget(nameLabel); - captionLayout->addWidget(typeLabel); - - Oid oid = m_model->index(row, column).data(CustomDataTypeRole).toUInt(); - FieldRow field; - field.column = column; - QWidget *fieldWidget = nullptr; - - if (isLongTextType(oid)) { - auto container = new QWidget(content); - auto containerLayout = new QVBoxLayout(container); - containerLayout->setContentsMargins(0, 0, 0, 0); - - auto edit = new QPlainTextEdit(container); - edit->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont)); - edit->setPlaceholderText(tr("null")); - edit->setFixedHeight(textEditHeight(edit, CollapsedTextLines)); - edit->installEventFilter(this); - - auto expand = new QToolButton(container); - expand->setCheckable(true); - expand->setArrowType(Qt::DownArrow); - expand->setToolTip(tr("Expand or collapse the value")); - connect(expand, &QToolButton::toggled, edit, [edit, expand](bool expanded) { - edit->setFixedHeight(textEditHeight(edit, expanded ? ExpandedTextLines : CollapsedTextLines)); - expand->setArrowType(expanded ? Qt::UpArrow : Qt::DownArrow); - }); - - auto buttonRow = new QHBoxLayout; - buttonRow->addStretch(); - buttonRow->addWidget(expand); - containerLayout->addWidget(edit); - containerLayout->addLayout(buttonRow); - - field.editor = edit; - field.expandButton = expand; - fieldWidget = container; - } - else { - auto edit = new QLineEdit(content); - edit->setPlaceholderText(tr("null")); - connect(edit, &QLineEdit::editingFinished, this, [this, edit]() { commitEditor(edit); }); - - field.editor = edit; - fieldWidget = edit; - } - - layout->addRow(caption, fieldWidget); - m_fields.push_back(field); - } - - setWidget(content); -} - -void RecordPanel::showValue(FieldRow &field, const QString &text, bool editable) -{ - QWidget *editor = field.editor; - if (auto line = qobject_cast(editor)) - line->setReadOnly(!editable); - else if (auto plain = qobject_cast(editor)) - plain->setReadOnly(!editable); - - // Do not replace the text while the user is typing in it - if (editor->hasFocus()) - return; - - if (auto line = qobject_cast(editor)) - line->setText(text); - else if (auto plain = qobject_cast(editor)) - plain->setPlainText(text); - field.original = text; -} - -void RecordPanel::commitEditor(QWidget *editor) -{ - if (m_updating || !m_model || !m_rowIndex.isValid()) - return; - - FieldRow *field = findField(editor); - if (!field) - return; - - QString text = editorText(editor); - if (text == field->original) - return; - - QModelIndex index = m_model->index(m_rowIndex.row(), field->column); - if (!m_model->flags(index).testFlag(Qt::ItemIsEditable)) - return; - - m_updating = true; - m_model->setData(index, text, Qt::EditRole); - m_updating = false; - - // The model may have rebuilt the fields while storing the value, so look the field up again - if (FieldRow *updated = findField(editor)) - updated->original = text; -} - -RecordPanel::FieldRow *RecordPanel::findField(QWidget *editor) -{ - for (auto &field : m_fields) { - if (field.editor == editor) - return &field; - } - return nullptr; -} - -bool RecordPanel::eventFilter(QObject *watched, QEvent *event) -{ - if (event->type() == QEvent::FocusOut) { - if (auto edit = qobject_cast(watched)) - commitEditor(edit); - } - return QScrollArea::eventFilter(watched, event); -} diff --git a/pglab/crud/RecordPanel.h b/pglab/crud/RecordPanel.h deleted file mode 100644 index 463038d..0000000 --- a/pglab/crud/RecordPanel.h +++ /dev/null @@ -1,55 +0,0 @@ -#ifndef RECORDPANEL_H -#define RECORDPANEL_H - -#include -#include -#include - -class QAbstractItemModel; -class QEvent; -class QToolButton; - -/** Shows the values of one row of a model as a scrollable list, one line per column. - * Values are edited through the model so the pending change logic of the model is reused. - */ -class RecordPanel : public QScrollArea { - Q_OBJECT -public: - explicit RecordPanel(QWidget *parent = nullptr); - - /// Sets the model to show, the columns from firstDataColumn on are shown - void setModel(QAbstractItemModel *model); - /// Columns before this one are skipped, e.g. the row number column of the CRUD model - void setFirstDataColumn(int column); - /// Shows the values of the given row of the model, -1 shows nothing - void setCurrentRow(int row); - -protected: - bool eventFilter(QObject *watched, QEvent *event) override; - -private slots: - void refresh(); - void onModelReset(); - -private: - struct FieldRow { - int column = -1; - QString original; ///< Text the editor shows, only a different text is written back - QWidget *editor = nullptr; - QToolButton *expandButton = nullptr; - }; - - void clearFields(); - void rebuild(); - void showValue(FieldRow &field, const QString &text, bool editable); - void commitEditor(QWidget *editor); - FieldRow *findField(QWidget *editor); - - QAbstractItemModel *m_model = nullptr; - int m_firstDataColumn = 0; - QPersistentModelIndex m_rowIndex; - std::vector m_fields; - bool m_updating = false; -}; - -#endif // RECORDPANEL_H diff --git a/pglab/tuplesresultwidget.ui b/pglab/tuplesresultwidget.ui index 9acdd3d..b13909e 100644 --- a/pglab/tuplesresultwidget.ui +++ b/pglab/tuplesresultwidget.ui @@ -31,12 +31,6 @@ - - - 0 - 0 - - 4 diff --git a/pglab/widgets/SingleRecordModel.cpp b/pglab/widgets/SingleRecordModel.cpp new file mode 100644 index 0000000..18128e4 --- /dev/null +++ b/pglab/widgets/SingleRecordModel.cpp @@ -0,0 +1,61 @@ +#include "SingleRecordModel.h" +#include "CustomDataRole.h" + +SingleRecordModel::SingleRecordModel(QObject *parent) + : QAbstractTableModel(parent) +{ +} + +QVariant SingleRecordModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + return {}; +} + +int SingleRecordModel::rowCount(const QModelIndex &parent) const +{ + if (parent.isValid()) + return 0; + + return elems.size(); +} + +int SingleRecordModel::columnCount(const QModelIndex &parent) const +{ + if (parent.isValid()) + return 0; + + return ColCount; +} + +QVariant SingleRecordModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid()) + return QVariant(); + + const int row = index.row(); + if (role == Qt::DisplayRole) + { + switch (index.column()) + { + case NameCol: + return elems[row].name; + case ValueCol: + return elems[row].value; + } + } + else if (role == CustomDataTypeRole) + { + switch (index.column()) + { + case NameCol: + return Pgsql::text_oid; + case ValueCol: + return elems[row].oid; + } + } + else if (role == CustomDataMeaningRole) + { + } + + return QVariant(); +} diff --git a/pglab/widgets/SingleRecordModel.h b/pglab/widgets/SingleRecordModel.h new file mode 100644 index 0000000..8107508 --- /dev/null +++ b/pglab/widgets/SingleRecordModel.h @@ -0,0 +1,39 @@ +#pragma once + +#include +#include "Pgsql_oids.h" + +class SingleRecordModel : public QAbstractTableModel +{ + Q_OBJECT + +public: + explicit SingleRecordModel(QObject *parent = nullptr); + + // Header: + QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; + + // Basic functionality: + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + int columnCount(const QModelIndex &parent = QModelIndex()) const override; + + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + +private: + enum Columns { + NameCol, + ValueCol, + + ColCount + }; + + class Elem { + public: + QString name; + QString value; + Oid oid; + }; + using Elems = std::vector; + Elems elems; +}; + diff --git a/pglab/widgets/SingleRecordWidget.cpp b/pglab/widgets/SingleRecordWidget.cpp new file mode 100644 index 0000000..e617918 --- /dev/null +++ b/pglab/widgets/SingleRecordWidget.cpp @@ -0,0 +1,14 @@ +#include "SingleRecordWidget.h" +#include "ui_SingleRecordWidget.h" + +SingleRecordWidget::SingleRecordWidget(QWidget *parent) : + QWidget(parent), + ui(new Ui::SingleRecordWidget) +{ + ui->setupUi(this); +} + +SingleRecordWidget::~SingleRecordWidget() +{ + delete ui; +} diff --git a/pglab/widgets/SingleRecordWidget.h b/pglab/widgets/SingleRecordWidget.h new file mode 100644 index 0000000..29e4033 --- /dev/null +++ b/pglab/widgets/SingleRecordWidget.h @@ -0,0 +1,20 @@ +#pragma once + +#include + +namespace Ui { + class SingleRecordWidget; +} + +class SingleRecordWidget : public QWidget +{ + Q_OBJECT + +public: + explicit SingleRecordWidget(QWidget *parent = nullptr); + ~SingleRecordWidget(); + +private: + Ui::SingleRecordWidget *ui; +}; + diff --git a/pglab/widgets/SingleRecordWidget.ui b/pglab/widgets/SingleRecordWidget.ui new file mode 100644 index 0000000..bfc73fd --- /dev/null +++ b/pglab/widgets/SingleRecordWidget.ui @@ -0,0 +1,31 @@ + + + SingleRecordWidget + + + + 0 + 0 + 500 + 679 + + + + Form + + + + + + + + + + PgLabTableView + QTableView +
util/PgLabTableView.h
+
+
+ + +
diff --git a/releasenotes/notes/datagrid-record-panel-6d2b9e4a1c7f8350.yaml b/releasenotes/notes/datagrid-record-panel-6d2b9e4a1c7f8350.yaml deleted file mode 100644 index da962b9..0000000 --- a/releasenotes/notes/datagrid-record-panel-6d2b9e4a1c7f8350.yaml +++ /dev/null @@ -1,9 +0,0 @@ ---- -features: - - | - Data grids in the table editor and in query results now have a record - panel next to the grid. It shows the column name, type and value of the - current row, long text, varchar, json, jsonb and xml values get an - expandable multi-line box. Values are editable exactly when the grid cell - is editable. The panel can be hidden with the :menuselection:`Record` - button. diff --git a/tests/integration/CMakeLists.txt b/tests/integration/CMakeLists.txt index ec8f25c..eb33c27 100644 --- a/tests/integration/CMakeLists.txt +++ b/tests/integration/CMakeLists.txt @@ -8,7 +8,6 @@ add_executable(IntegrationTests tst_CrudModelBehavior.cpp ${CMAKE_SOURCE_DIR}/pglab/crud/CrudModel.cpp ${CMAKE_SOURCE_DIR}/pglab/crud/DataGridWidget.cpp - ${CMAKE_SOURCE_DIR}/pglab/crud/RecordPanel.cpp ${CMAKE_SOURCE_DIR}/pglab/OpenDatabase.cpp ${CMAKE_SOURCE_DIR}/pglab/util/PgLabItemDelegate.cpp ${CMAKE_SOURCE_DIR}/pglab/util/Colors.cpp diff --git a/tests/pglabtests/CMakeLists.txt b/tests/pglabtests/CMakeLists.txt index 01fe58a..d9f30de 100644 --- a/tests/pglabtests/CMakeLists.txt +++ b/tests/pglabtests/CMakeLists.txt @@ -18,8 +18,6 @@ add_executable(pglabtests tst_TypeMappings.cpp tst_Range.cpp ResultSourceMappingTest.cpp - tst_RecordPanel.cpp - ${CMAKE_SOURCE_DIR}/pglab/crud/RecordPanel.cpp ) target_link_libraries(pglabtests PRIVATE @@ -34,8 +32,6 @@ target_link_libraries(pglabtests PRIVATE target_include_directories(pglabtests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} - ${CMAKE_SOURCE_DIR}/pglab - ${CMAKE_SOURCE_DIR}/pglab/crud ) set_target_properties(pglabtests PROPERTIES diff --git a/tests/pglabtests/main.cpp b/tests/pglabtests/main.cpp index 5755f0d..291bdee 100644 --- a/tests/pglabtests/main.cpp +++ b/tests/pglabtests/main.cpp @@ -1,13 +1,7 @@ -#include - -#include -#include +#include int main(int argc, char *argv[]) { - if (!qEnvironmentVariableIsSet("QT_QPA_PLATFORM")) - qputenv("QT_QPA_PLATFORM", "offscreen"); - QApplication app(argc, argv); ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/tests/pglabtests/tst_RecordPanel.cpp b/tests/pglabtests/tst_RecordPanel.cpp deleted file mode 100644 index 44d7ffc..0000000 --- a/tests/pglabtests/tst_RecordPanel.cpp +++ /dev/null @@ -1,144 +0,0 @@ -#include - -#include -#include -#include -#include -#include - -#include "CustomDataRole.h" -#include "Pgsql_oids.h" -#include "RecordPanel.h" - -namespace { - -/// Column 0 is the row number column, columns 1 to 3 are data columns -/// id (int4, editable), body (text, editable), ro (int4, read-only) -void fillModel(QStandardItemModel &model) -{ - model.setColumnCount(4); - model.setRowCount(1); - model.setHeaderData(0, Qt::Horizontal, QStringLiteral("#"), Qt::DisplayRole); - model.setHeaderData(1, Qt::Horizontal, QStringLiteral("id\nint4"), Qt::DisplayRole); - model.setHeaderData(2, Qt::Horizontal, QStringLiteral("body\ntext"), Qt::DisplayRole); - model.setHeaderData(3, Qt::Horizontal, QStringLiteral("ro\nint4"), Qt::DisplayRole); - model.setData(model.index(0, 1), Pgsql::int4_oid, CustomDataTypeRole); - model.setData(model.index(0, 2), Pgsql::text_oid, CustomDataTypeRole); - model.setData(model.index(0, 3), Pgsql::int4_oid, CustomDataTypeRole); - model.setData(model.index(0, 1), QStringLiteral("1")); - model.setData(model.index(0, 2), QStringLiteral("hello")); - model.setData(model.index(0, 3), QStringLiteral("7")); - model.itemFromIndex(model.index(0, 3))->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); -} - -} - -class RecordPanelTest : public ::testing::Test { -protected: - void SetUp() override - { - model.reset(new QStandardItemModel); - fillModel(*model); - panel.setModel(model.get()); - panel.setFirstDataColumn(1); - panel.setCurrentRow(0); - } - - std::unique_ptr model; - RecordPanel panel; -}; - -TEST_F(RecordPanelTest, OneFieldPerDataColumnSkippingRowNumber) -{ - ASSERT_NE(panel.widget(), nullptr); - EXPECT_EQ(panel.widget()->findChildren().size(), 6); - EXPECT_EQ(panel.widget()->findChildren().size(), 1); - EXPECT_EQ(panel.widget()->findChildren().size(), 2); -} - -TEST_F(RecordPanelTest, ShowsValuesOfCurrentRow) -{ - auto lines = panel.widget()->findChildren(); - auto texts = panel.widget()->findChildren(); - ASSERT_EQ(lines.size(), 2); - ASSERT_EQ(texts.size(), 1); - EXPECT_EQ(lines[0]->text(), QStringLiteral("1")); - EXPECT_EQ(texts[0]->toPlainText(), QStringLiteral("hello")); - EXPECT_EQ(lines[1]->text(), QStringLiteral("7")); -} - -TEST_F(RecordPanelTest, ReadOnlyWhenCellNotEditable) -{ - auto lines = panel.widget()->findChildren(); - ASSERT_EQ(lines.size(), 2); - EXPECT_FALSE(lines[0]->isReadOnly()); - EXPECT_TRUE(lines[1]->isReadOnly()); -} - -TEST_F(RecordPanelTest, NullShowsEmptyTextWithNullPlaceholder) -{ - model->setData(model->index(0, 1), QVariant(), Qt::EditRole); - panel.setCurrentRow(0); - - auto lines = panel.widget()->findChildren(); - ASSERT_EQ(lines.size(), 2); - EXPECT_TRUE(lines[0]->text().isEmpty()); - EXPECT_EQ(lines[0]->placeholderText(), QStringLiteral("null")); -} - -TEST_F(RecordPanelTest, EmptyStringShownAsQuotedEmpty) -{ - model->setData(model->index(0, 2), QString(""), Qt::EditRole); - panel.setCurrentRow(0); - - auto texts = panel.widget()->findChildren(); - ASSERT_EQ(texts.size(), 1); - EXPECT_EQ(texts[0]->toPlainText(), QStringLiteral("''")); -} - -TEST_F(RecordPanelTest, EditingFinishedWritesThroughModel) -{ - auto lines = panel.widget()->findChildren(); - ASSERT_EQ(lines.size(), 2); - lines[0]->setText(QStringLiteral("42")); - emit lines[0]->editingFinished(); - - EXPECT_EQ(model->index(0, 1).data(Qt::EditRole).toString(), QStringLiteral("42")); -} - -TEST_F(RecordPanelTest, DataChangedRefreshesEditor) -{ - model->setData(model->index(0, 1), QStringLiteral("5"), Qt::EditRole); - - auto lines = panel.widget()->findChildren(); - ASSERT_EQ(lines.size(), 2); - EXPECT_EQ(lines[0]->text(), QStringLiteral("5")); -} - -TEST_F(RecordPanelTest, NoCurrentRowShowsNothing) -{ - panel.setCurrentRow(-1); - EXPECT_EQ(panel.widget(), nullptr); -} - -TEST_F(RecordPanelTest, ModelResetClearsPanel) -{ - model->clear(); - EXPECT_EQ(panel.widget(), nullptr); -} - -TEST_F(RecordPanelTest, ExpandButtonGrowsTextBox) -{ - auto texts = panel.widget()->findChildren(); - auto buttons = panel.widget()->findChildren(); - ASSERT_EQ(texts.size(), 1); - ASSERT_EQ(buttons.size(), 1); - - int collapsed = texts[0]->maximumHeight(); - buttons[0]->setChecked(true); - int expanded = texts[0]->maximumHeight(); - EXPECT_GT(expanded, collapsed); - - buttons[0]->setChecked(false); - EXPECT_EQ(texts[0]->maximumHeight(), collapsed); -}