Merge upstream/master into prefix-button

Only one conflict: the include block of
sources/ui/configpage/generalconfigurationpage.cpp, where upstream adds
editor/terminalnamecheck.h next to the two includes of the prefix
editor. All three are kept. The file list in
cmake/qet_compilation_vars.cmake merged itself this time.
This commit is contained in:
Kellermorph
2026-09-30 11:10:02 +02:00
93 changed files with 2001 additions and 71 deletions
@@ -18,6 +18,9 @@
#include "elementcollectionitem.h"
#include <QCoreApplication>
#include <QThread>
/**
@brief ElementCollectionItem::ElementCollectionItem
Constructor
@@ -247,6 +250,73 @@ QList<ElementCollectionItem *> ElementCollectionItem::items() const
return list;
}
/**
@brief onGuiThread
@return true when called from the thread the application lives in
*/
static bool onGuiThread()
{
return QThread::currentThread() == QCoreApplication::instance()->thread();
}
/**
@brief ElementCollectionItem::setData
ElementsCollectionModel::loadCollections() runs setUpData() for every
item on worker threads (QtConcurrent::map), while the items are
already in the model. QStandardItem::setData() updates the model and
emits its dataChanged() signal, which is not safe from a worker
thread. So a value set from a worker thread is kept on the item, and
the model applies it on the GUI thread with applyDeferredData() once
every item is set up. setText(), setFlags(), setToolTip() and setIcon()
all end up here.
@param value
@param role
*/
void ElementCollectionItem::setData(const QVariant &value, int role)
{
if (role == Qt::EditRole)
role = Qt::DisplayRole;
if (onGuiThread())
QStandardItem::setData(value, role);
else
m_deferred_data.insert(role, value);
}
/**
@brief ElementCollectionItem::data
On a worker thread, a value set by setData() but not applied yet is
returned, so setUpData() reads back what it has just set
(localName() tests text() for example).
@param role
@return
*/
QVariant ElementCollectionItem::data(int role) const
{
if (role == Qt::EditRole)
role = Qt::DisplayRole;
if (!onGuiThread())
{
const auto it = m_deferred_data.constFind(role);
if (it != m_deferred_data.constEnd())
return it.value();
}
return QStandardItem::data(role);
}
/**
@brief ElementCollectionItem::applyDeferredData
Apply the values set from a worker thread, see setData().
Must be called on the GUI thread, after the worker is done.
*/
void ElementCollectionItem::applyDeferredData()
{
for (auto it = m_deferred_data.constBegin() ; it != m_deferred_data.constEnd() ; ++it)
QStandardItem::setData(it.value(), it.key());
m_deferred_data.clear();
}
void setUpData(ElementCollectionItem *eci) {
eci->setUpData();
}
@@ -18,6 +18,7 @@
#ifndef ELEMENTCOLLECTIONITEM2_H
#define ELEMENTCOLLECTIONITEM2_H
#include <QHash>
#include <QStandardItem>
/**
@@ -56,6 +57,16 @@ class ElementCollectionItem : public QStandardItem
QList<ElementCollectionItem *> elementsChild() const;
QList<ElementCollectionItem *> directoriesChild() const;
QList<ElementCollectionItem *> items() const;
QVariant data(int role = Qt::UserRole + 1) const override;
void setData(const QVariant &value, int role = Qt::UserRole + 1) override;
void applyDeferredData();
private:
/// Values set by setUpData() while it runs on a worker thread,
/// see setData(). Only ever touched by that one worker thread,
/// until applyDeferredData() empties it on the GUI thread.
QHash<int, QVariant> m_deferred_data;
};
void setUpData(ElementCollectionItem *eci);
@@ -312,8 +312,15 @@ void ElementsCollectionModel::loadCollections(bool common_collection,
this, &ElementsCollectionModel::loadingProgressValueChanged);
connect(watcher, &QFutureWatcher<void>::progressRangeChanged,
this, &ElementsCollectionModel::loadingProgressRangeChanged);
connect(watcher, &QFutureWatcher<void>::finished,
this, &ElementsCollectionModel::loadingFinished);
//setUpData() ran on worker threads, which only kept the values on
//the items (ElementCollectionItem::setData()): apply them here, on
//the GUI thread, before anyone is told the loading is finished.
connect(watcher, &QFutureWatcher<void>::finished, this, [this]()
{
for (ElementCollectionItem *eci : std::as_const(m_items_list_to_setUp))
eci->applyDeferredData();
emit loadingFinished();
});
connect(watcher, &QFutureWatcher<void>::finished, watcher, &QFutureWatcher<void>::deleteLater);
@@ -172,6 +172,18 @@ void ElementsCollectionWidget::setCurrentLocation(
m_model->indexFromLocation(location));
}
/**
@brief ElementsCollectionWidget::addTemplate
Show a newly saved template in the templates tab, which is otherwise
only read when the collections are loaded or reloaded.
@param location : the saved .qetmak file
*/
void ElementsCollectionWidget::addTemplate(const ElementsLocation &location)
{
if (m_macros_model && location.exist())
m_macros_model->addLocation(location);
}
void ElementsCollectionWidget::leaveEvent(QEvent *event)
{
if (QETDiagramEditor *qde = QETApp::diagramEditorAncestorOf(this))
@@ -72,6 +72,7 @@ class ElementsCollectionWidget : public QWidget
void removeProject (QETProject *project);
void highlightUnusedElement();
void setCurrentLocation(const ElementsLocation &location);
void addTemplate(const ElementsLocation &location);
QVector<ElementSearchHit> rankedSearch(const QString &text,
const QModelIndex &within = QModelIndex());
@@ -20,12 +20,14 @@
#include <QDate>
#include "ui_renamedialog.h"
#include "../../qet.h"
RenameDialog::RenameDialog(QString path, QWidget *parent) :
QDialog(parent),
ui(new Ui::RenameDialog),
m_path(std::move(path))
{
ui->setupUi(this);
QET::trackDialogGeometry(this);
m_name = m_path.split("/").last();
if (m_name.endsWith(".elmt")) m_name.remove(".elmt");
ui->m_label->setText(tr("L'élément « %1 » existe déjà. Que souhaitez-vous faire ?").arg(m_path));