[multimedia/kdenlive] src/titler: Allow drag & drop image files into titler widget

Jean-Baptiste Mardelle <[email protected]>
Newsgroups gmane.comp.kde.cvs
Message-ID <[email protected]>
Git commit c42997854e6c97c30bf68fe5390a44fe61630f1b by Jean-Baptiste Mardelle.
Committed on 18/08/2026 at 08:21.
Pushed by mardelle into branch 'master'.

Allow drag & drop image files into titler widget

M  +41   -0    src/titler/graphicsscenerectmove.cpp
M  +5    -0    src/titler/graphicsscenerectmove.h
M  +35   -18   src/titler/titlewidget.cpp
M  +3    -0    src/titler/titlewidget.h

https://invent.kde.org/multimedia/kdenlive/-/commit/c42997854e6c97c30bf68fe5390a44fe61630f1b

diff --git a/src/titler/graphicsscenerectmove.cpp b/src/titler/graphicsscenerectmove.cpp
index 4ae73af4aa..f2081e39e3 100644
--- a/src/titler/graphicsscenerectmove.cpp
+++ b/src/titler/graphicsscenerectmove.cpp
@@ -18,6 +18,8 @@ SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
 #include <QGraphicsView>
 #include <QKeyEvent>
 #include <QList>
+#include <QMimeData>
+#include <QMimeDatabase>
 #include <QScrollBar>
 #include <QTextBlock>
 #include <QTextCursor>
@@ -728,6 +730,45 @@ void GraphicsSceneRectMove::contextMenuEvent(QGraphicsSceneContextMenuEvent *)
     // Disable QGraphicsScene standard context menu that was crashing
 }
 
+void GraphicsSceneRectMove::dragEnterEvent(QGraphicsSceneDragDropEvent *event)
+{
+    m_dragAllowed = false;
+    if (event->mimeData()->hasUrls()) {
+        QList<QUrl> urls = event->mimeData()->urls();
+        bool imageOnly = true;
+        for (auto &u : urls) {
+            auto mime = QMimeDatabase().mimeTypeForFile(u.toLocalFile());
+            if (!mime.name().startsWith(QStringLiteral("image/"))) {
+                imageOnly = false;
+                break;
+            }
+        }
+        if (imageOnly) {
+            m_dragAllowed = true;
+            event->acceptProposedAction();
+        }
+    } else {
+        QGraphicsScene::dragEnterEvent(event);
+    }
+}
+
+void GraphicsSceneRectMove::dragMoveEvent(QGraphicsSceneDragDropEvent *event)
+{
+    event->setAccepted(m_dragAllowed);
+}
+
+void GraphicsSceneRectMove::dropEvent(QGraphicsSceneDragDropEvent *event)
+{
+    event->setAccepted(m_dragAllowed);
+    QPoint pos = event->scenePos().toPoint();
+    QList<QUrl> urls = event->mimeData()->urls();
+    for (auto &u : urls) {
+        Q_EMIT addImage(u, pos);
+        pos.setX(pos.x() + 10);
+        pos.setY(pos.y() + 10);
+    }
+}
+
 void GraphicsSceneRectMove::setSelectedItem(QGraphicsItem *item)
 {
     clearSelection();
diff --git a/src/titler/graphicsscenerectmove.h b/src/titler/graphicsscenerectmove.h
index 408dd0b011..84b9b0de81 100644
--- a/src/titler/graphicsscenerectmove.h
+++ b/src/titler/graphicsscenerectmove.h
@@ -157,6 +157,9 @@ protected:
     void wheelEvent(QGraphicsSceneWheelEvent *wheelEvent) override;
     void drawForeground(QPainter *painter, const QRectF &rect) override;
     void contextMenuEvent(QGraphicsSceneContextMenuEvent *contextMenuEvent) override;
+    void dragEnterEvent(QGraphicsSceneDragDropEvent *event) override;
+    void dragMoveEvent(QGraphicsSceneDragDropEvent *event) override;
+    void dropEvent(QGraphicsSceneDragDropEvent *event) override;
 
 private:
     void setCursor(const QCursor &);
@@ -179,6 +182,7 @@ private:
     QList<QGraphicsItem *> m_lastSnapPreviews;
     int m_frameWidth;
     int m_frameHeight;
+    bool m_dragAllowed{false};
 
 Q_SIGNALS:
     void itemMoved();
@@ -191,4 +195,5 @@ Q_SIGNALS:
     void doubleClickEvent();
     void copy();
     void paste();
+    void addImage(const QUrl &url, const QPoint p);
 };
