include/vcl/weld/TreeView.hxx | 26 ++++++++++++++------------ vcl/inc/qt5/QtTreeViewModel.hxx | 11 +++++++++++ vcl/qt5/QtInstanceBuilder.cxx | 1 + vcl/qt5/QtInstanceTreeView.cxx | 37 ++++++++++++++++++++++++++----------- vcl/qt5/QtTreeViewModel.cxx | 34 ++++++++++++++++++++++++++++++++-- 5 files changed, 84 insertions(+), 25 deletions(-)
New commits: commit b7147efd95c075028a7a93fce8cfdb014db6773d Author: Michael Weghorn <[email protected]> AuthorDate: Wed Jan 14 12:34:40 2026 +0100 Commit: Michael Weghorn <[email protected]> CommitDate: Thu Jan 15 01:34:39 2026 +0100 tdf#130857 qt weld: Support "Expert Configuration" dialog This means that native Qt widgets are used for that dialog now when using the qt5 or qt6 VCL plugin and starting LO with environment variable SAL_VCL_QT_USE_WELDED_WIDGETS=1 set. The dialog can be triggered like this: * start Writer * "Tools" -> "Options" -> "Advanced" -> "Expert Configuration" This makes use of support for on-demand treeview children which was implemented in previous commit Change-Id: I269710801e54656b5891215145d7e4d3790aad18 Author: Michael Weghorn <[email protected]> Date: Wed Jan 14 12:23:16 2026 +0100 tdf#130857 qt weld: Support on-demand TreeView items Change-Id: Ifcf32e08d816522366757268df9c36a6cac9a096 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/197268 Reviewed-by: Michael Weghorn <[email protected]> Tested-by: Jenkins diff --git a/vcl/qt5/QtInstanceBuilder.cxx b/vcl/qt5/QtInstanceBuilder.cxx index 577d9923d267..496412bcc589 100644 --- a/vcl/qt5/QtInstanceBuilder.cxx +++ b/vcl/qt5/QtInstanceBuilder.cxx @@ -67,6 +67,7 @@ bool QtInstanceBuilder::IsUIFileSupported(const OUString& rUIFile, const weld::W // what is relevant for that particular one, without having to implement the full // weld API at once. static std::unordered_set<OUString> aSupportedUIFiles = { + u"cui/ui/aboutconfigdialog.ui"_ustr, u"cui/ui/aboutdialog.ui"_ustr, u"cui/ui/additionsdialog.ui"_ustr, u"cui/ui/breaknumberoption.ui"_ustr, commit f53088eb09021ce6daa60fa37997bb2007d5c535 Author: Michael Weghorn <[email protected]> AuthorDate: Wed Jan 14 12:23:16 2026 +0100 Commit: Michael Weghorn <[email protected]> CommitDate: Thu Jan 15 01:34:32 2026 +0100 tdf#130857 qt weld: Support on-demand TreeView items Implement support for on-demand child entries in QtInstanceTreeView. The documentation in the "expanding on-demand node details" section in include/vcl/weld/TreeView.hxx contains more details. So far, it was documenting that on-demand children are implemented by temporarily inserting a dummy child. For Qt, implement it a different way: Override QAbstractItemView::hasChildren in the QtTreeViewModel used in QtInstanceTreeView to always return true if on-demand children are enabled for an entry/item. If on-demand children are enabled, indicate that by setting a true boolean value for the new custom ROLE_CHILDREN_ON_DEMAND role in the item. Update the documentation in include/vcl/weld/TreeView.hxx to make clear that the use of dummy children is just one possible way of implementing support for on-demand children. On-demand children are used e.g. in the "Tools" -> "Options" -> "Advanced" -> "Expert Configuration" dialog that is not supported yet, but will be declared as supported in an upcoming commit, now that the required logic has been implemented. Change-Id: I269710801e54656b5891215145d7e4d3790aad18 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/197267 Tested-by: Jenkins Reviewed-by: Michael Weghorn <[email protected]> diff --git a/include/vcl/weld/TreeView.hxx b/include/vcl/weld/TreeView.hxx index 194f770c89de..2456eba9f1c1 100644 --- a/include/vcl/weld/TreeView.hxx +++ b/include/vcl/weld/TreeView.hxx @@ -377,24 +377,26 @@ public: /* expanding on-demand node details When a node is added with children-on-demand (typically via 'insert' with - bChildrenOnDemand of true), then initially in reality the - children-on-demand node is given a 'placeholder' child entry to indicate - the load-on-demand state. - - The 'placeholder' needs to be there for the expander indicator to be - drawn/shown even when there are no "real" entries yet. This child doesn't - exist for the purposes of any of the iterator methods, e.g. iter_has_child - on an on-demand node which hasn't been expanded yet is false. Likewise the - rest of the iterator methods skip over or otherwise ignore that node. + bChildrenOnDemand of true), then it shows an expander indicator even if + it doesn't have any "real" child entries (yet). + + (Depending on the underlying toolkit, implementations may in reality give + the children-on-demand node a 'placeholder' child entry to indicate the + load-on-demand state and ensure the treeview draws/shows the expander + indicator even when there are no "real" entries yet. In that case, this + child doesn't exist for the purposes of any of the iterator methods, + e.g. iter_has_child on an on-demand node which hasn't been expanded yet + is false. Likewise the rest of the iterator methods skip over or otherwise + ignore that node.) Normal usage is the user clicks on the expander, the expansion mechanism - removes the 'placeholder' entry (set_children_on_demand(false)) and calls + disables on-demand nodes (set_children_on_demand(false)) and calls any installed expanding-callback (installable via connect_expanding) which has the opportunity to populate the node with children. If you decide to directly populate the children of an on-demand node - outside of the expanding-callback then you also need to explicitly remove - the 'placeholder' with set_children_on_demand(false) otherwise the treeview + outside of the expanding-callback then you also need to explicitly disable + on-demand mode with set_children_on_demand(false); otherwise the treeview is in an inconsistent state. */ virtual bool get_row_expanded(const TreeIter& rIter) const = 0; diff --git a/vcl/inc/qt5/QtTreeViewModel.hxx b/vcl/inc/qt5/QtTreeViewModel.hxx index 22e2c4010545..f485cd174187 100644 --- a/vcl/inc/qt5/QtTreeViewModel.hxx +++ b/vcl/inc/qt5/QtTreeViewModel.hxx @@ -28,7 +28,12 @@ public: virtual Qt::ItemFlags flags(const QModelIndex& rIndex) const override; + virtual bool hasChildren(const QModelIndex& rIndex = QModelIndex()) const override; + void setEditableColumns(const std::unordered_set<int>& rEditableColumns); + + bool getChildrenOnDemand(const QModelIndex& rIndex) const; + void setChildrenOnDemand(const QModelIndex& rIndex, bool bOnDemandChildren); }; /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/vcl/qt5/QtInstanceTreeView.cxx b/vcl/qt5/QtInstanceTreeView.cxx index 02b2df1e0420..a0d07d45019c 100644 --- a/vcl/qt5/QtInstanceTreeView.cxx +++ b/vcl/qt5/QtInstanceTreeView.cxx @@ -53,10 +53,6 @@ void QtInstanceTreeView::do_insert(const weld::TreeIter* pParent, int nPos, cons VirtualDevice* pImageSurface, bool bChildrenOnDemand, weld::TreeIter* pRet) { - assert(!bChildrenOnDemand && "Not implemented yet"); - // avoid -Werror=unused-parameter for release build - (void)bChildrenOnDemand; - SolarMutexGuard g; GetQtInstance().RunInMainThread([&] { const QModelIndex aParentIndex @@ -90,6 +86,9 @@ void QtInstanceTreeView::do_insert(const weld::TreeIter* pParent, int nPos, cons itemFromIndex(toggleButtonModelIndex(QtInstanceTreeIter(aIndex)))->setCheckable(true); } + if (bChildrenOnDemand) + m_pModel->setChildrenOnDemand(aIndex, true); + if (pRet) static_cast<QtInstanceTreeIter*>(pRet)->setModelIndex(aIndex); }); @@ -534,15 +533,24 @@ void QtInstanceTreeView::collapse_row(const weld::TreeIter& rIter) GetQtInstance().RunInMainThread([&] { m_pTreeView->collapse(modelIndex(rIter)); }); } -void QtInstanceTreeView::do_set_children_on_demand(const weld::TreeIter&, bool) +void QtInstanceTreeView::do_set_children_on_demand(const weld::TreeIter& rIter, + bool bChildrenOnDemand) { - assert(false && "Not implemented yet"); + SolarMutexGuard g; + + GetQtInstance().RunInMainThread( + [&] { m_pModel->setChildrenOnDemand(modelIndex(rIter), bChildrenOnDemand); }); } -bool QtInstanceTreeView::get_children_on_demand(const weld::TreeIter&) const +bool QtInstanceTreeView::get_children_on_demand(const weld::TreeIter& rIter) const { - assert(false && "Not implemented yet"); - return false; + SolarMutexGuard g; + + bool bChildrenOnDemand = false; + GetQtInstance().RunInMainThread( + [&] { bChildrenOnDemand = m_pModel->getChildrenOnDemand(modelIndex(rIter)); }); + + return bChildrenOnDemand; } void QtInstanceTreeView::set_show_expanders(bool) { assert(false && "Not implemented yet"); } diff --git a/vcl/qt5/QtTreeViewModel.cxx b/vcl/qt5/QtTreeViewModel.cxx index 67e3d7ddfca4..4c41a175d3f5 100644 --- a/vcl/qt5/QtTreeViewModel.cxx +++ b/vcl/qt5/QtTreeViewModel.cxx @@ -12,6 +12,9 @@ #include <QtGui/QStandardItemModel> +// role used to indicate whether an item has on-demand children +constexpr int ROLE_CHILDREN_ON_DEMAND = Qt::UserRole + 2000; + QtTreeViewModel::QtTreeViewModel(QWidget* pParent) : QSortFilterProxyModel(pParent) { @@ -28,9 +31,27 @@ Qt::ItemFlags QtTreeViewModel::flags(const QModelIndex& rIndex) const return eFlags; } +bool QtTreeViewModel::hasChildren(const QModelIndex& rIndex) const +{ + if (getChildrenOnDemand(rIndex)) + return true; + + return QSortFilterProxyModel::hasChildren(rIndex); +} + void QtTreeViewModel::setEditableColumns(const std::unordered_set<int>& rEditableColumns) { m_aEditableColumns = rEditableColumns; } +bool QtTreeViewModel::getChildrenOnDemand(const QModelIndex& rIndex) const +{ + return data(rIndex, ROLE_CHILDREN_ON_DEMAND).toBool(); +} + +void QtTreeViewModel::setChildrenOnDemand(const QModelIndex& rIndex, bool bOnDemandChildren) +{ + setData(rIndex, QVariant(bOnDemandChildren), ROLE_CHILDREN_ON_DEMAND); +} + /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ commit bb101bff7eab618dc2682fe7d91a8b04398b952a Author: Michael Weghorn <[email protected]> AuthorDate: Wed Jan 14 11:34:48 2026 +0100 Commit: Michael Weghorn <[email protected]> CommitDate: Thu Jan 15 01:34:24 2026 +0100 tdf#130857 qt weld: Implement TreeView::set_column_editables Extend the approach implemented in previous commit Change-Id: I36e188880fd5bc9eabbc9392f05b057ab3cfaea9 Author: Michael Weghorn <[email protected]> Date: Wed Jan 14 11:10:23 2026 +0100 tdf#130857 qt weld: Make TreeView columns readonly by default to keep track of columns set as editable via QtInstanceTreeView::set_column_editables: Only clear the editable flag for items in columns that haven't explicitly been set to be editable. See the above-mentioned commit for more details/background. The implementation is still untested in practice as no dialog using weld::TreeView::set_column_editables method is supported yet with SAL_VCL_QT_USE_WELDED_WIDGETS=1. Change-Id: I77fa14ce15c6a04f36ee2b259078e18f4b4f350f Reviewed-on: https://gerrit.libreoffice.org/c/core/+/197266 Tested-by: Jenkins Reviewed-by: Michael Weghorn <[email protected]> diff --git a/vcl/inc/qt5/QtTreeViewModel.hxx b/vcl/inc/qt5/QtTreeViewModel.hxx index 06af92c9c2cf..22e2c4010545 100644 --- a/vcl/inc/qt5/QtTreeViewModel.hxx +++ b/vcl/inc/qt5/QtTreeViewModel.hxx @@ -14,15 +14,21 @@ #include <QtCore/QSortFilterProxyModel> #include <QtWidgets/QWidget> +#include <unordered_set> + /** Item model for QtInstanceTreeView. */ class QtTreeViewModel : public QSortFilterProxyModel { Q_OBJECT + std::unordered_set<int> m_aEditableColumns; + public: QtTreeViewModel(QWidget* pParent); virtual Qt::ItemFlags flags(const QModelIndex& rIndex) const override; + + void setEditableColumns(const std::unordered_set<int>& rEditableColumns); }; /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/vcl/qt5/QtInstanceTreeView.cxx b/vcl/qt5/QtInstanceTreeView.cxx index 7bdc24839267..02b2df1e0420 100644 --- a/vcl/qt5/QtInstanceTreeView.cxx +++ b/vcl/qt5/QtInstanceTreeView.cxx @@ -669,9 +669,16 @@ void QtInstanceTreeView::set_column_fixed_widths(const std::vector<int>& rWidths }); } -void QtInstanceTreeView::set_column_editables(const std::vector<bool>&) +void QtInstanceTreeView::set_column_editables(const std::vector<bool>& rEditables) { - assert(false && "Not implemented yet"); + std::unordered_set<int> aEditableColumns; + for (size_t i = 0; i < rEditables.size(); i++) + { + if (rEditables.at(i)) + aEditableColumns.insert(i); + } + + m_pModel->setEditableColumns(aEditableColumns); } int QtInstanceTreeView::get_column_width(int nCol) const diff --git a/vcl/qt5/QtTreeViewModel.cxx b/vcl/qt5/QtTreeViewModel.cxx index 20531e45c328..67e3d7ddfca4 100644 --- a/vcl/qt5/QtTreeViewModel.cxx +++ b/vcl/qt5/QtTreeViewModel.cxx @@ -20,8 +20,17 @@ QtTreeViewModel::QtTreeViewModel(QWidget* pParent) Qt::ItemFlags QtTreeViewModel::flags(const QModelIndex& rIndex) const { - // weld::TreeView columns are not editable by default - return QSortFilterProxyModel::flags(rIndex) & ~Qt::ItemIsEditable; + Qt::ItemFlags eFlags = QSortFilterProxyModel::flags(rIndex); + + if (!m_aEditableColumns.contains(rIndex.column())) + eFlags &= ~Qt::ItemIsEditable; + + return eFlags; +} + +void QtTreeViewModel::setEditableColumns(const std::unordered_set<int>& rEditableColumns) +{ + m_aEditableColumns = rEditableColumns; } /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
