diff --git a/qml/HorizontalHeaderViewDelegate.qml b/qml/HorizontalHeaderViewDelegate.qml new file mode 100644 index 0000000..5f586a2 --- /dev/null +++ b/qml/HorizontalHeaderViewDelegate.qml @@ -0,0 +1,44 @@ +// SPDX-FileCopyrightText: 2025 Martin Leutelt +// SPDX-FileCopyrightText: 2025 basysKom GmbH +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +import QtQuick +import QtQuick.Controls + +Control { + id: container + + required property int column + required property var model + property int sortOrder: Qt.AscendingOrder + + signal clicked(int sortOrder) + + padding: 8 + + background: Rectangle { + color: tapHandler.pressed ? "gray" : "lightgray" + + TapHandler { + id: tapHandler + + onTapped: { + if (container.sortOrder === Qt.AscendingOrder) { + container.sortOrder = Qt.DescendingOrder + } else { + container.sortOrder = Qt.AscendingOrder + } + + container.clicked(container.sortOrder) + } + } + } + + contentItem: Label { + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + elide: Text.ElideRight + text: container.model.display + } +} diff --git a/qml/VerticalHeaderViewDelegate.qml b/qml/VerticalHeaderViewDelegate.qml new file mode 100644 index 0000000..57bb684 --- /dev/null +++ b/qml/VerticalHeaderViewDelegate.qml @@ -0,0 +1,26 @@ +import QtQuick +import QtQuick.Controls + +Control { + id: container + + required property int row + required property var model + + padding: 8 + + background: Rectangle { + color: tapHandler.pressed ? "gray" : "lightgray" + + TapHandler { + id: tapHandler + } + } + + contentItem: Label { + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + elide: Text.ElideRight + text: container.model.display + } +} diff --git a/qml/XMain.qml b/qml/XMain.qml new file mode 100644 index 0000000..f2a0605 --- /dev/null +++ b/qml/XMain.qml @@ -0,0 +1,161 @@ +// SPDX-FileCopyrightText: 2025 Martin Leutelt +// SPDX-FileCopyrightText: 2025 basysKom GmbH +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +pragma ComponentBehavior: Bound + +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts + +ApplicationWindow { + width: 640 + height: 480 + visible: true + title: Application.displayName + + header: ToolBar { + ColumnLayout { + RowLayout { + Label { + text: "Rows" + } + SpinBox { + id: rowSettings + from: 1 + to: 20 + } + + ToolSeparator {} + + Label { + text: "Columns" + } + SpinBox { + id: columnSettings + from: 1 + to: 20 + } + + ToolSeparator {} + + CheckBox { + id: movableColumnsSetting + + text: "Movable columns" + } + + ToolSeparator {} + + CheckBox { + id: resizableColumnsSetting + + text: "Resizable columns" + } + } + + RowLayout { + Label { + text: "Selection" + } + ComboBox { + id: selectionSetting + textRole: "text" + valueRole: "value" + model: [ + { text: "disabled", value: TableView.SelectionDisabled }, + { text: "by cells", value: TableView.SelectCells }, + { text: "by rows", value: TableView.SelectRows }, + { text: "by columns", value: TableView.SelectColumns } + ] + + onCurrentIndexChanged: tableView.selectionModel.clear() + } + Label { + text: "Longpress to start selection, modify selection with CTRL/SHIFT of by mouse" + visible: selectionSetting.currentIndex > 0 + } + } + } + } + + Rectangle { + id: tableBackground + + anchors.fill: parent + + color: Application.styleHints.colorScheme === Qt.Light ? palette.mid : palette.midlight + + HorizontalHeaderView { + id: horizontalHeader + + anchors.left: tableView.left + anchors.top: parent.top + + syncView: tableView + movableColumns: movableColumnsSetting.checked + resizableColumns: resizableColumnsSetting.checked + clip: true + boundsBehavior: tableView.boundsBehavior + + delegate: HorizontalHeaderViewDelegate { + onClicked: (sortOrder) => tableView.model.sort(column, sortOrder) + } + } + + VerticalHeaderView { + id: verticalHeader + + anchors.top: tableView.top + anchors.left: parent.left + + syncView: tableView + clip: true + boundsBehavior: tableView.boundsBehavior + + delegate: VerticalHeaderViewDelegate {} + } + + TableView { + id: tableView + + anchors.left: verticalHeader.right + anchors.top: horizontalHeader.bottom + anchors.right: parent.right + anchors.bottom: parent.bottom + + clip: true + columnSpacing: 1 + rowSpacing: 1 + rowHeightProvider: (row) => 40 + boundsBehavior: TableView.StopAtBounds + selectionModel: ItemSelectionModel {} + selectionBehavior: selectionSetting.currentValue + + model: SortFilterModel { + sourceModel: TableModel { + columns: columnSettings.value + rows: rowSettings.value + + // when adding a new column its width isn't properly applied to the header, so we do that manually + onColumnsInserted: { + if (columns > 1) { + horizontalHeader.setColumnWidth(columns - 1, tableView.implicitColumnWidth(columns - 1)) + } + } + } + } + delegate: TableViewDelegate { + implicitWidth: tableView.width / columnSettings.to + } + + ScrollBar.horizontal: ScrollBar {} + ScrollBar.vertical: ScrollBar {} + } + + SelectionRectangle { + target: tableView + } + } +} diff --git a/qml/dummyview.qml b/qml/dummyview.qml new file mode 100644 index 0000000..95bad0a --- /dev/null +++ b/qml/dummyview.qml @@ -0,0 +1,37 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import QtQuick.Window + +ApplicationWindow +{ + visible: true + width: 600 + height: 400 + title: "TableView mit myChildModel" + + TableView + { + anchors.fill: parent + clip: true + columnSpacing: 1 + rowSpacing: 1 + + model: myChildModel + + delegate: Rectangle + { + implicitWidth: 150 + implicitHeight: 40 + border.color: "#cccccc" + //color: index % 2 === 0 ? "#f9f9f9" : "#e0e0e0" + + Text { + anchors.centerIn: parent + text: display + font.pixelSize: 14 + } + } + + } +} diff --git a/qml/xqtableview.qml b/qml/xqtableview.qml new file mode 100644 index 0000000..01e1d3d --- /dev/null +++ b/qml/xqtableview.qml @@ -0,0 +1,65 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts + +Window +{ + width: 640 + height: 480 + visible: true + title: qsTr("StringListModel") + + TableView + { + id: childTableView + anchors.fill: parent + + model: myChildModel // z. B. QStandardItemModel mit 9 Spalten + + delegate: Rectangle + { + required property string display + + //height: 5 + //width: childTableView.width + color : "blue" + border.color: "#ccc" + width: childTableView.width; + + RowLayout + { + anchors.fill: parent + anchors.margins: 2 + + TextField + { + text : display + font.pixelSize: 10 + Layout.fillWidth: true + + background: Rectangle + { + color : "white" + } + + onEditingFinished: + { + console.log("Editing finished, new text is :"+ text + " at index :" + index) + model.names = text //The roles here are defined in model class + } + } + } + + } + + ScrollBar.horizontal: ScrollBar {} + ScrollBar.vertical: ScrollBar {} + + // // Optional: Spaltenbreiten setzen + // onModelChanged: { + // for (let i = 0; i < model.columns; ++i) + // table.setColumnWidth(i, 100) + // } + } + +} diff --git a/quick/quickview.qml b/quick/quickview.qml deleted file mode 100644 index becad0c..0000000 --- a/quick/quickview.qml +++ /dev/null @@ -1,35 +0,0 @@ -import QtQuick -import QtQuick.Controls -import QtQuick.Layouts - -TableView -{ - id: table - anchors.fill: parent - columnSpacing: 2 - rowSpacing: 2 - model: meinModel // z. B. QStandardItemModel mit 9 Spalten - - delegate: Rectangle - { - - required property string display - width: 100 - height: 20 - border.color: "#ccc" - - Text - { - anchors.centerIn: parent - text: display - font.pixelSize: 10 - } - } - - // // Optional: Spaltenbreiten setzen - // onModelChanged: { - // for (let i = 0; i < model.columns; ++i) - // table.setColumnWidth(i, 100) - // } -} - diff --git a/src/application/xqmainwindow.cpp b/src/application/xqmainwindow.cpp index 4ce91a9..3e3affd 100644 --- a/src/application/xqmainwindow.cpp +++ b/src/application/xqmainwindow.cpp @@ -117,7 +117,7 @@ void XQMainWindow::initMainWindow() // #2. load demo data loadDocument( c_DocumentFileName1 ); - //loadDocumentQML( c_DocumentFileName2 ); + loadDocumentQML( c_DocumentFileName2 ); qDebug() << " --- all here: " << XQNode::s_Count; @@ -131,6 +131,9 @@ void XQMainWindow::initMainWindow() } + + + //! slot für zentrales undo void XQMainWindow::onUndo() @@ -319,8 +322,6 @@ void XQMainWindow::loadDocumentQML( const QString& fileName ) // das als Namen verwendet wird. const QString& fName = contentRoot->friendly_name(); - QStandardItemModel* model = createModel(); - // Ein neues Child-Model erzeugen XQChildModel* childModel = new XQChildModel(this); // die Modelstruktur anlegen @@ -331,13 +332,12 @@ void XQMainWindow::loadDocumentQML( const QString& fileName ) XQQuickWidget* quickChild = new XQQuickWidget(_tabWidget); //quickChild->setResizeMode(QQuickWidget::SizeViewToRootObject); - quickChild->rootContext()->setContextProperty("meinModel", childModel); - quickChild->setSource(QUrl(QStringLiteral("qrc:/quickview.qml"))); - _tabWidget->addTab( quickChild, "Fitze!" ); + quickChild->rootContext()->setContextProperty("myChildModel", childModel); + quickChild->setSource(QUrl("qrc:/xqtableview.qml")); + _tabWidget->addTab( quickChild, "QML:"+fName ); _tabWidget->setCurrentWidget( quickChild ); quickChild->setResizeMode(QQuickWidget::SizeRootObjectToView); - } //! liest eine XML datei namens 'fileName' diff --git a/src/application/xqmainwindow.h b/src/application/xqmainwindow.h index 5737d6d..dbc9d4a 100644 --- a/src/application/xqmainwindow.h +++ b/src/application/xqmainwindow.h @@ -52,10 +52,11 @@ public slots: void onSectionCreated( const XQModelSection& section); void onSectionToggled( const XQModelSection& section ); + static void setupWorkingDir(); protected: - void setupWorkingDir(); + // fixme implement void showDocumnet( const QString& key ){} diff --git a/src/main.cpp b/src/main.cpp index 6189c3b..8c49fb2 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -15,6 +15,13 @@ #include #include #include +#include +#include +#include +#include + +#include +#include #include @@ -49,12 +56,58 @@ who is who: */ +XQChildModel* createChildModel() +{ + XQChildModel* myModel = new XQChildModel(); + + myModel->initModel( c_ChildModelName ); + + XQNodeFactory treeLoader; + // xml daten laden + XQNodePtr rawTree = treeLoader.load_tree( qPrintable(c_DocumentFileName1) ); + // versteckten root node ignorieren + XQNodePtr contentRoot = rawTree->first_child(); + myModel->addModelData( contentRoot->first_child() ); + + return myModel; + +} + + +class DummyModel : public XQChildModel +{ + +public: + + DummyModel() + { + + initModel( c_ChildModelName ); + XQNodeFactory treeLoader; + // xml daten laden + XQNodePtr rawTree = treeLoader.load_tree( qPrintable(c_DocumentFileName1) ); + // versteckten root node ignorieren + XQNodePtr contentRoot = rawTree->first_child(); + addModelData( contentRoot->first_child() ); + + //XQTreeTable* treeTable = new XQTreeTable; + //treeTable->setModel(this); + //setTreeTable( treeTable ); + + //treeTable->show(); + } +}; + + +using namespace Qt::Literals::StringLiterals; + int main(int argc, char *argv[]) { - QApplication app(argc, argv); - /* + + + // Signal für einzelne QStandardItem-Änderungen connect(model, &QStandardItemModel::itemChanged, this, [](QStandardItem *changedItem){ @@ -63,10 +116,41 @@ connect(model, &QStandardItemModel::itemChanged, }); */ - + /* + QApplication app(argc, argv); //app.setStyle("fusion"); XQMainWindow window; window.show(); +*/ + + + QApplication app(argc, argv); + + + XQMainWindow::setupWorkingDir(); + XQItemFactory::instance().initItemFactory( c_ModelSheetFileName ); + + XQChildModel* myModel = createChildModel(); + qmlRegisterType< XQChildModel>("MyApp.Models", 1, 0, "MyChildModel"); + + + QQmlApplicationEngine engine; + + engine.rootContext()->setContextProperty("myChildModel", myModel); + + QObject::connect( + &engine, + &QQmlApplicationEngine::objectCreationFailed, + &app, + []() { QCoreApplication::exit(-1); }, + Qt::QueuedConnection); + + qDebug() << " fitz!"; + + engine.load( QUrl(QStringLiteral("qrc:/dummyview.qml")) ); + //engine.load( QUrl(QStringLiteral("qrc:/xqtableview.qml")) ); + + qDebug() << " hhakl!"; return app.exec(); diff --git a/src/model/xqcommand.cpp b/src/model/xqcommand.cpp index 0fcf876..eb34745 100644 --- a/src/model/xqcommand.cpp +++ b/src/model/xqcommand.cpp @@ -31,7 +31,7 @@ void XQNodeStore::dumpList( const QString& title ) const //! kostruktor. übergibt command-type und die aufrufende modelView. XQCommand::XQCommand(CmdType cmdType, XQViewModel* modelView ) - : _cmdType{ cmdType }, _model(modelView) + : _cmdType{ cmdType }, _viewModel(modelView) { } @@ -66,7 +66,7 @@ void XQCommand::setCommandType( XQCommand::CmdType cmdType ) void XQCommand::redo() { - _model->onCommandRedo( *this ); + _viewModel->onCommandRedo( *this ); } @@ -74,7 +74,7 @@ void XQCommand::redo() void XQCommand::undo() { - _model->onCommandUndo( *this ); + _viewModel->onCommandUndo( *this ); } diff --git a/src/model/xqcommand.h b/src/model/xqcommand.h index 6464b5e..f68d8d0 100644 --- a/src/model/xqcommand.h +++ b/src/model/xqcommand.h @@ -84,9 +84,9 @@ public: protected: - CmdType _cmdType{cmdInvalid}; - XQViewModel* _model{}; // needed for redo() / undo() - QModelIndex _originIndex; + CmdType _cmdType{cmdInvalid}; + XQViewModel* _viewModel{}; // needed for redo() / undo() + QModelIndex _originIndex; /* diff --git a/src/model/xqmodelsectionlist.cpp b/src/model/xqmodelsectionlist.cpp index 3f6d83b..d689d14 100644 --- a/src/model/xqmodelsectionlist.cpp +++ b/src/model/xqmodelsectionlist.cpp @@ -40,9 +40,9 @@ bool XQModelSection::isValid() const return _modelIndex.isValid() && _sectionRootNode; } -const QModelIndex& XQModelSection::modelIndex() const +QModelIndex XQModelSection::persistentModelIndex() const { - return _modelIndex; + return _modelIndex.operator QModelIndex(); } XQNodePtr XQModelSection::sectionRootNode() const @@ -129,7 +129,7 @@ const XQModelSection& XQModelSectionList::sectionFromRow(int itemRow ) const int i = size() - 1; for (; i >= 0; --i) { - if ( at(i).modelIndex().row() < itemRow ) + if ( at(i).persistentModelIndex().row() < itemRow ) return at(i); } @@ -170,7 +170,7 @@ int XQModelSectionList::lastRow(const XQModelSection& section ) const { // last section? return last row of model if (index == size() - 1) - return section.modelIndex().model()->rowCount();// - 1; + return section.persistentModelIndex().model()->rowCount();// - 1; // return row above the row of the next section -> last row of given section return at(index+1).row(); } @@ -185,7 +185,7 @@ void XQModelSectionList::dump() const qDebug() << " --- sections dump(): " < #include #include +#include #include #include @@ -36,12 +37,15 @@ class XQCommand; class XQViewModel : public QStandardItemModel { Q_OBJECT + //QML_ELEMENT public: XQViewModel(QObject* parent = nullptr); virtual ~XQViewModel() = default; + QHash roleNames() const override; + XQTreeTable* treeTable(); virtual void setTreeTable( XQTreeTable* mainView ); @@ -68,7 +72,7 @@ public: virtual void cmdNew( XQCommand& command ); virtual void cmdNewUndo( XQCommand& command ); - QHash roleNames() const override; + /*! @@ -106,7 +110,7 @@ signals: protected: void addSection(const XQItemList& list, const XQNodePtr& sheetNode ); - virtual void initContextMenu() = 0; + virtual void initContextMenu(){} // __fixme: should be created from xml virtual void setupViewProperties(); diff --git a/src/xtree.pro b/src/xtree.pro index a2ac858..b1d5b1c 100644 --- a/src/xtree.pro +++ b/src/xtree.pro @@ -1,7 +1,10 @@ QT += core gui widgets quick quickwidgets # widgets-private -CONFIG += c++20 +CONFIG += c++20 qmltypes + +QML_IMPORT_NAME = org.sourceworx.qmlcomponents +QML_IMPORT_MAJOR_VERSION = 1 # You can make your code fail to compile if it uses deprecated APIs. # In order to do so, uncomment the following line. diff --git a/src/xtree.qrc b/src/xtree.qrc index 31c6dd8..3655eb4 100644 --- a/src/xtree.qrc +++ b/src/xtree.qrc @@ -1,10 +1,13 @@ - - ../xml/modeldata1.xtr - ../xml/modeldata2.xtr - ../xml/modeldata3.xtr - ../xml/modelsheets.xml - ../quick/quickview.qml - - - + + ../xml/modeldata1.xtr + ../xml/modeldata2.xtr + ../xml/modeldata3.xtr + ../xml/modelsheets.xml + ../qml/xqtableview.qml + ../qml/HorizontalHeaderViewDelegate.qml + ../qml/XMain.qml + ../qml/VerticalHeaderViewDelegate.qml + ../qml/dummyview.qml + + \ No newline at end of file