diff --git a/src/titler/titlewidget.cpp b/src/titler/titlewidget.cpp
index c0331a5867..acfe642d31 100644
--- a/src/titler/titlewidget.cpp
+++ b/src/titler/titlewidget.cpp
@@ -435,12 +435,12 @@ TitleWidget::TitleWidget(const QUrl &url, QString projectTitlePath, Monitor *mon
     m_buttonLoad = m_toolbar->addAction(QIcon::fromTheme(QStringLiteral("document-open")), i18n("Open Title…"));
     m_buttonLoad->setCheckable(false);
     m_buttonLoad->setShortcut(Qt::CTRL | Qt::Key_O);
-    connect(m_buttonLoad, SIGNAL(triggered()), this, SLOT(loadTitle()));
+    connect(m_buttonLoad, &QAction::triggered, this, [this]() { loadTitle(); }, Qt::QueuedConnection);
 
     m_buttonSave = m_toolbar->addAction(QIcon::fromTheme(QStringLiteral("document-save-as")), i18n("Save Title As…"));
     m_buttonSave->setCheckable(false);
     m_buttonSave->setShortcut(Qt::CTRL | Qt::Key_S);
-    connect(m_buttonSave, &QAction::triggered, this, [this]() { saveTitle(); });
+    connect(m_buttonSave, &QAction::triggered, this, [this]() { saveTitle(); }, Qt::QueuedConnection);
 
     m_buttonDownload = new KNSWidgets::Action(i18n("Download New Title Templates…"), QStringLiteral(":data/kdenlive_titles.knsrc"), this);
     m_buttonDownload->setShortcut(Qt::ALT | Qt::Key_D);
@@ -457,6 +457,7 @@ TitleWidget::TitleWidget(const QUrl &url, QString projectTitlePath, Monitor *mon
     // initialize graphic scene
     m_scene = new GraphicsSceneRectMove(TITLERVERSION, m_frameWidth, m_frameHeight, this);
     graphicsView->setScene(m_scene);
+    graphicsView->setAcceptDrops(true);
     graphicsView->setMouseTracking(true);
     graphicsView->setViewportUpdateMode(QGraphicsView::FullViewportUpdate);
     graphicsView->setDragMode(QGraphicsView::RubberBandDrag);
@@ -465,6 +466,7 @@ TitleWidget::TitleWidget(const QUrl &url, QString projectTitlePath, Monitor *mon
     connect(m_scene, &QGraphicsScene::changed, this, &TitleWidget::slotChanged);
     connect(m_scene, &GraphicsSceneRectMove::copy, this, &TitleWidget::slotCopy);
     connect(m_scene, &GraphicsSceneRectMove::paste, this, &TitleWidget::slotPaste);
+    connect(m_scene, &GraphicsSceneRectMove::addImage, this, &TitleWidget::addImageToScene);
     connect(font_size, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), m_scene, &GraphicsSceneRectMove::slotUpdateFontSize);
     connect(use_grid, &QAbstractButton::toggled, m_scene, &GraphicsSceneRectMove::slotUseGrid);
 
@@ -949,23 +951,38 @@ void TitleWidget::slotImageTool()
     QUrl url = QUrl::fromLocalFile(dialog.selectedFiles().at(0));
     if (url.isValid()) {
         KRecentDirs::add(QStringLiteral(":KdenliveImageFolder"), url.adjusted(QUrl::RemoveFilename).toLocalFile());
-        if (url.toLocalFile().endsWith(QLatin1String(".svg"))) {
-            MySvgItem *svg = new MySvgItem(url.toLocalFile());
-            svg->setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemSendsGeometryChanges);
-            svg->setZValue(m_count++);
-            svg->setData(Qt::UserRole, url.toLocalFile());
-            m_scene->addNewItem(svg);
-            prepareTools(svg);
-        } else {
-            QPixmap pix(url.toLocalFile());
-            auto *image = new MyPixmapItem(pix);
-            image->setShapeMode(QGraphicsPixmapItem::BoundingRectShape);
-            image->setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemSendsGeometryChanges);
-            image->setData(Qt::UserRole, url.toLocalFile());
-            image->setZValue(m_count++);
-            m_scene->addNewItem(image);
-            prepareTools(image);
+        addImageToScene(url);
+    } else {
+        m_scene->setTool(GraphicsSceneRectMove::TITLE_SELECT);
+        showToolbars(GraphicsSceneRectMove::TITLE_SELECT);
+        checkButton(GraphicsSceneRectMove::TITLE_SELECT);
+    }
+}
+
+void TitleWidget::addImageToScene(const QUrl url, QPoint pos)
+{
+    if (url.toLocalFile().endsWith(QLatin1String(".svg"))) {
+        MySvgItem *svg = new MySvgItem(url.toLocalFile());
+        svg->setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemSendsGeometryChanges);
+        svg->setZValue(m_count++);
+        svg->setData(Qt::UserRole, url.toLocalFile());
+        m_scene->addNewItem(svg);
+        if (!pos.isNull()) {
+            setItemPosition(svg, pos.x(), pos.y());
         }
+        prepareTools(svg);
+    } else {
+        QPixmap pix(url.toLocalFile());
+        auto *image = new MyPixmapItem(pix);
+        image->setShapeMode(QGraphicsPixmapItem::BoundingRectShape);
+        image->setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemSendsGeometryChanges);
+        image->setData(Qt::UserRole, url.toLocalFile());
+        image->setZValue(m_count++);
+        m_scene->addNewItem(image);
+        if (!pos.isNull()) {
+            setItemPosition(image, pos.x(), pos.y());
+        }
+        prepareTools(image);
     }
     m_scene->setTool(GraphicsSceneRectMove::TITLE_SELECT);
     showToolbars(GraphicsSceneRectMove::TITLE_SELECT);
diff --git a/src/titler/titlewidget.h b/src/titler/titlewidget.h
index 988782caba..4ba07a5e8d 100644
--- a/src/titler/titlewidget.h
+++ b/src/titler/titlewidget.h
@@ -343,6 +343,9 @@ private Q_SLOTS:
 
     void setCurrentItem(QGraphicsItem *item);
 
+    /** @brief Adds an image from its url */
+    void addImageToScene(const QUrl url, QPoint pos = QPoint());
+
     void slotTextTool();
     void slotRectTool();
     void slotEllipseTool();
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.