vcl/CustomTarget_qt5_moc.mk | 1 vcl/CustomTarget_qt6_moc.mk | 1 vcl/Library_vclplug_qt5.mk | 1 vcl/Library_vclplug_qt6.mk | 1 vcl/inc/qt5/QtInstanceTreeView.hxx | 3 ++ vcl/inc/qt5/QtTreeViewItemDelegate.hxx | 36 +++++++++++++++++++++++++ vcl/inc/qt6/QtTreeViewItemDelegate.hxx | 12 ++++++++ vcl/qt5/QtInstanceBuilder.cxx | 1 vcl/qt5/QtInstanceTreeView.cxx | 40 +++++++++++++++++++++++++++- vcl/qt5/QtTreeViewItemDelegate.cxx | 46 +++++++++++++++++++++++++++++++++ vcl/qt6/QtTreeViewItemDelegate.cxx | 12 ++++++++ 11 files changed, 153 insertions(+), 1 deletion(-)
New commits: commit 19a44ab0e4e8158c81c216840c958cefa56ce898 Author: Michael Weghorn <[email protected]> AuthorDate: Fri Jan 23 01:24:32 2026 +0100 Commit: Michael Weghorn <[email protected]> CommitDate: Fri Jan 23 10:41:37 2026 +0100 tdf#130857 qt weld: Implement QtInstanceTreeView::do_remove_selection Sort the indices of the selected rows before removal and remove from the last selected row to the first one, to avoid that removing an earlier row invalidates the index of a later one. The method gets used e.g. for the following case with SAL_VCL_QT_USE_WELDED_WIDGETS=1: * start Writer * type "Hello world" * select the text * "Insert -> Bookmark" * confirm bookmark creation with "Insert" * "Insert" -> "Bookmark" * select the treeview entry * press the "Delete" button Change-Id: I37978356912139b38dca7e58c2e0723ead7acd01 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/197892 Reviewed-by: Michael Weghorn <[email protected]> Tested-by: Jenkins diff --git a/vcl/qt5/QtInstanceTreeView.cxx b/vcl/qt5/QtInstanceTreeView.cxx index 9eacc117b17d..b21fa119c4ef 100644 --- a/vcl/qt5/QtInstanceTreeView.cxx +++ b/vcl/qt5/QtInstanceTreeView.cxx @@ -745,7 +745,23 @@ void QtInstanceTreeView::set_selection_mode(SelectionMode eMode) int QtInstanceTreeView::count_selected_rows() const { return get_selected_rows().size(); } -void QtInstanceTreeView::do_remove_selection() { assert(false && "Not implemented yet"); } +void QtInstanceTreeView::do_remove_selection() +{ + SolarMutexGuard g; + + GetQtInstance().RunInMainThread([&] { + // remove from last to first selected row to ensure indexes remain valid + QModelIndexList aSelectedIndexes = m_pSelectionModel->selectedRows(); + std::sort(aSelectedIndexes.begin(), aSelectedIndexes.end(), + [this](const QModelIndex& rFirst, const QModelIndex& rSecond) { + return iter_compare(QtInstanceTreeIter(rFirst), QtInstanceTreeIter(rSecond)) + == -1; + }); + + for (auto aIt = aSelectedIndexes.rbegin(); aIt != aSelectedIndexes.rend(); aIt++) + m_pModel->removeRow(aIt->row(), aIt->parent()); + }); +} bool QtInstanceTreeView::changed_by_hover() const { commit 96e2a24ef1c9f6a773dd218d80046a68b8c433b1 Author: Michael Weghorn <[email protected]> AuthorDate: Fri Jan 23 00:57:19 2026 +0100 Commit: Michael Weghorn <[email protected]> CommitDate: Fri Jan 23 10:41:30 2026 +0100 tdf#130857 qt weld: Support Writer "Insert Bookmark" 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. See commit message of previous commit Change-Id: I98ea4e9237ac3174c6049075cf0ecb04b2448167 Author: Michael Weghorn <[email protected]> Date: Fri Jan 23 00:49:08 2026 +0100 tdf#130857 qt weld: Call TreeView signal hdls when editing starts/ends for sample steps to trigger and test this newly supported dialog. QtInstanceTreeView::do_remove_selection, required to remove selected entries in the dialog when pressing the "Delete" button, will be implemented in an upcoming commit. Change-Id: Ic5508d3c5df83d9136af4a336bf14b6566c07b17 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/197891 Reviewed-by: Michael Weghorn <[email protected]> Tested-by: Jenkins diff --git a/vcl/qt5/QtInstanceBuilder.cxx b/vcl/qt5/QtInstanceBuilder.cxx index 3b2604461d9a..a0df2c03b33e 100644 --- a/vcl/qt5/QtInstanceBuilder.cxx +++ b/vcl/qt5/QtInstanceBuilder.cxx @@ -194,6 +194,7 @@ bool QtInstanceBuilder::IsUIFileSupported(const OUString& rUIFile, const weld::W u"modules/swriter/ui/footendnotedialog.ui"_ustr, u"modules/swriter/ui/footnotepage.ui"_ustr, u"modules/swriter/ui/inforeadonlydialog.ui"_ustr, + u"modules/swriter/ui/insertbookmark.ui"_ustr, u"modules/swriter/ui/insertbreak.ui"_ustr, u"modules/swriter/ui/insertcaption.ui"_ustr, u"modules/swriter/ui/inserttable.ui"_ustr, commit a7a8c98858bfdde442bac443a24c8e8f0cd54176 Author: Michael Weghorn <[email protected]> AuthorDate: Fri Jan 23 00:49:08 2026 +0100 Commit: Michael Weghorn <[email protected]> CommitDate: Fri Jan 23 10:41:23 2026 +0100 tdf#130857 qt weld: Call TreeView signal hdls when editing starts/ends weld::TreeView has signal_editing_started and signal_editing_done methods that are supposed to be called by the toolkit-specific weld::TreeView implementations when editing is about to start and when it is about to end. weld::TreeView::signal_editing_started returns a bool indicating whether or not editing should actually start for the given index/entry. weld::TreeView::signal_editing_done returns a bool indicating whether the entry should actually be updated with the newly typed text. In addition, the handlers connected to those signals may do additional work. For the Qt-based implementation, introduce a custom item delegate called QtTreeViewItemDelegate that subclasses QStyledItemDelegate. As [1] says: > QStyledItemDelegate is the default delegate for all Qt item views, and > is installed upon them when they are created. In the new QtTreeViewItemDelegate class, override the methods that get called when the editor gets created and when the model data is supposed to get updated. Call the weld::TreeView signal handlers and return early in case they return false. In QtTreeViewItemDelegate::setModelData, only handle the case where the editor is a QLineEdit, which is used for editing (single-line) strings, as that is what weld::TreeView::signal_editing_done also expects. The newly introduced logic will e.g. be used by Writer's "Insert" -> "Bookmark" dialog once it is declared as supported by QtInstanceBuilder, which will happen in an upcoming commit. Sample scenario: * start Writer * type "Hello world" * select the text * "Insert -> Bookmark" * confirm bookmark creation with "Insert" * "Insert" -> "Bookmark" * select the treeview entry * press the "Edit Text" button * type new text "Something else" and press Enter to confirm -> The text in the document is changed to "Something else" by the handler called by the call to weld::TreeView::signal_editing_done. [1] https://doc.qt.io/qt-6/qstyleditemdelegate.html#details Change-Id: I98ea4e9237ac3174c6049075cf0ecb04b2448167 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/197890 Reviewed-by: Michael Weghorn <[email protected]> Tested-by: Jenkins diff --git a/vcl/CustomTarget_qt5_moc.mk b/vcl/CustomTarget_qt5_moc.mk index f8f2a5819eaa..9068019700aa 100644 --- a/vcl/CustomTarget_qt5_moc.mk +++ b/vcl/CustomTarget_qt5_moc.mk @@ -62,6 +62,7 @@ $(call gb_CustomTarget_get_target,vcl/qt5) : \ $(gb_CustomTarget_workdir)/vcl/qt5/QtMainWindow.moc \ $(gb_CustomTarget_workdir)/vcl/qt5/QtMenu.moc \ $(gb_CustomTarget_workdir)/vcl/qt5/QtTransferable.moc \ + $(gb_CustomTarget_workdir)/vcl/qt5/QtTreeViewItemDelegate.moc \ $(gb_CustomTarget_workdir)/vcl/qt5/QtTreeViewModel.moc \ $(gb_CustomTarget_workdir)/vcl/qt5/QtObject.moc \ $(gb_CustomTarget_workdir)/vcl/qt5/QtTimer.moc \ diff --git a/vcl/CustomTarget_qt6_moc.mk b/vcl/CustomTarget_qt6_moc.mk index f545b285150e..1797ba66cf21 100644 --- a/vcl/CustomTarget_qt6_moc.mk +++ b/vcl/CustomTarget_qt6_moc.mk @@ -62,6 +62,7 @@ $(call gb_CustomTarget_get_target,vcl/qt6) : \ $(gb_CustomTarget_workdir)/vcl/qt6/QtMainWindow.moc \ $(gb_CustomTarget_workdir)/vcl/qt6/QtMenu.moc \ $(gb_CustomTarget_workdir)/vcl/qt6/QtTransferable.moc \ + $(gb_CustomTarget_workdir)/vcl/qt6/QtTreeViewItemDelegate.moc \ $(gb_CustomTarget_workdir)/vcl/qt6/QtTreeViewModel.moc \ $(gb_CustomTarget_workdir)/vcl/qt6/QtObject.moc \ $(gb_CustomTarget_workdir)/vcl/qt6/QtTimer.moc \ diff --git a/vcl/Library_vclplug_qt5.mk b/vcl/Library_vclplug_qt5.mk index 90826dcd06ae..3352bbb7432d 100644 --- a/vcl/Library_vclplug_qt5.mk +++ b/vcl/Library_vclplug_qt5.mk @@ -155,6 +155,7 @@ $(eval $(call gb_Library_add_exception_objects,vclplug_qt5,\ vcl/qt5/QtTimer \ vcl/qt5/QtTools \ vcl/qt5/QtTransferable \ + vcl/qt5/QtTreeViewItemDelegate \ vcl/qt5/QtTreeViewModel \ vcl/qt5/QtVirtualDevice \ vcl/qt5/QtWidget \ diff --git a/vcl/Library_vclplug_qt6.mk b/vcl/Library_vclplug_qt6.mk index 83cf22d38888..7894b0f8900e 100644 --- a/vcl/Library_vclplug_qt6.mk +++ b/vcl/Library_vclplug_qt6.mk @@ -154,6 +154,7 @@ $(eval $(call gb_Library_add_exception_objects,vclplug_qt6,\ vcl/qt6/QtTimer \ vcl/qt6/QtTools \ vcl/qt6/QtTransferable \ + vcl/qt6/QtTreeViewItemDelegate \ vcl/qt6/QtTreeViewModel \ vcl/qt6/QtVirtualDevice \ vcl/qt6/QtWidget \ diff --git a/vcl/inc/qt5/QtInstanceTreeView.hxx b/vcl/inc/qt5/QtInstanceTreeView.hxx index be22c9096286..fc83fbcd97ba 100644 --- a/vcl/inc/qt5/QtInstanceTreeView.hxx +++ b/vcl/inc/qt5/QtInstanceTreeView.hxx @@ -158,6 +158,9 @@ public: using QtInstanceWidget::set_sensitive; using QtInstanceWidget::get_sensitive; + bool signalEditingStarted(const QModelIndex& rIndex); + bool signalEditingDone(const QModelIndex& rIndex, const QString& rNewText); + // methods to get/set which roles are supported by the individual columns // based on the underlying GtkTreeViewColumns and their GtkCellRenderers static QList<QList<Qt::ItemDataRole>> columnRoles(QTreeView& rTreeView); diff --git a/vcl/inc/qt5/QtTreeViewItemDelegate.hxx b/vcl/inc/qt5/QtTreeViewItemDelegate.hxx new file mode 100644 index 000000000000..60672cca7371 --- /dev/null +++ b/vcl/inc/qt5/QtTreeViewItemDelegate.hxx @@ -0,0 +1,36 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#pragma once + +#include <QtWidgets/QStyledItemDelegate> +#include <QtWidgets/QWidget> + +/** Item delegate for QtInstanceTreeView. */ +class QtTreeViewItemDelegate : public QStyledItemDelegate +{ + Q_OBJECT + + std::function<bool(const QModelIndex& rIndex)> m_aStartEditingFunction; + std::function<bool(const QModelIndex& rIndex, const QString& rNewText)> + m_aFinishEditingFunction; + +public: + QtTreeViewItemDelegate(QObject* pParent, + std::function<bool(const QModelIndex& rIndex)> aStartEditingFunction, + std::function<bool(const QModelIndex& rIndex, const QString& rNewText)> + aEndEditingFunction); + + virtual QWidget* createEditor(QWidget* pParent, const QStyleOptionViewItem& rOption, + const QModelIndex& rIndex) const override; + virtual void setModelData(QWidget* pEditor, QAbstractItemModel* pModel, + const QModelIndex& rIndex) const override; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/vcl/inc/qt6/QtTreeViewItemDelegate.hxx b/vcl/inc/qt6/QtTreeViewItemDelegate.hxx new file mode 100644 index 000000000000..2469d54a45a7 --- /dev/null +++ b/vcl/inc/qt6/QtTreeViewItemDelegate.hxx @@ -0,0 +1,12 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include "../qt5/QtTreeViewItemDelegate.hxx" + +/* 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 5f5e5238cfc4..9eacc117b17d 100644 --- a/vcl/qt5/QtInstanceTreeView.cxx +++ b/vcl/qt5/QtInstanceTreeView.cxx @@ -9,6 +9,7 @@ #include <QtInstanceTreeView.hxx> #include <QtInstanceTreeView.moc> +#include <QtTreeViewItemDelegate.hxx> #include <vcl/qt/QtUtils.hxx> @@ -46,6 +47,13 @@ QtInstanceTreeView::QtInstanceTreeView(QTreeView* pTreeView) assert(m_pTreeView->viewport()); m_pTreeView->viewport()->installEventFilter(this); + + QtTreeViewItemDelegate* pDelegate = new QtTreeViewItemDelegate( + m_pTreeView, [this](const QModelIndex& rIndex) { return signalEditingStarted(rIndex); }, + [this](const QModelIndex& rIndex, const QString& rNewText) { + return signalEditingDone(rIndex, rNewText); + }); + m_pTreeView->setItemDelegate(pDelegate); } void QtInstanceTreeView::do_insert(const weld::TreeIter* pParent, int nPos, const OUString* pStr, @@ -810,6 +818,20 @@ QAbstractItemView::SelectionMode QtInstanceTreeView::mapSelectionMode(SelectionM } } +bool QtInstanceTreeView::signalEditingStarted(const QModelIndex& rIndex) +{ + SolarMutexGuard g; + + return signal_editing_started(QtInstanceTreeIter(rIndex)); +} + +bool QtInstanceTreeView::signalEditingDone(const QModelIndex& rIndex, const QString& rNewText) +{ + SolarMutexGuard g; + + return signal_editing_done({ QtInstanceTreeIter(rIndex), toOUString(rNewText) }); +} + QList<QList<Qt::ItemDataRole>> QtInstanceTreeView::columnRoles(QTreeView& rTreeView) { QVariant aVariant = rTreeView.property(PROPERTY_COLUMN_ROLES); diff --git a/vcl/qt5/QtTreeViewItemDelegate.cxx b/vcl/qt5/QtTreeViewItemDelegate.cxx new file mode 100644 index 000000000000..e80944ee9aae --- /dev/null +++ b/vcl/qt5/QtTreeViewItemDelegate.cxx @@ -0,0 +1,46 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include <QtTreeViewItemDelegate.hxx> +#include <QtTreeViewItemDelegate.moc> + +#include <QtWidgets/QLineEdit> + +QtTreeViewItemDelegate::QtTreeViewItemDelegate( + QObject* pParent, std::function<bool(const QModelIndex& rIndex)> aStartEditingFunction, + std::function<bool(const QModelIndex& rIndex, const QString& rNewText)> aFinishEditingFunction) + : QStyledItemDelegate(pParent) + , m_aStartEditingFunction(aStartEditingFunction) + , m_aFinishEditingFunction(aFinishEditingFunction) +{ +} + +QWidget* QtTreeViewItemDelegate::createEditor(QWidget* pParent, const QStyleOptionViewItem& rOption, + const QModelIndex& rIndex) const +{ + if (!m_aStartEditingFunction(rIndex)) + return nullptr; + + return QStyledItemDelegate::createEditor(pParent, rOption, rIndex); +} + +void QtTreeViewItemDelegate::setModelData(QWidget* pEditor, QAbstractItemModel* pModel, + const QModelIndex& rIndex) const +{ + // check whether new string is accepted + if (QLineEdit* pLineEdit = qobject_cast<QLineEdit*>(pEditor)) + { + if (!m_aFinishEditingFunction(rIndex, pLineEdit->text())) + return; + } + + QStyledItemDelegate::setModelData(pEditor, pModel, rIndex); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/vcl/qt6/QtTreeViewItemDelegate.cxx b/vcl/qt6/QtTreeViewItemDelegate.cxx new file mode 100644 index 000000000000..5b4b27a1a26b --- /dev/null +++ b/vcl/qt6/QtTreeViewItemDelegate.cxx @@ -0,0 +1,12 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include "../qt5/QtTreeViewItemDelegate.cxx" + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
